잠시 멈췄던 vue 프로젝트를 다시 진행하기 위해 프로젝트 구성 후, vue router에 대해 공부했다. vue router같은 경우에는 처음 vue 프로젝트를 구성할 때 추가하여 바로 사용할 수도 있지만 나는 기본적인 셋팅만 맞춰놓고, 라우터를 직접 설치했다. 라우터뿐만 아니라 피니아, 타입스크립트 등도 처음에 모두 설정 가능하지만 추후 하나씩 적용해보며 공부해보려고 한다.
1. Vue Router란?
SPA, 싱글페이지 어플리케이션의 경우 기본적으로 html이 한개이기 때문에 우리가 기존 바닐라 자바스크립트로 만들었을때와 달리 html을 여러개 생성하는 구조가 아니다. 그래서 쉽게 말해 하나의 html안에서 router를 통해 화면을 바꿔준다고 생각하면 될 것 같다. 즉, route는 URL에 따라 어떤 페이지를 보여줄지를 매핑해주는 라이브러리라고 생각하면 된다. 먼저 vite, vue3, Eslint, prettier까지만 세팅을 해놓고 vue-router를 설치했다.
npm install vue-router
2. Router 설정(페이지 이동)
HomeView.vue와 DetailView.vue간의 페이지 이동을 보여주기 위해 이제 라우터 설정을 해줘야하는데 router 폴더 아래에 index.js를 만들어 해당 코드를 작성했다. 작성하다보니 리액트와는 작성하는 방법이 조금 다른 듯했다.
1) router/index.js 파일 작성
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';
import ListView from '@/views/ListView.vue';
import DetailView from '@/views/DetailView.vue';
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/list',
name: 'list',
component: ListView,
},
{
path: '/detail/:id',
name: 'detail',
component: DetailView,
},
];
const router = createRouter({
history: createWebHistory('/'),
routes,
});
export default router;
index.js 파일 안에 routes를 만들고 그 안에 이제 내가 보여줄 페이지들을 작성하면 된다. path는 경로를 뜻하고 name은 말그대로 name, component는 내가 보여줄 컴포넌트라고 생각하면 된다. 위의 코드는 각 컴포넌트를 import해서 사용했지만 아래와 같이 사용할 수도 있다. component 부분에 함수형으로 작성하는 방법이다.
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'home',
component: () => import("@/views/HomeView.vue"),
},
{
path: '/list',
name: 'list',
component: import("@/views/ListView.vue"),
},
{
path: '/detail/:id',
name: 'detail',
component: () => import("@/views/DetailView.vue"),
},
];
const router = createRouter({
history: createWebHistory('/'),
routes,
});
export default router;
component 작성 방법 차이(직접 vs 함수)
처음에 component에 직접 쓰는 법과 함수형으로 쓰는게 어떻게 다른지가 궁금해서 찾아보게 되었는데 첫번째는 각각의 컴포넌트를 직접 참조하여 component 속성에 할당하는 것이고, 두번째는 함수를 사용하여 component 속성에 할당하는 방법으로 동적 import를 통해 작성한 경로에 있는 파일을 비동기적으로 불러오는 역할을 수행한다고 한다.
결론적으로, 첫번째 예제는 라우트 설정 시 이미 import 되어 있는 컴포넌트를 할당하는 반면, 두번째 예제는 동적 import를 통해 비동기적으로 불러오는 방식으로 사용된 것이다. 두가지 방법 중에 두번째 방법을 사용할 경우 초기 렌더링 성능 및 애플리케이션의 로딩 속도를 개선할 수 있다는 장점이 있다. 불필요한 컴포넌트 로딩을 최소화하여 초기 로딩 속도를 빠르게 하고, 필요한 시점에 필요한 컴포넌트를 로딩하여 성능을 최적화시킬 수 있기 때문이다.
초기 렌더링 성능 및 애플리케이션의 로딩 속도를 개선하고자 할 때는 동적 import를 통해 파일을 비동기적으로 불러오는 방식을 사용하는 것이 좋기 때문에 두번째 방식을 사용하는 것이 좋을 것 같다.
2) main.js 및 App.vue 수정
라우트 설정을 마쳤다면 main.js 파일로 가서 설정한 라우터 객체를 Vue 인스턴스에 추가해야한다.
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
// App.vue
<script setup></script>
<template>
<nav>
<Routerlink to="/">Home</Routerlink>
<span> | </span>
<RouterLink to="/list">List</RouterLink>
</nav>
<main>
<RouterView></RouterView>
</main>
</template>
이렇게 작성하고 나면 Home과 Detail 네비게이션바와 아래 main 영역이 생기는데 Home을 클릭했을 때는 HomeView.vue가 보여지고 Detail을 클릭했을 때는 DetailView.vue가 보여지게 된다. 우리가 보여주고자하는 영역에 RouterView를 넣으면 해당 부분에 원하는 페이지가 보일 것이다. 지금 같은 상황에서는 nav 태그 밑 main 안에 RouterView가 있기 때문에 nav는 고정이며 main 안쪽의 부분만 바뀌는 것을 볼 수 있다.
Vue Router를 사용하여 페이지를 이동하는 방법(RouterLink vs $router.push())
1) RouterLink 컴포넌트 사용
첫번째로, RouterLink 또는 router-link 컴포넌트를 사용해서 링크를 생성하고 클릭 시 페이지를 이동시킬 수 있다. 아래 두가지는 같은 방식으로 파스칼케이스를 사용하느냐 케밥케이스를 사용하느냐의 차이일뿐이다.
// 파스칼
<RouterLink to="/">Home</RouterLink>
// 케밥
<router-link to="/">Home</router-link>
2) $router.push() 를 통한 이동
두번째로는 해당 메소드를 사용하여 페이지를 이동시킬 수 있는데 이런식으로 click 이벤트에 바로 작성할 수도 있고, 함수를 만들어 사용할 수도 있다.
<template>
<div>
<h2>Detail</h2>
<button class="btn btn-primary" @click="$router.push('/list')">목록으로</button>
</div>
</template>
여기서 또 한가지 궁금한게 생겼었는데 click 이벤트에 바로 작성하는 것과 함수로 빼서 사용하는 것 중 어떤 것이 더 좋은 코드일까?를 고민하게 되었다. 그래서 찾아본 결과 정말 간단하게 vue 컴포넌트 내에서만 사용하는 경우에는 click 이벤트에 바로 작성해도 괜찮지만 코드의 가독성, 재사용성, 유지보수성을 생각한다면 함수를 만들어서 router를 시키는 방식이 더 좋은 방법으로 권장된다고 한다.
// Home
<template>
<div>
<h2>Detail</h2>
<button class="btn btn-primary" @click="goList">목록으로</button>
</div>
</template>
<script setup>
import { useRoute, useRouter } from "vue-router";
const router = useRouter();
const route = useRoute();
const goList = () => {
router.push("/list");
};
</script>
<style lang="scss" scoped></style>
이제 목록 페이지에서 각각을 클릭했을 때 상세페이지로 이동시킬 수 있는데 /detail/:id 라는 주소로 id가 1인 값을 넘겨주면 된다. id를 넣어주려면 아래와 같은 방법을 사용하면 된다.
const routes = [
{
path: '/detail/:id',
name: 'detail',
component: DetailView
}
]
name or path에 따라 params를 아래와같이 넘겨줄 수 있다.
const id = 1;
router.push(`/detail/${id}`)
router.push({ path: `/detail/${id}` })
router.push({ name: 'detail', params: { id } })
router.push({ path: '/detail', params: { id } })
이렇게 되면 /detail/1 로 잘 이동한다.
3. Router 설정(중첩 라우트)
기본적으로 vue를 사용하면 여러개의 vue와 컴포넌트로 구성을 하게 될텐데 여러 단계의 중첩된 컴포넌트가 많을 수밖에 없다. 상위 컴포넌트 아래에 하위 컴포넌트, 또 하위 컴포넌트가 있을 수 있다는 것이다. 탭버튼은 고정이고 탭버튼에 따른 하위컴포넌트만 바뀔 수 있도록 구성해보았다. 탭을 클릭했을 때 아래쪽의 RouterView에 원하는 컴포넌트를 보여줄 예정이다.
<template>
<div>
<ul class="nav nav-pills">
<li class="nav-item">
<RouterLink class="nav-link" active-class="active" to="/nested/one"
>One</RouterLink
>
</li>
<li class="nav-item">
<RouterLink class="nav-link" active-class="active" to="/nested/two"
>Two</RouterLink
>
</li>
</ul>
<hr class="my-4" />
<RouterView> </RouterView>
</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>
중첩된 라우트를 사용하기 위해서는 라우트 설정 파일도 수정해줄 필요가 있는데 중첩된 라우트의 경우 children 속성을 사용한다.
const routes = [
{
path: "/nested",
name: "Nested",
component: NestedView,
children: [
{
path: "",
name: "NestedHome",
component: NestedHomeView,
},
{
path: "one",
name: "NestedOne",
component: NestedOneView,
},
{
path: "two",
name: "NestedTwo",
component: NestedTwoView,
},
],
},
];
이렇게 설정해주면 /nested 페이지에서 각각의 탭 버튼을 클릭했을 때 /nested/one, /nested/two 이런 식으로 탭의 아래쪽 부분에 원하는 컴포넌트가 매칭된다. router를 잘 사용하면 각각의 주소에 따라 내가 원하는 컴포넌트나 페이지를 보여줄 수 있기 때문에 유용하다. params에 따라 원하는 액션을 취할 수도 있고 다양하게 활용할 수 있다.