라즈베리파이반

라즈베리파이 등 컴퓨터계열 게시판입니다.

제목Vue.js: Vue Router2023-01-15 05:52
작성자user icon Level 4

88x31.png


1. Vue Router란?


Vue Router는 Vue.js를 통해 단일 페이지 어플리케이션(Single Page Application, SPA)을 구현할 때 사용하는 라이브러리입니다.


라우팅(Routing)은 출발지에서 목적지까지의 경로를 결정하는 기능입니다. 웹어플리케이션에서 라우팅은 뷰(View)에서 다른 뷰로 이동하는 내비게이션을 관리하기 위한 기능을 의미합니다.


SPA가 보편화 되기 전 전통적인 라우팅은 서버에서 처리했습니다. 클라이언트에서 URL을 통해 서버에 페이지를 요청(Request)하면 서버에서는 해당 HTML파일을 찾아 렌더링을 통해 HTML을 완성하여 클라이언트에게 응답(Response)을 보냅니다. 클라이언트의 역할은 서버에 요청을 보내는 것밖에 없으며 서버가 모든 라우팅에 대한 권한을 가지고 처리하기 때문에 서버 사이드 라우팅이라고 합니다.


SPA의 경우 라우팅을 클라이언트에서 처리합니다. 클라이언트에서 서버에 페이지를 요청하면 서버에서는 단순히 하나의 HTML 파일을 응답으로 보냅니다. URL에 해당하는 내용은 클라이언트에서 AJAX를 통해 비동기 요청을 보내어 데이터를 응답받고 자바스크립트를 통해 페이지를 렌더링합니다. 클라이언트에서 라우팅에 대한 권한을 가지고 처리하기 때문에 클라이언트 사이드 라우팅이라고 합니다.


즉, Vue Router는 Vue.js를 통해 클라이언트 사이드 라우팅을 돕는 라이브러리입니다.



2. Vue Router 사용



1) 라우터 등록


Vue Router를 사용하려면 Vuex처럼 Vue Router를 Vue에 추가해야합니다.


Vue Router 사용 선언

import Vue from 'vue'

import VueRouter from 'vue-router'


Vue.use(VueRouter)


Vuex와 마찬가지로 CDN을 통해 Vue Router를 import하여 모듈방식으로 개발하는 경우 Vue Router를 로드하면 자동으로 Vue Router를 사용하도록 설정되기 때문에 생략가능합니다. Vue 2버전의 경우 Vue Router 3버전을 로드합니다.


Vue Router의 사용을 선언하고 Vue 인스턴스의 옵션으로 router를 넣어주면 해당 컴포넌트에서 라우터를 사용할 수 있습니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app"></div>

</body>

</html>


main.js 

import router from "./router.js";


new Vue({

  router,

}).$mount('#app')


router.js 

export default new VueRouter();



2) 라우트(Routes) 옵션


라우터(Router) 인스턴스의 옵션에는 mode routes가 있습니다. 여기에서 routes 옵션에는 라우팅할 URL과 컴포넌트 등을 포함한 라우트(route) 객체 배열을 입력합니다.


라우트 객체의 속성은 다음과 같습니다.


라우트 객체의 속성

 속성

 내용

 path

 라우트의 URL 경로

 name

 라우트 이름

 component

 라우트가 로드할 컴포넌트

 components

 라우트가 로드될 때 주입될 컴포넌트 목록

 redirect

 리다이렉트할 컴포넌트

 alias

 라우트 별칭

 children

 중첩된 라우트 목록

 props

 동적 세그먼트 변수를 컴포넌트의 props 속성으로 주입할 것인지에 대한 여부


path의 경우 필수 속성이며 나머지는 옵션에 해당합니다. 하지만 componentcomponents의 경우 입력되지 않으면 빈 화면이 로드되므로 거의 필수라고 보면 됩니다.


Vue Router에서는 router-view 컴포넌트와 router-link 컴포넌트를 제공합니다. router-view 컴포넌트는 URL에 해당하는 컴포넌트를 렌더링하는 역할을 하며, router-link 컴포넌트는 라우팅을 요청하는 역할을 합니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <nav>

      <router-link to="/">Main Page</router-link> |

      <router-link to="/first">First Page</router-link> |

      <router-link to="/second">Second Page</router-link>

    </nav>

    <router-view></router-view>

  </div>

</body>

</html>


router-link 컴포넌트의 to 속성에는 라우트 경로를 입력하면 해당 라우트 경로를 가진 라우트에 매핑됩니다. 라우트를 설정하기 전에 매핑될 컴포넌트부터 작성하겠습니다.


MainPage.js

export default {

  template: `<div>Main Page</div>`

}


FirstPage.js 

export default {

  template: `<div>First Page</div>`

}


SecondPage.js 

export default {

  template: `<div>Second Page</div>`

}


이제 라우터에 라우트를 설정해줍니다.


router.js

import MainPage from "./MainPage.js";

import FirstPage from "./FirstPage.js";

import SecondPage from "./SecondPage.js";


export default new VueRouter({

  routes: [

    { path: '/', component: MainPage },

    { path: '/first', component: FirstPage },

    { path: '/second', component: SecondPage }

  ]

});


출력 결과 

img60 

네비게이션을 클릭함에 따라 URL이 변하는 것을 볼 수 있습니다. Vue Router에서 기본으로 사용하는 모드는 Hash 모드입니다. 아래에서 다시 설명할텐데 URL의 형식이 프로토콜://호스트:포트/#/경로입니다. 즉, 해시태그를 사용하여 페이지를 이동하지 않고 화면을 렌더링하여 마치 페이지를 이동한 것 같이 보이도록 합니다.


라우터 인스턴스에 설정한 라우트를 보면 path와 component 속성이 존재합니다. 라우트는 router-link의 to 속성값에 해당하는 path와 매핑되고, 유저의 라우팅 요청시 해당 라우트의 컴포넌트가 router-view에 렌더링됩니다.



3) 동적 라우트 매칭


동적 라우트란 경로에 변수를 가지고 있는 라우트를 의미합니다. 예를 들어 게시글 페이지의 경우 게시글 번호로 페이지를 매핑할 수 있습니다.


예를 들어 스프링 프레임워크를 통해 웹어플리케이션을 만들때 컨트롤러에 @GetMapping('/board/{documentId}') 형식으로 어노테이션을 작성하여 게시글 페이지에 매핑하고, @PathVariable('documentId') Long documentId 형태로 documentId 파라미터를 주입받았습니다.


이처럼 Vue에서도 파라미터를 통해 동적인 라우트 매핑이 가능한데, 이때 경로에 포함되어 있는 변수를 동적 세그먼트라고 합니다. 동적 세그먼트에는 어떤 값이 들어오는지 알 수 없으므로 라우트의 path 속성에 패턴을 정의해주어야 합니다.


동적 세그먼트는 콜론(:)으로 시작하는 변수명으로 할당할 수 있으며, 컴포넌트 내에서 this.$route.params를 통해 접근할 수 있습니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view></router-view>

    <nav>

      <router-link to="/board/1">1</router-link> |

      <router-link to="/board/2">2</router-link> |

      <router-link to="/board/3">3</router-link>

    </nav>

  </div>

</body>

</html>


BoardDetail.js 

export default {

  template: `<div>documentId: {{ $route.params.documentId }}</div>`

}


router.js 

import BoardDetail from "./BoardDetail.js";


export default new VueRouter({

  routes: [

    { path: '/board/:documentId', component: BoardDetail }

  ]

});


출력 결과 

img61 


동적 라우트를 사용할 경우 동적 세그먼트가 변경되더라도 Vue Router는 이를 동일한 경로로 인식하여 컴포넌트를 다시 로드하지 않고 기존에 로드된 컴포넌트를 재사용합니다. 그러므로 Vue 인스턴스의 created나 mounted 훅을 통해 경로 변경을 감지할 수 없습니다.


경로 변경을 감지하기 위해서는 watch 옵션을 통해 $route 객체를 감지하면 됩니다.


BoardDetail.js

export default {

  template: `<div>documentId: {{ $route.params.documentId }}</div>`,

  watch: {

    '$route' (to, from) {

      console.log("old path: ", from.params.documentId);

      console.log("new path: ", to.params.documentId);

    }

  }

}


출력 결과 

img62 



4) 중첩된 라우트


중첩된 라우트는 /user/profile, /user/boardDetail과 같이 URL의 세그먼트가 중첩된 라우트를 말합니다.


중첩된 라우트

 


중첩된 profile과 boardDetail 경로의 경우 user 라우트의 children 속성에 정의하면 됩니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view></router-view>

    <nav>

      <router-link to="/user/profile">Profile</router-link> |

      <router-link to="/user/boardDetail">BoardDetail</router-link>

    </nav>

  </div>

</body>

</html>


User.js

export default {

  template: `

    <div>

      <router-view></router-view>

    </div>

  `

}


Profile.js 

export default {

  template: `<div>닉네임 : Rubisco</div>`

}


BoardDetail.js 

export default {

  template: `<div>작성한 글 내용</div>`

}


router.js 

import User from "./User.js";

import Profile from "./Profile.js";

import BoardDetail from "./BoardDetail.js";


export default new VueRouter({

  routes: [

    {

      path: '/user',

      component: User,

      children: [

        { path: 'profile', component: Profile },

        { path: 'boardDetail', component: BoardDetail }

      ]

     }

  ]

});


출력 결과 

img64 



5) 프로그래밍 방식 내비게이션


Vue Router에서는 메소드를 통해 라우팅할 수 있도록 지원하고 있습니다. push, replace, go 메소드가 존재하며, 각각 history API의 pushState, replaceState, go 메소드와 상응합니다.


매개변수로 location, onComplete, onAbort를 받습니다. location은 라우팅 경로, onComplete는 라우팅에 완료된 후에 호출될 함수, onAbort는 라우팅에 실패하면 호출될 함수입니다.


push 메소드는 브라우저의 히스토리에 라우트 객체를 저장후 페이지를 이동하고, replace 메소드는 히스토리에 저장하지 않고 페이지를 이동합니다. 히스토리에 라우트 객체가 저장된다면 브라우저의 뒤로가기 버튼으로 이전 페이지로 이동이 가능합니다.


BoardDetail.js

export default {

  template: `

    <button @click="goBack">뒤로</button>

  `,

  methods: {

    goBack() {

      this.$router.go(-1)

    }

  }

}



6) 이름을 가지는 라우트


동적 라우팅의 경우 라우트에 이름(name)을 설정하여 사용하는 것이 편리할 수도 있습니다. router-link 컴포넌트의 to props에 name을 전달하여 라우트를 링크할 수 있습니다.


index.html 

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view></router-view>

    <nav>

      <router-link :to="{ name: 'profile' }">Profile</router-link> |

      <router-link :to="{ name: 'boardDetail' }">BoardDetail</router-link>

    </nav>

  </div>

</body>

</html>


router.js

import User from "./User.js";

import Profile from "./Profile.js";

import BoardDetail from "./BoardDetail.js";


export default new VueRouter({

  routes: [

    {

      path: '/user',

      component: User,

      children: [

        { path: 'profile', name: 'profile', component: Profile },

        { path: 'boardDetail', name: 'boardDetail', component: BoardDetail }

      ]

     }

  ]

});



7) 이름을 가지는 뷰


때로는 여러 개의 뷰를 중첩하지 않고 동시에 표시하는 경우가 있습니다.


여러 레이아웃을 가진 뷰

 


이 경우 router-view 컴포넌트에 이름을 설정하여 각각의 컴포넌트를 바인딩할 수 있습니다.


라우터 인스턴스에는 components 속성을 설정하는데, router-view의 이름을 key값으로 하고 바인딩할 컴포넌트를 value로 가지는 객체를 설정합니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view name="header"></router-view>

    <router-view name="main"></router-view>

  </div>

</body>

</html>


HeaderComponent.js 

export default {

  template: `<div>Header</div>`

}


MainComponent.js 

export default {

  template: `<div>Main</div>`

}


router.js 

 import MainComponent from "./MainComponent.js";

import HeaderComponent from "./HeaderComponent.js";


export default new VueRouter({

  routes: [

    {

      path: '/',

      components: {

        header: HeaderComponent,

        main: MainComponent

      }

    }

  ]

});


이름을 가지지 않은 router-view 컴포넌트는 기본적으로 default 라는 이름을 가집니다.



8) 리다이렉트와 별칭


리다이렉트(redirect)는 사용자가 어떤 페이지에 접속하는 경우 자동으로 다른 페이지로 보내는 기능입니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view></router-view>

  </div>

</body>

</html>


router.js 

import MainPage from "./MainPage.js";


export default new VueRouter({

  routes: [

    { path: '/', redirect: '/main' },

    { path: '/main', component: MainPage }

  ]

});


이 경우 루트 페이지(/)에 접속하면 자동으로 /main 페이지로 리다이렉트 되어 MainPage 컴포넌트가 뷰에 바인딩됩니다.


출력 결과

img66 


별칭(alias)은 페이지의 이동없이 URL만 변경되는 기능입니다.


router.js

import MainPage from "./MainPage.js";


export default new VueRouter({

  routes: [

    { path: '/main', alias: '/',  component: MainPage }

  ]

});


이 경우 루트 페이지(/) 페이지에 접속하면 /main 페이지에 접속한 것처럼 MainPage 컴포넌트가 뷰에 바인딩됩니다.


출력 결과

img67 


9) 라우트 컴포넌트에 속성 전달


컴포넌트에서 $route를 사용하여 동적 세그먼트에 접근하는 경우 특정 URL에서만 사용할 수 있는 컴포넌트가 되어 버립니다. 즉, 컴포넌트가 라우트와 강하게 결합되어 있습니다. 이는 컴포넌트의 재사용성을 떨어트릴 수 있습니다.


Vue Router는 라우트와의 결합도를 낮출 수 있도록 라우트의 동적 세그먼트를 컴포넌트의 props 속성으로 주입할 수 있는 기능을 제공합니다.


라우트의 props를 true로 설정하는 경우 $route.params가 컴포넌트의 props로 설정됩니다.


index.html

<!DOCTYPE html>

<html lang="ko">

<head>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>

  <script defer type="module" src="./main.js"></script>

  <title>Vue 연습</title>

</head>

<body>

  <div id="app">

    <router-view></router-view>

    <nav>

      <router-link to="/board/1">1</router-link> |

      <router-link to="/board/2">2</router-link> |

      <router-link to="/board/3">3</router-link>

    </nav>

  </div>

</body>

</html>


BoardDetail.js 

export default {

  template: `<div>documentId: {{ documentId }}</div>`,

  props: {

    documentId: {

      type: String,

      required: true

    }

  }

}


router.js 

import BoardDetail from "./BoardDetail.js";


export default new VueRouter({

  routes: [

    { path: '/board/:documentId', component: BoardDetail, props: true }

  ]

});


출력 결과 

img68 


props를 객체로 설정한다면 해당 각체의 속성이 컴포넌트의 props로 전달됩니다.

함수로 설정할 수도 있는데, 이 경우 라우트를 매개변수로 받으며 props 객체를 반환해야 합니다.



10) 해시모드와 히스토리 모드


Vue Router는 해시(hash, #) 모드히스토리(history) 모드 2가지를 지원합니다. 기본값은 해시 모드 입니다.


해시 모드는 SPA가 개발되기 시작하면서 고안된 기법으로 해시태그를 사용하여 페이지를 이동하는 것처럼 보이지만 실제로는 페이지를 이동하지 않습니다. 이는 브라우저가 페이지 경로를 읽을 때 해시(#) 뒤쪽으로 오는 문자열은 경로로 인식하지 않는 성질을 이용한 것입니다.


해시모드의 경우 호스트 주소와 경로 사이에 해시(#)를 붙여 URL을 나타냅니다.


해시 모드 

프로토콜://호스트주소:포트/#/경로


히스토리 모드는 HTML5의 history API를 사용하는 방법입니다. history API는 pushState라는 메소드를 제공하는데, 이 메소드는 URL을 변경하고 브로우저의 히스토리를 남기지만 실제로는 페이지를 이동하지 않는 기능을 가집니다. 그러므로 pushState를 사용하여 URL을 변경하면 브라우저가 페이지 이동으로 인식하지 않습니다. 그러나 히스토리가 저장되기 때문에 사용자에게 실제 페이지를 이동한 것처럼 보이도록 합니다.


히스토리 모드 

프로토콜://호스트주소:포트/경로


물론 Vue Router는 해시 모드든 히스토리 모드든 내부적으로는 history API를 사용하여 라우팅 하기 때문에 해시 모드 역시 히스토리를 저장합니다.


다만 history API를 지원하지 않는 브라우저의 경우 해시 모드는 페이지가 이동하지 않지만 히스토리 모드는 페이지가 이동해버린다는 단점이 있습니다.

#vue.js# vue router
댓글
자동등록방지
(자동등록방지 숫자를 입력해 주세요)