ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 라우터(Router)
    Vue.js 2019. 12. 29. 19:38

    Vue.js 그 자체로는 단순한 뷰 계층 라이브러리다. 

     

    그러므로 여러 페이지로 구성되고 복잡한 사용자 인터랙션을 갖는 애플리케이션 등을 개발할 경우에는 Vue.js만으로는 구현이 어려울 때가 있다.

     

    공식 플러그인인 Vue Router를 사용하면 단일 페이지 애플리케이션(SPA)을 비롯한 URL 이동이 필요한 동작을 간단히 구현할 수 있다.

     

    Vue Router를 이용한 SPA


    SPA란 최초에 HTML 페이지 하나를 로드한 다음, 그 이후로 사용자 인터랙션에 따라 Ajax로 정보를 받아오면서 동적으로 페이지를 업데이트하는 웹 애플리케이션을 말한다. 

     

    일반적으로 웹 애플리케이션은 페이지 이동 시에 대상 URL을 서버에 요청해 전체 페이지에 해당하는 HTML 콘텐츠를 받아온다.

     

    반면 SPA는 페이지 이동을 클라이언트에서 처리한다.

    페이지 이동 시 Ajax를 사용해 적시에 필요한 데이터를 받아와 뷰를 화면에 표시한다. 

    전체 페이지에 해당하는 HTML을 모두 받아오는데 드는 오버헤드가 줄어들어 애플리케이션의 속도가 향상되며 매끄러운 사용자 경험을 제공할 수 있다.

     

    SPA를 구현하려면 다음과 같은 사항을 고려해야 한다.

    • 클라이언트 사이드에서 히스토리를 관리하는 페이지 이동(라우팅 관리)
    • 비동기로 데이터 받아오기
    • 뷰 렌더링
    • 모듈화된 코드 관리

     

    라우터 혹은 라우팅 라이브러리라 불리는 모듈이 이런 기능을 제공한다. 

    라우팅 설정에 따라 URL별로 특정 컴포넌트를 선택적으로 표시하는 방법으로 페이지 이동을 구현할 수 있다.

     

    Vue Router란 무엇인가?


    Vue Router는 Vue.js 공식 플러그인으로 제공되는 라우팅 라이브러리로 SPA 개발에 사용된다.

     

    Vue Router는 기본적인 페이지 이동 기능 외에도 다음과 같은 고급 기능을 제공한다.

    • 중첩 라우팅
    • 리다이렉션과 앨리어싱
    • HTML5 History API와 URL 해시를 이용한 히스토리 관리 (IE9에서는 자동으로 풀백)
    • 자동으로 CSS 클래스가 활성화되는 링크 기능
    • Vue.js 트랜지션 기능을 이용한 페이지 이동 트랜지션
    • 커스터마이즈된 스크롤링

     

    라우팅 구현하기


    라우팅을 구현하려면 라우트와 라우터 생성자를 사용한다.

     

    라우트(route)란 URL과 뷰의 정보를 저장한 레코드를 말한다. 

    어떤 URL에 대해 어떤 페이지를 표시해야 하는지에 대한 정보라고 생각하면 된다. 

    애플리케이션을 구성하는 페이지마다 라우트를 정의하고 사용할 라우트를 지정하면 해당 라우트가 연결된 페이지로 이동한다.

     

    예를 들어 /goods/라는 URL에 goods 컴포넌트를 노출하도록 라우트를 정의하면 /goods에 접근했을 때 이 라우트가 goods 페이지가 표시되도록 하는 것이다.

     

    Vue Router의 라우트는 Vue.js의 컴포넌트를 특정 URL에 대응시킨 객체 형태를 갖는다. 

    이 객체를 라우터 생성자를 사용해 라우터를 초기화할 때 routes 옵션으로 설정한다. 

     

    다음과 같이 라우트 정의를 작성하고 라우터 생성자에 이 정의를 전달하면 vue 인스턴스를 생성할 때 라우팅 설정이 반영된다.

    어떤 URL에 접근할 때 어떤 컴포넌트를 렌더링해야 하는지가 지정되는 것이다.

     

    <!-- Vue.js와 Vue Router 로딩 -->
    
    <script>
    //라우트 옵션을 지정해 라우터 인스턴스를 생성
    var router = new VueRouter({
       // 컴포넌트를 매핑한 라우트 정의를 배열 형태로 전달
       routes: [{
          path: '/top',
          component: {
             template: '<div>최상위 페이지</div>'
          }
       },
       {
          path: '/users',
          component: {
             template: '<div>사용자 목록 페이지</div>'
          }
       }]
    })
    
    //라우터 인스턴스를 루트 Vue 인스턴스에 전달
    var app = new Vue({
       router: router
    }).$mount('#app')
    </script>

     

     

    라우터 정의와 Vue 인스턴스 생성이 끝났으면 라우팅이 동작할 HTML을 작성해야 한다.

    Vue 인스턴스가 마운트될 요소 외에도 라우트 정의에 사용된 컴포넌트를 실제로 반영시킬 요소가 있어야 한다.

     

    router-view 요소에 이 역할을 맡긴다. 

    그러면 라우트에서 매핑된 컴포넌트가 <router-view> 부분에 렌더링된다.

     

    링크를 표시해서 페이지 이동이 가능하게 하려면 router-link요소를 사용해 링크를 정의한다.

     

    <div id="app">
       <!-- 'to' 프로퍼티에 링크 대상을 지정 -->
       <!-- <router-link>는 기본적으로 '<a>' 태그로 렌더링된다. -->
       
       <router-link to="/top">최상위 페이지</router-link>
       <router-link to="/users">사용자 목록 페이지</router-link>
       <router-view></router-view>
    </div>

     

     

    전체 코드는 다음과 같다.

    <!DOCTYPE html>
    <html>
       </head>
          <meta charset="UTF-8">
          <title>Study</title>
       </head>
       
       <body>
          <div id="app">  
             <router-link to="/top">최상위 페이지</router-link>
             <router-link to="/users">사용자 목록 페이지</router-link>
             <router-view></router-view>
          </div>
          
    <script src="https://unpkg.com/vue@2.5.17"></script>
    <script src="https://unpkh.com/vue-router@3.0.1"></script>
    <script>
       var router = new VueRouter({
    
       routes: [{
          path: '/top',
          component: {
             template: '<div>최상위 페이지</div>'
          }
       },
       {
          path: '/users',
          component: {
             template: '<div>사용자 목록 페이지</div>'
          }
       }]
    })
    
    var app = new Vue({
       router: router
    }).$mount('#app')
    </script>
       
       </body>
    </html>

     

     

    Vue Router의 보조 기능


    URL 파라미터를 처리하는 방법과 패턴 매칭

    SPA는 접근 대상 URL의 패턴 매칭을 통해 파라미터를 전달하는 경우가 많다.

     

    예를 들면 사용자의 상세 페이지를 '/user/:userId'와 같은 식으로 URL로 전달받아서 이 URL에 포함된 사용자 ID에 따라 페이지를 구성하는 UI 구현이 이런 경우에 속한다.

     

    이런 경우에는 URL의 경로에 ' : ' 을 붙여 패턴을 작성한다.

    URL에서 이 패턴과 일치하는 파라미터는 컴포넌트에서 $route.params의 속성 중 패턴에 쓰인 파라미터 이름과 같은 속성명으로 접근할 수 있다.

     

    다음 예제는 /user/123에 대한 요청이 들어왔을 때 컴포넌트의 $route.params.userId의 값이 123이 됨을 보여준다. 

     

    var router = new VueRouter({
       routes: [{
          //패턴 매칭에 사용되는 패턴은 콜론으로 시작
          path: '/user/:userId',
          component: {
             template: '<div>사용자 IDsms {{ $route.params.userId }} 입니다. </div>'
          }
       }]
    })

     

    이름을 가지는 라우트

    Vue Router는 라웉트에 이름을 붙여 정의하고 HTML에서 이 이름을 사용해(<router-link>) 페이지 이동을 수행할 수 있다.

     

    에를 들어, 사용자 ID를 사용해 동적으로 만든 URL은 HTML에 정적으로 삽입할 수가 없다.

     

    라우트에 이름을 붙여 사용하면 이런 문제를 해결할 수 있다.

     

     

    다음 예제는 /user/:userId 경로에 user라는 이름을 붙여 라우트를 정희한 예다.

     

    var router = new VueRouter({
       routes: [{
          path: '/user/:userId',
          name: 'user',
          component: {
             template: '<div>사용자 ID는 {{ $route.params.userId }} 입니다.</div>'
          }
       }]
    })

     

    이에서 정의한 이름을 가진 라우트를 호출하려면 <router-link>에 to 파라미터를 지정하면 된다. 

    이때 URL 패턴의 파라미터도 함께 전달할 수 있다.

     

    <router-link:to="{name: 'user', params: {userId:123}}">
       사용자 상세 정보 페이지
    </router-link>

     

    router.push를 사용한 페이지 이동

    지금까지 살펴본 <router-link>는 선언적인 방식이었다.

    router.push를 사용해서 프로그램적인 방식으로 페이지를 이동할 수도 있다. 

     

    이것은 <router-link>를 클릭 할 때 내부적으로 호출되는 메소드이므로 <router-link :to="...">를 클릭하면 router.push(...)를 호출하는 것과 같다.

     

    이때 전달하는 인자는 <router-link>의 to 파라미터를 전달하는 객체와 같다.

    마찬가지로 이름을 가진 라우트를 사용할 수 있다.

     

    router.push({ name: 'user', params: {userId: 123}})

     

    훅 함수

    Vue Router는 페이지 이동 전후 시점에 추가 처리를 삽입할 수 있는 훅 함수를 제공한다.

     

    리다이렉트나 페이지 이동 전 사용자 확인 등을 구현하는 데 이 훅 함수를 사용한다. 

    전역 훅 함수, 라우트 단위 훅 함수, 컴포넌트 내 훅 함수 이렇게 3가지 패턴을 알아보겠다.

     

    전역 훅 함수

     

    전역 훅 함수는 모든 페이지 이동에 설정할 수 있는 함수다. 

     

    router.beforeEach 함수에 훅을 설정하면 페이지 이동 직전에 해당 함수가 실행된다.

     

    인자 to와 from은 각각 현재 페이지와 이동 대상 페이지 정보를 전달한다.

    이 2가지 인자에 담긴 라우트는 패턴이 일치한 라우트의 경로나 컴포넌트의 정보를 가진다.

     

    router.beforeEach(function(to, from, next) {
       // '/users'로 이동하면 /top으로 리다이렉트
       if(to.path === '/users') {
          next('/top')
       } else {
          // 인자 없이 next를 호출하면 일반적인 페이지 이동
          next()
       }
    })

     

    위 예제는 '/users'에 접근하면 '/top' 페이지로 리다이렉트시킨다.

    일반적인 라우팅과 같이 페이지를 이동하려면 next()를 인자 없이 호출하면 된다.

     

    이 훅 함수에서 next를 호출하지 않으면 페이지 이동이 무한 반복되므로 주의해야 한다.

     

    라우트 단위 훅 함수

     

    특정 라우트만을 대상으로(per-route) 훅을 추가하려면 Vue router를 초기화 할 때 라우트 정의에서 개별적으로 설정해야 한다.

     

    라우트 정의에 beforedEnter를 작성하면 페이지 이동 전에 실행되는 훅을 추가한다.

     

    var router = new VueRouter({
       routes: [{
          path: '/users',
          component: UserList,
          beforeEnter: funtion(to, from, next) {
             // /users?redirect=true로 접근할 때만 top으로 리다이렉트하는 훅 함수를 추가
             if(to.query.redirect === 'true') {
                next('/top')
             } else {
                next()
             }
          }
       }]
    })

     

    컴포넌트 내 훅 함수

     

    라우트뿐만 아니라 컴포넌트에서도(in-component) 훅 함수를 정의할 수 있다. 

     

    다음은 컴포넌트 옵션에서 beforeRouteEnter를 정의하고 이를 사용해서 데이터를 받아온다.

     

    var UserList = {
       template: '#user-list',
       data: funtion() {
          return {
             users: function() {
                return []
             },
             error: null
          }
       },
       //페이지가 이동되었으나 컴포넌트가 초기화되기 전에 호출된다.
       beforeRouteEnter: function(to, from, next) {
          gerUsers((funtion(err, users) {
             if(err) {
                this.error = err.toString()
             } else {
                // next로 전달되는 콜백함수로 자기 자신에 접근한다.
                next(funtion(vm) {
                   vm.users = users
                })
             }
          }).bind(this))
       }
    }

     

    위 예는 컴포넌트가 화면에 표시되는 시점에 실행되는 훅인 beforeRouteEnter를 사용한다. 

    그 외에도 다음 페이지 이동으로 인해 컴포넌트가 사라지는 시점에 실행되는 훅인 beforeRouteLeave를 사용할 수도 있다.

     

     

    Vue Router의 고급 기능


    Router 인스턴스와 Route 객체

    $router$route에 대해 알아보자.

     

    이 두 객체는 이름은 비슷하지만, 전혀 다른 것이기 때문에 주의가 필요하다.

     

    this.$router.push처럼 컴포넌트에서 Router 인스턴스에 접근하는 코드가 있다.

    $routerRouter 인스턴스를 가리킨다. 

    Router 인스턴스는 웹 애플리케이션 전체에서 딱 하나만 존재하는 것으로 전반적인 라우터 기능을 관리한다.

    예를 들면 애플리케이션 전체에서 히스토리를 어떻게 관리할지에 대한 설정이나 router-link 요소 없이 프로그램적인 방법으로 페이지를 이동할 때 이 Router 인스턴스를 사용한다. 

     

    이와 달리 this.$route.params 등의 코드에 나오는 $routeRoute 객체다. 

    페이지 이동 등으로 라우팅이 발생할 때마다 생성된다.

    현재 활성화된 라우트의 상태를 저장한 객체로, 현재의 경로 및 URL 파라미터 등의 정보를 이 객체에서 받을 수 있다.

    컴포넌트 내부에 구현된 Router 훅 함수 등을 통해서도 참조할 수 있다.

    watch에서 모니터링하기도 한다.

     

    다음은 이 두 객체의 대표적인 기능을 표로 정리하였다.

    프로퍼티 / 메서드명 설명
    app 라우터를 사용하는 루트 Vue 인스턴스
    mode 라우터 모드
    currentRoute 현재 라우트에 대한 Route 객체
    push(loaction, onComplete?, onAbort?) 페이지 이동 실행, 히스토리에 새 엔트리를 추가하고 브라우저에서 뒤로 가기 버튼을 누르면 앞의 URL로 돌아감
    replace(location, onComplete?, onAbort?) 페이지 이동 실행, 히스토리에 새 엔트리 추가하지 않음
    go(n) 히스토리 단계에서 n단계 이동. window.history.go(n)과 비슷함
    back() 히스토리에서 한 단계 돌아감. history.back()과 같음
    forward() 히스토리에서 한 단계 앞으로 나아감
    addRoutes(routes) 라우터에 동적으로 라우트를 추가

    < Router 객체의 주요 프로퍼티와 메서드 >

     

     

    프로퍼티 설명
    path 현재 라우트의 경로를 나타내는 문자열
    params 정의된 URL 패턴과 일치하는 파라미터의 키-값 쌍을 담고 있는 객체,          파라미터가 없다면 빈 객체
    query 쿼리 문자열의 키-값 쌍을 담고 있는 객체.                                                          쿼리가 없다면 빈 객체.                                                                                            /foo?user=1이면 $route.query.user === 1이 된다
    hash 현재 URL에 URL 해시가 있을 경우 라우트의 해시값을 갖는다.                    해시가 없다면 빈 객체
    fullPath 쿼리 및 해시를 포함하는 전체 URL
    name 이름을 가진 라우트인 경우 라우트의 이름

    < Route 객체의 주요 프로퍼티와 메서드 >

     

     

    중첩 라우팅

    애플리케이션이 복잡해짐에 따라 중첩 라우팅이 필요해지는 경우가 있다.

     

    Vue Router의 중첩 라우팅은 어떤 컴포넌트 안에 든 컴포넌트에 대한 라우트 정의를 말한다.

     

    예를 들어 페이지 내용은 '/user/사용자_id'를 기본으로 하되 '/user/사용자_id/posts'이면 포스트 정보를 노출하고,

    '/user/사용자_id/profile'이면 프로필 정보를 부분적으로 노출하는 정의가 중첩 라우팅이다.

     

    컴포넌트 정의에는 <router-view>, 라우트 정의에서 children 속성에 안긴 컴포넌트 부분을 설정하면 된다.

     

    //사용자 상세 정보 페이지 컴포넌트 정의
    
    var User = {
       template: '<div class="user">' +
          '<h2>사용자 ID는 {{ $route.params.userId }} 입니다. </h2>' +
          '<router-link :to="\'/user/\' + $route.paras.userId + \'/posts\'">' +
             '사용자의 글 모음 보기' +
          '</router-link>'+
          '<router-view></router-view>' +
          '</div>'
    }
    
    var UserPosts = {
       template: '<div class="user-posts">' +
          '<h3>사용자 {{ $route.params.userId }} 의 글 모음 페이지입니다.</h3>' +
          '</div>'
    }
    
    var router = new VueRouter({
       routes: [{
          path: '/user/:userId',
          name: 'user',
          component: User,
          children: [{
             // /user/:userId/profile 와 일치한 경우
             // UserProfile 컴포넌트는 User 컴포넌트의 <router-view> 안에서 렌더링됨
             path: 'profile',
             component: UserProfile
          },
          {
             // /user/:userId/posts 와 일치한 경우
             // UserPosts 컴포넌트는 User 컴포넌트의 <router-view> 안에서 렌더링됨
             path: 'posts',
             component: UserPosts
          }]
       }]
    })
       

     

    리다이렉션과 앨리어싱

    간혹 SPA에서도 일반적인 웹 애플리케이션과 마찬가지로 리다이렉션 기능을 사용해야 하는 경우가 있다.

     

    Vue Router는 URL을 바꿔주는 리다이렉션과 URL 수정 없이 라우팅 처리만 해주는 앨리어싱 기능을 제공한다.

     

    리다이랙션

     

    다음 리다이랙션 예제 코드는 '/a'에 접근하면 '/b'를 연결해준다. 

    이때 URL도 연결 대상에 대한 URL로 바뀐다. 또한 *를 사용하면 현재 정의된 모든 라우트와 일치하지 않았을 때 리다이렉션 대상을 지정할 수 있다.

     

    대표적인 예로 Not Found 페이지를 만들 때 유용하다.

     

    var router = new VueRouter({
       routes: {
          { path: '/a', redirect: '/b' },
          { path: '/b', component: B },
          { path: '/notfound', component: NotFound },
          
          //현재 URL이 어떤 라우트와도 일치하지 않으면 '/notfound'로 이동
          { path: '*', redirect: '/notfound' }
       ]
    })

     

     

    앨리어싱

     

    접근한 URL은 그대로 두고 다른 라우터에서 정의한 페이지로 이동하도록 하려면 앨리어싱을 사용한다.

     

    다음의 첫 번째 예제는 '/b'에 접근했을 때 URL은 '/b'로 그대로 두고 컴포넌트는 A를 렌더링해서 마치 '/a'에 접근한 것처럼 보이게 한다. 앨리어싱은 하나 이상을 지정할 수 있다.

     

    var router - new VueRouter({
       routes: [
          { path: '/a', component: A, alias: '/b' }
          { path: '/c', component: C, alias: ['/d', '/e'] }
       ]
    })

     

     

    히스토리 관리

     

    SPA는 서버 사이드의 라우팅을 거치지 않기 때문에 브라우저 히스토리 역시 클라이언트에서 관리해야 한다.

     

    히스토리 관리는 URL 해시를 사용하는 방법과 HTML5 History API를 사용하는 두 가지 방법이 있다.

     

    URL 해시

     

    URL 해시는 URL 끝에 '#'를 붙여서 라우팅 경로를 관리한다.

    Vue Router는 기본적으로 URL 해시로 동작한다.

     

    클라이언트 쪽에서 URL이 변화하기 때문에 브라우저 히스토리에는 URL이 각각 추가된다. .

    브라우저에서 뒤로 가기 혹은 앞으로 가기 버튼을 누르면 내부적으로는 hashchange 이벤트로 라우팅 변경 시와 같은 처리가 일어난다. 

     

    이런 방식은 사용자가 직접 브라우저에 URL을 입력해 접근해도 따로 특별한 처리를 하지 않아도 딘다.

     

     

    HTML5 History API

     

    히스토리를 관리하는 또 다른 방법은 HTML5부터 도입된 HTML5 History API로 히스토리 스택을 조작하는 것이다.

     

    이 방법을 사용하면 '#/'을 붙이지 않아도 일반적인 서버 사이드 라우팅과 가은 방식의 URL을 사용할 수 있다.

    그러나 사용자가 직접 브라우저에 URL을 입력해 접근한 경우에 적절히 SPA 페이지를 반환하도록 하는 처리가 필요하기 때문에 주의가 필요하다. 

     

    Vue Router 인스턴스를 만들 때 옵션 객체의 mode속성을 'history'로 설정하면 History API를 사용할 수 있다.

Designed by Tistory.