vue.js 에서 router 사용법을 정리해볼까 한다.
router, routing 은 route(경로) 를 뜻하는 단어에서 파생된 것으로,
말 그대로 특정 js 리소스(아마 여기선 .vue 파일 등 페이지 컴포넌트)을
가리킴으로써 해당 js 파일을 브라우저(클라이언트) 단에서 호출 및 관측이 가능하게끔 도와주는 장치이다.
예를 들어 싱글 페이지 애플리케이션(SPA)을 구축한다고 할 때,
우리는 하나의 index.html 파일에 vue 로 구축한 프로젝트의 여러 javascript 리소스를 붙여넣는다고 볼 수 있다.

이렇게 하나의 SPA 가 실행되게 되고, 해당 화면에서 특정 버튼을 클릭하거나, 페이지를 이동하게 되면 싱글 페이지 앱 특성상 매번 동일한 URL 에서 작업이 이루어지게 되는데,
우리가 일상속에서 인터넷을 이용할 땐, 버튼과 상호작용하여 페이지를 이동하는 경우도 있지만
www.example.com/ex1/detail?page=1
과 같이 url 을 직접 입력하여 들어가는 경우도 존재한다.
하지만 URL 이 매번 동일한 vue 프로젝트에선 어떻게 해야할까?
각 리소스로 보여지는 화면은 해당 순간에 나에게만 유효할 뿐이다.
그 화면을 타인과 주소로 (즉 경로로) 공유하려면 해당 화면에 특정 경로가 부여되어야 한다.
이럴때 각 리소스, 즉 컴포넌트파일들에 적절한 경로값을 부여해 URL 단위로 작업을 수행할 수 있도록 도움을 주는 것이 바로 라우터이다.
vue-router 를 사용하기 위해 먼저 설치를 진행한다.
npm install vue-router

router.js 파일을 따로 생성하여 route 에 대한 정의 및 로직 작성을 main.js 파일로부터 분리해 main.js 파일을 가벼운 상태로 유지한다.
이때 router 인스턴스 객체를 생성하기 위해 vue-router 라이브러리로부터 createRouter 메소드를 import 해온다.
router 객체는 url 이동 정보에 대한 history 관리 프로퍼티와,
url 경로마다 렌더링할 컴포넌트를 정의할 수 있는 routes 프로퍼티가 존재한다.
routes 프로퍼티에는 route 객체타입을 배열로 관리하게 된다.
history 는 router 를 통해 컴포넌트간 이동 시 해당 기록을 어떤 방식으로 관리할 지 지정할 수 있다.
크게 2가지가 있는데,
createWebHashHistory() 와 createWebHistory() 방식이 있다.

createWebHashHistory() 방식은 각 route 의 path 경로 바로 앞에 # 코드를 붙여 url 을 관리하고,

createWebHistory() 방식은 우리가 익히 아는 평범한 형태의 url 로 관리한다.
'#' 이 붙게 되면 이후 경로는 브라우저가 인식하지 못하므로,
브라우저는 url 의 변경을 감지하지 못한다.
이에 검색 엔진에서의 url 중복 및 인지불가 오류가 발생할 수도 있다.
일반적으로 createWebHistory() 방식을 이용하여 검색 엔진 최적화 관리를 해주는 것이 좋으며, (# 이후의 url 혼재 방지)
다만 해당하는 주소에 매핑된 적절한 컴포넌트가 없을 경우 404 에러등이 발생할 수 있으므로 각별한 주의가 필요하다.

main.js 에서는 router 객체를 import 해오고
createApp 으로 생성한 메인 뷰 (App) 객체에
use 메소드를 이용하여 라우터 사용을 선언한다.

각 route 객체는 자바스크립트 객체 타입을 이용하여 작성하며, path 프로퍼티엔 해당하는 url 주소를, component 프로퍼티엔 렌더링할 컴포넌트를 지정한다.

현재 라우터는 최상단으로 App.vue 에 붙어있으며 '/teams', '/users' 경로 등으로 설정되어 있으므로 App.vue 에서 렌더링할 컴포넌트의 위치를
<router-view> 태그로 지정할 수 있다.

기존 <a> 태그 대신 <router-link> 태그를 이용하여 url 변경 동작을 수행할 수 있다. 이때 to 속성에 이동하고자할 url 경로를 문자열로 입력하거나, computed 메소드를 작성 후 해당 메소드를 포인팅해줄 수도 있다.
router-link 태그는 기존 a 앵커 태그와 유사하나, 기존 브라우저의 기본 동작을 막고 (새로고침 등) 다른 html 파일을 호출하지 못하게 한다.
대신 라우터가 router-link 태그에 적힌 경로를 분석해 알맞은 컴포넌트를 렌더링해주는 방식을 취한다.
(기존에 그려진 컴포넌트 파괴 x, 이에 created 같은 생명주기 메소드 사용 시 유의가 필요!)
스타일링은 a 태그로 인식되기에 기존 a 태그처럼 사용해줄 수 있고,
router-link 의 자체 클래스인 router-link-active, router-link-exact-active 클래스를 활용해 사용자에게 더 나은 시각적 정보 제공이 가능하다.



<router-link> 태그 외에 특정 코드 실행 후 페이지 이동이 필요하다면 프로그래밍 방식으로 라우터 객체를 활용할 수도 있다.

$router 으로 App.vue 아래의 컴포넌트에서 어디서나 전역 router 객체를 활용할 수 있다.
이때 push 메소드를 이용해 해당하는 문자열 경로의 컴포넌트로 이동하게끔 지정할 수 있다.

$route 내장 객체는 현재 url 경로에 대한 정보를 제공한다.
path 프로퍼티는 query 제외 경로를 알려주며
params 프로퍼티는 동적 매개변수의 목록을 제공한다.

동적 매개변수는 아래에서 이어서 설명..

route 의 동적 매개변수는 ' : ' 콜론 기호를 사용하며 위 사진에서는 teamId 라는 이름의 동적 변수를 이용하고 있다.
route 선언 시 순서가 중요한데, 위 사진처럼 작성할 경우 맨 하단의 new 경로는 동적 변수 경로인 teamId 값에 포함되어 영영 적용이 안된다..
간단히 TeamMembers 컴포넌트에 log 코드만 작성해본 뒤 확인해보자.



이렇게 동적으로 path 경로의 값을 매개변수로 활용할 수 있다.
하나의 컴포넌트 (예를 들면 TeamMember.vue) 가 $router, $route 내장 객체나 외부 router-link 등 라우팅에 의해서만 호출된다면
해당 컴포넌트는 다른 외부 컴포넌트에 의해 활용되거나 재사용되기가 어렵다.
이때 해당 컴포넌트가 라우터 객체로부터 동적 매개변수를 props 로 전달받게 되면
후에 다른 컴포넌트도 해당 props 만 넘겨주면 되므로 재사용성이 훨씬 좋아지게 된다.


route 객체 내 props 프로퍼티를 true 로 설정하면,
해당 컴포넌트가 라우터에 의해 렌더링 될때, 매개변수의 값이 props 로 전달되게 된다.
이때 기본으로 노출시킬 페이지가 있다면, 해당 페이지의 경로로 redirect 시키는 방법으로 손쉽게 해결할 수 있다.

이제 기본 루트 경로로 접속 시 TeamsList 컴포넌트가 기본으로 보여지게 된다.
만약 아무것도 해당하지않는, 존재하지 않는 경로를 사용자가 입력했을땐 어떻게 처리해야할까?
다른 컴포넌트로 리다이렉트 시킬 수도 있겠지만, 따로 오류 페이지 컴포넌트를 생성해 해당 컴포넌트를 가리킬 수도 있다.

해당 catchAll route 는 순서에 유의하며 제일 마지막에 작성하도록 하고,
NotFound.vue 파일을 간단하게 작성하여 가리키도록 하였다.


children 프로퍼티에 배열로 작성하며, 기존 route 객체 목록을 전달하면 된다.
이때 중복되는 경로인 '/teams' 를 제외하고 동적으로 할당받을 경로 부분만 작성하면 된다.
기존에 routes 객체는 App.vue 에 붙어 있기에 App.vue 에 <router-view> 태그를 작성하였다.
해당 router-view 는 routes 배열 바로 안에 있는 '/', '/teams', '/users', '/notFound' 등 형제 관계의 route 만 렌더링하게되고, 중첩 라우트는 어디에 렌더링해야할 지 인지하지 못한다.

따라서 중첩 라우트로 작성된 ':teamId' 경로의 라우트는 해당 중첩 라우트가 작성된 부모 요소인 TeamsList 컴포넌트에 router-view 로 렌더링 위치를 지정해주어야 한다.
이때 name 프로퍼티를 사용하여 각 route 에 이름을 명명하고,
해당 이름으로 route 를 찾아갈 수 있도록 해줄 수 있다.


router-link 태그에서 to 프로퍼티를 v-bind 로 동적으로 할당하고,
이때 route 객체를 넘겨 해당하는 name 의 컴포넌트로 갈 수 있도록 설정해준다.
query 를 사용하여 url 에 쿼리스트링으로 추가 데이터를 전송할 수도 있다.
위와 같이 name 프로퍼티를 사용하면 가독성도 좋아질 뿐더러,
만약 이후에 router.js 에서 '/teams' 경로를 수정한다 해도
문자열 하드코딩이 아닌 객체내 name 프로퍼티로 경로를 설정했으므로
각 route 를 찾아 수정할 필요가 없기에 유지보수도 훨씬 좋아지게 된다.
router-view 에서도 name 속성을 이용할 수 있다!
route 에서 name 프로퍼티를 사용하는 것과 같이
router-view 태그에서도 name 속성을 이용하여 여러 컴포넌트를 불러와 배치할 수 있다.
App.vue 에 하나 이상의 router-view 가 존재하고, 특정 router-view 에 이름을 부여하면 나머지 하나는 default 가 된다.
components 프로퍼티로 해당하는 router-view 에 렌더링할 컴포넌트를 지정할 수 있다.
위와 같이 명명된 router-view 태그를 이용하면 사용자에게 보여지는 화면 요소들을 좀 더 유연하게 배치할 수 있다.
반대로 브라우저의 뒤로가기 등 이전페이지로 돌아간 후 이전 페이지에서 스크롤이 멈췄던 위치로 다시 돌아가서 읽던 부분을 마저 읽을 수도 있다.
이렇듯 일상에서 접했었던 사용자 편의 경험을 구현하기 위해
scrollBehavior() 메소드를 활용할 수 있다.

routes 배열 밖에 작성하며,
to, from, savedPosition 인자로 각각
이동할 페이지,
이동한 페이지(이전 페이지),
페이지 내의 스크롤 위치 정보
값에 접근할 수 있다.
간단히 console 로 찍어보면..

이동에 관련된 route 의 상세 정보가 담겨있는 걸 볼 수 있다.
beforeEach 메소드는 router 내에서 전역적으로 실행되는데,
모든 이동이 실행되기 전 거쳐가는 중간 검문소? 라 생각하면 된다.


route 내의 meta 프로퍼티를 활용해 인증작업을 수행하여
인증되지 않은 사용자가 특정 페이지에 접속하는 것을 방지할 수 있다.
함수를 정의해야하며, 인자로 to, from, next 를 받는데
to, from 은 이전의 scrollBehavior 메소드와 유사하지만
이동에 직접적으로 관여하는 next 가 있다는 점에서 차이가 있다.
next 에는 true, false 로 이동을 단순히 승인, 거부할 수도 있고,
route 객체를 넘겨 이동할 페이지를 지정할 수도 있다.
beforeEach 메소드가 전역적으로 실행되었다면,
각 route 마다 지역적으로 내비게이션가드를 적용하는데 beforeEnter 가 사용된다.

마찬가지로 함수 형태이며, to, from, next 를 통해 정보를 취합, 이동을 승인 또는 거부할 수 있다.
위 beforeEach(), beforeEnter() 가 router 파일 내에서 선언되었다면,
beforeRouteEnter() 메소드는 컴포넌트 단에서 선언할 수 있다.

예시로 UsersList.vue 컴포넌트에 설정된 내비게이션 가드
가드의 적용 우선순위는
1. beforeEach
2. beforeEnter
3. beforeRouteEnter
으로 컴포넌트 단에 선언된 가드가 제일 나중에 적용되게 된다.
afterEach 메소드는 이전의 내비게이션 가드들과 달리 이동을 제어할 수는 없다.
이동에 관련된 작업은 before~ 메소드에서 이미 처리가 되고 난 후,
afterEach 메소드에선 단순히 현 사용자의 페이지 이동 액션에 대한 기록 처리, 저장용으로 쓰이게 된다.

afterEach 메소드는 컴포넌트단의 내비게이션 가드까지 실행되고 나서 실행된다.

중첩 라우트로 한 화면에서 여러 라우트가 사용될 때
바뀌는 부분이 있는가 하면, 고정되어 계속 재사용되는 부분도 있다.
(전체 팀 목록 같은 것..)
이때 계속 재사용되는 라우트에 beforeRouteUpdate() 메소드로 해당 라우트가 다시 사용되기 전 동작을 제어할 수 있다.

예시로 TeamMembers.vue 의 메소드 참조.
현재 라우트에서 다른 라우트로 이동하기 전 로직을 추가할 수 있는 메소드.

사용자가 현재 화면에서 데이터를 수정 후 실수로 뒤로가기를 눌렀을 때
confirm 창을 띄워 불의의 사고를 막을 수 있는, 사용자 경험 향상에 효과적인 메소드라 생각된다.
https://blinders.tistory.com/76
https://router.vuejs.kr/guide/essentials/history-mode