티스토리 뷰

728x90
반응형

Vue-router

예전에는 아래처럼 별도로 설치해서 사용했지만 지금은 vue-cli 에서 아예 설치를 해준다.

 

터미널에 아래와 같은 구문으로 라우터를 추가

- ( TypeScript 사용자는 vue-router@3.0+ vue@2.5+를 사용 )

npm install vue-router

 

설치한 라우터 사용( 라우터를 명시적 추가 및 선언 )

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
const User = {
  template: '<div>User</div>'
}

const router = new VueRouter({
  routes: [
    // 동적 세그먼트는 콜론으로 시작합니다.
    { path: '/user/:id', component: User }
  ]
})

동적 세그먼트는 콜론 : 으로 표시한다.

라우트가 이리하면 동적 세그먼트의 값은 모든 컴포넌트에서 this.$route.params 로 표신된다.

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}

동일한 라우트에 여러 동적 세그먼트를 가질 수 있으며, $route.params의 해당 필드에 매핑된다.

/user/:username  --> /user/tom  --> { username:'tom'}

 

$route.params 외에도 $route 객체는 $route.query (URL에 쿼리가 있는 경우), $route.hash 등의 유용한 정보를 제공한다.

 

 

 

아래는 직접 실무에 도입했던 코드 중 vue-router 설정하는 index.ts 이다.

import Vue from 'vue';
import store from '@/store';
import VueRouter, {RawLocation, Route, RouteConfig } from 'vue-router';
 // ---------------------- 중략 -------------------------------//
import { LoginRoute } from '@/router/login';



Vue.use( VueRouter );

const routes: Array<RouteConfig>=[
    ...LoginRoute,
   // ---------------------- 중략 -------------------------------//
    {
        path: '*',
        name: 'notfound',
        meta: {
            layout: 'full'
        },
        components: { default: ()=>import('@/views/notfound/NotFound')}
    }
];

const router=new VueRouter( {
    mode: 'history',
    base: process.env.VUE_APP_BASE_URL,
    routes,
} );

//전역 가드 
router.beforeEach( (to, from, next)=>{
    if (to.meta !== undefined) {
        const token=getToken();
        const metaInfo=to.meta;

        //로그인 상태 인지 파악
        if (metaInfo.auth) {
        
            //우선 token 값이 있는 사용자 인지 파악 ( store 의 isAuth )
            if (token) {
                // 로그인한 user 정보 및 token 가져오기 값이 false 일때
                if (!isUserLoggedIn()) {
                    //인증 필요하다는 팝업 띄움.
                    store.commit( `Alert/${ AlertMutationTypes.VALID_AUTH }`, false );
                    return next( '/login' );
                }else{
                    //화면 권한
                    if (metaInfo.authName) {
                        // console.log( to, from );
                        const findIdx=getAuthority().findIndex( (item: string)=> item === metaInfo.authName );
                        
                        if (findIdx !== -1) {
                            return next();
                        }else{
                            //접근 권한이 없는 경우 - 접근권한 없다는 팝업 띄움.
                            store.commit( `Auth/${ PageAuthMutationType.IS_AUTH }`, false );
                            // 메인으로 돌려보낸다.
                            return next('/');
                        }
                    }else{
                        return next();
                    }
                }
            }else{
                console.log( '인증이 필요합니다' );
                return next( '/login' );
            }
        }
    }
    return next();
} );

export default router;

책이나 기타 인터넷 뒤져보면 라우터 설정하는 부분을 아래처럼 오브젝트 타입으로 모두 나열하여 설정한다.

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

 

하지만 실무에서는 보통 라우터 설정 페이지가 많기에  카테고리별로 나누어야 한다.

 

아래 코드에서 ...LoginRoute 라고 전개연산자를 통해 routes 배열에 합치는 것을 볼 수 있다. 

라우터 설정하는 페이지들이 많다면 따로 카테고리별로 분리해 두는 것이 관리면에서 좋다. 

 

const routes: Array<RouteConfig>=[
    ...LoginRoute,
   // ---------------------- 중략 -------------------------------//
    {
        path: '*',
        name: 'notfound',
        meta: {
            layout: 'full'
        },
        components: { default: ()=>import('@/views/notfound/NotFound')}
    }
];

 

아래는 로그인 설정 라우트 이다.

login.ts

import { RouteURLManage } from '@/router/url';
import { NavigationGuardNext, Route } from 'vue-router';
import { isUserLoggedIn } from '@/core/auth/utils';

export const LoginRoute=[
    {
        path: RouteURLManage.LOGIN,
        name: 'login',
        meta: { layout: 'full', renderClass: 'login' },
        beforeEnter: (to:Route, from:Route, next:NavigationGuardNext)=>{
            //로그인한 user 정보 및 token 가져오기 - 값이 있을 경우 main  으로 보냄.
            if (isUserLoggedIn()) {
                return next( '/');
            }
            return next();
        },
        component: ()=>import('@/views/login/LoginForm'), //로그인 랜딩 페이지
    },
];

 

설정한 부분을 아래와 같이 간략이 설명을 추가해 보았다.

 

route 코드 설정 설명

{
    path: //라우터 주소
    name: //라우터 name --> vue 클래스 내에서 this.$router.push({name:'adminList'}) 이런식으로 접근할 때 사용. 권장하지 않음.
    meta: {
        layout: 'side',  // 초기 화면 레이아웃 정할 때 쓰인다. 값이 'side' 일 경우 좌측 사이드 메뉴가 노출되게 된다.
        renderClass:  //  FullType.ts 에 정의 되어 있고 class="wrapper"( 페이지 전체를 감싸는 div ) 에 addClass 할 클래스 이름
        auth:  // 인증 권한이 필요한 화면 -> 하단 beforeEach 전역 가드에서 설정하고 있다.
        authName:  // 각 화면의 권한 code name 을 지정하고 있다.  전역가드에서 화면에 접근/불가 등의 기준이 된다.
    },
    components: { 

                default: ()=>import( /* vue 페이지 컴포넌트 경로 - 해당 라우터에 접근시 동적 로드*/ ),

                header:// 상단 header 컴포넌트 지정 - 멤버일 때 header 컴포넌트가 다르게 지정할 수 있다.

           }

}

여기서 등장하는 것이 meta 라는 필드이다.

 

간혹 Route 에 대한 임의의 정보를 첨부하고 싶을 수 있기에 Vue-router 에서 meta 라는 동적 필드를 추가해 두었다고 한다.

이로 인해 meta 속성의 개체를 허용하고 경로 위치 및 탐색 가드 등 액세스할 수 있는 속성을 추가 정의해 둘 수 있겠다.

 

위의  'route 코드 설정 설명' 중  meta.auth, meta.authName 등은 LoginRoute.ts 에 추가되어 있지 않다.

해당 로그인 라우터는 인증 및 권한이 필요없는 페이지이기에 그렇다. 

로그인한 멤버일 경우 페이지 이동시 meta.auth, meta.authName 등이 필요로 하다.

 

 

전역 가드 / 라우터 가드 / 컴포넌트 내부가드 

 

전역 가드

router.beforeEach를 사용하여 보호하기 이전에 전역 등록을 할 수 있다.

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

네비게이션이 트리거될 때마다 가드가 작성 순서에 따라 호출되기 전의 모든 경우에 발생한다.

가드는 비동기식으로 실행 될 수 있으며 네비게이션은 모든 훅이 해결되기 전까지 보류 중 으로 간주된다.

모든 가드 기능은 세 가지 전달인자를 받는다.

  • to: 라우트: 대상 Route 객체로 이동.
  • from: 라우트: 현재 라우트로 오기전 라우트.
  • next: 함수: 이 함수는 훅을 해결하기 위해 호출되야 다음 라우트가 있는지 탐색이 된다. 액션은 next에 제공된 전달인자에 달려 있다.
    • next(): 파이프라인의 다음 훅으로 이동. 훅이 없는 경우 네비게이션은 승인.
    • next(false): 현재 네비게이션을 중단. 브라우저 URL이 변경되면 from경로의 URL로 재설정됩니다.
    • next('/') 또는 next({ path: '/' }): 다른 위치로 리디렉션. 현재 네비게이션이 중단되고 새 네비게이션이 시작된다.
    • next(error): (2.4.0 이후 추가) next에 전달된 인자가 Error 의 인스턴스이면 탐색이 중단되고 router.onError()를 이용해 등록 된 콜백에 에러가 전달.

주의 : next 함수를 호출하지 않으면 훅이 절대 불러지지 않는다.

 

 

아래는 index.ts 에서 전역가드 부분이다. 

//전역 가드 
router.beforeEach( (to, from, next)=>{
    if (to.meta !== undefined) {
        const token=getToken();
        const metaInfo=to.meta;

        //로그인 상태 인지 파악
        if (metaInfo.auth) {
        
            //우선 token 값이 있는 사용자 인지 파악 ( store 의 isAuth )
            if (token) {
                // 로그인한 user 정보 및 token 가져오기 값이 false 일때
                if (!isUserLoggedIn()) {
                    //인증 필요하다는 팝업 띄움.
                    store.commit( `Alert/${ AlertMutationTypes.VALID_AUTH }`, false );
                    return next( '/login' );
                }else{
                    //화면 권한
                    if (metaInfo.authName) {
                        // console.log( to, from );
                        const findIdx=getAuthority().findIndex( (item: string)=> item === metaInfo.authName );
                        
                        if (findIdx !== -1) {
                            return next();
                        }else{
                            //접근 권한이 없는 경우 - 접근권한 없다는 팝업 띄움.
                            store.commit( `Auth/${ PageAuthMutationType.IS_AUTH }`, false );
                            // 메인으로 돌려보낸다.
                            return next('/');
                        }
                    }else{
                        return next();
                    }
                }
            }else{
                console.log( '인증이 필요합니다' );
                return next( '/login' );
            }
        }
    }
    return next();
} );

 

라우터 가드

- 아래 befoeEnter는 각 라우트에 해당하는 개별 라우트 가드이다.

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Home,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

네비게이션이 트리거될 때마다 가드가 작성 순서에 따라 호출되기 전의 모든 경우에 발생한다.

가드는 비동기식으로 실행 될 수 있으며 네비게이션은 모든 훅이 해결되기 전까지 보류 중 으로 진행된다.

모든 가드 기능은 세 가지 전달인자를 받는다.

  • to: 라우트: 대상 Route객체로 이동
  • from: 라우트: 현재 라우트로 오기전 라우트
  • next: 함수: 이 함수는 훅을 해결하기 위해 호출 되어야 한다.
  • next(): 파이프라인의 다음 훅으로 이동. 훅이 없는 경우 네비게이션은 승인.
  • next(false): 현재 네비게이션을 중단. 브라우저 URL이 변경되면(사용자 또는 뒤로 버튼을 통해 수동으로 변경됨) from경로의 URL로 재설정된다.
  • next('/') 또는 next({ path: '/' }): 다른 위치로 리디렉션.
  • next(error): (2.4.0 이후 추가) next에 전달된 인자가 Error 의 인스턴스이면 탐색이 중단되고 router.onError()를 이용해 등록 된 콜백에 에러가 전달.

 

컴포넌트 내부가드 

마지막으로 beforeRouteEnter  beforeRouteLeave를 사용하여 라우트 컴포넌트(라우터 설정으로 전달되는 컴포넌트) 안에 라우트 네비게이션 가드를 직접 정의 할 수 있다.

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 버전에 추가)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 이 컴포넌트를 렌더링하는 라우트 앞에 호출됩니다.
    // 이 가드가 호출 될 때 아직 생성되지 않았기 때문에
    // `this` 컴포넌트 인스턴스에 접근 할 수 없습니다!
  },
  beforeRouteLeave (to, from, next) {
    // 이 컴포넌트를 렌더링하는 라우트가 이전으로 네비게이션 될 때 호출됩니다.
    // `this` 컴포넌트 인스턴스에 접근 할 수 있습니다.
  }
}

beforeRouteEnter 가드는 네비게이션이 확인전 가드가 호출된다.  컴포넌트가 생성되지 않은 시점이기에 this에 접근하지 못한다.

 

그러나 콜백을 next에 전달하여 인스턴스에 액세스는 가능하다. 네비게이션이 확인되고 컴포넌트 인스턴스가 콜백에 전달인자로 전달 될 때 콜백이 호출된다.

beforeRouteEnter (to, from, next) {
  next(vm => {
    // `vm`을 통한 컴포넌트 인스턴스 접근
  })
}

beforeRouteLeave 안에서 this에 직접 접근 할 수 있다.

leave 가드는 일반적으로 사용자가 저장하지 않은 편집 내용을 두고 실수로 라우트를 떠나는 것을 방지하는데 사용한다.

탐색은 next(false)를 호출하여 취소할 수 있다.

 

 

클래스 컴포넌트 방식에서 컴포넌트 내부 가드

근데 여기서 주의 해야 할 것은 클래스 컴포넌트 방식에서는 Component.registerHooks 에 미리 등록 하고 써야 한다는 것이다.  

 

import { Component, Vue } from 'vue-property-decorator';

// Register the router hooks with their names
Component.registerHooks([
    'beforeRouteEnter',
    'beforeRouteLeave',
]);


@Component
export default class TestComponent extends Vue{ 

   public beforeRouteEnter(to:Route, from:Route, next:NavigationGuardNext) {
        next();
   }

   public beforeRouteLeave(to:Route, from:Route, next:NavigationGuardNext) {
        next();
   }
    
}

 

 

728x90
반응형
댓글