네비게이션 가드
공식문서
네비게이션 가드란 특정 페이지를 보호하기 위해 사용하는 방법으로서 URL의 접근을 제한하는 것을 의미한다.
😥 문제 상황
로그인을 하지 않은 유저:
개인 정보 수정
,회원 탈퇴
페이지에 접근을 못한다.로그인을 한 유저:
로그인
,회원가입
페이지에 접근을 못한다.
회원 관리에 관한 페이지를 개발한 후, 로그인 여부에 따라 페이지에 접근할 수 있는 것을 제한하려 했다. 그러나 로그인 여부 자체를 인식하지 못했다.
1) 로그인 유저인지 확인을 못하나..?
로그인유저 확인 로직
- 로그인 했을 때, 유저의 access토큰과 refresh토큰을 로컬 스토리지와 vuex에 저장한다.
- 브라우저를 재 시작 했을 때, 로컬 스토리지에 토큰 정보가 있으면 이를 vuex에 저장한다.
- vuex에 토큰이 저장되어 있을 경우 해당 유저는 로그인 유저로 간주한다.
vuex를 확인해보면, 로그인 여부는 잘 확인하고 있는 것을 알 수 있었다.
2) Router 코드가 문젠가..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// src/router.ts
import { computed } from "vue";
import { useStore } from "vuex"
const store = useStore()
// Navigation Guard 설정: 오류를 해결하기 위해 임의로 any타입을 사용했다.
router.beforeEach((to: any, from, next) => {
// 로그인 여부 확인
const isLoggedIn = computed(() => store.getters["personalInfo/isLoggedIn"]);
// 로그인이 필요한 페이지
const authPages = ["TheSignOutView", "TheChangeUserView", "TheChangePwView"];
// 로그인이 되어있지 않을 때만 가능한 페이지
const notAuthPages = ["TheSignupView", "TheLoginView"];
// 1. 현재 이동하고자 하는 페이지가 로그인이 필요한지 확인
const isAuthRequired = authPages.includes(to.name);
const isNotAuthRequired = notAuthPages.includes(to.name);
// 2. 로그인이 필요한 페이지인데 로그인이 되어있지 않다면 로그인 페이지(/login)로 이동
if (isAuthRequired && !isLoggedIn.value) {
next({ name: "TheLoginView" });
// 3. 로그인이 되어있는데 /login, /signup 페이지로 이동한다면 메인 페이지(/)로 이동
} else if (isNotAuthRequired && isLoggedIn.value) {
next({ name: "TheHomeView" });
// 4. 원래 이동 페이지
} else {
next();
}
});
router.ts에 네비게이션 가드로 작성한 코드가 로직적인 문제가 있는지 확인해 보았다.
- 현재 유저가 로그인 유저인지 확인한다.
- 유저의 로그인 상태에 따라 접근할 수 있는 페이지와 접근할 수 없는 페이지를 나눈다.
- 현재 이동하고자 하는 페이지가 어떤 페이지 인지 확인한다.
- 현재 이동하고자 하는 페이지가 어디에 속해있는지
includes
함수를 통해 확인
- 현재 이동하고자 하는 페이지가 어디에 속해있는지
- 1~3에서 확인한
유저의 로그인 여부
와이동하고자 하는 페이지의 종류
에 따라 이동되는 분기가 나뉜다.- 권한이 없는 페이지에 이동하려고 할 시, 지정된 페이지로 이동
- 권한이 있는 페이지에 이동하려고 할 시, 원래 이동하려고 하는 페이지로 이동
로직은 큰 문제가 없는 것 같은데.. 🤔
🤸♂️ 해결 방법
위의 코드에서 문제가 되는 부분은 다음과 같다.
1
2
3
4
// src/router.ts
const isLoggedIn = computed(() => store.getters["personalInfo/isLoggedIn"]);
console.log(isLoggedIn)
console에 찍어보니 undefined
가 출력 되어 로그인 여부를 확인할 수 없는 것이다. 분명 store에는 값이 잘 저장되어 있는데 왜 값을 못 가져오는 걸까.
기존에 컴포넌트에서 store를 접근할 때는 useStore
라는 메서드를 통해 접근할 수 있기 때문에 외부 ts파일도 이렇게 접근이 가능할 것이라고 생각했었다. 그러나 같은 코드로 접근하면 안된다.
1
2
3
4
5
6
7
8
9
// src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
const app = createApp(App);
app.use(store).use(router);
app.mount("#app");
- vue는 이렇게 main.ts에서 store가 연동되어 있기 때문에
{useStore}
메서드로 접근을 할 수 있다. (추측..) - 그래서 vue파일이 아닌 외부 파일에서 접근을 하기 위해서는 직접 store에 접근해서 store를 가져와야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/store/index.ts
import { createStore } from "vuex";
import { account, AccountState } from "../store/accounts/account";
import { signup, SignupState } from "../store/accounts/signup";
import { password, PasswordState } from "../store/accounts/password";
import { personalInfo, PersonalInfoState} from "../store/accounts/personalInfo";
// 모듈의 state를 공유한다.
export interface RootState {
account: AccountState;
signup: SignupState;
personalInfo: PersonalInfoState;
password: PasswordState;
}
// store를 생성한다.
export default createStore({
modules: {
account,
signup,
personalInfo,
password,
},
});
- store를
createStore
를 통해 export하고 있기 때문에createStore
로 vuex의 store에 접근하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { computed } from "vue";
import createStore from "../src/store/index";
// 스토어 정의
const store = createStore;
// Navigation Guard 설정: 오류를 해결하기 위해 임의로 any타입을 사용했다.
router.beforeEach((to: any, from, next) => {
const isLoggedIn = computed(() => store.getters["personalInfo/isLoggedIn"]);
const authPages = ["TheSignOutView", "TheChangeUserView", "TheChangePwView"];
const notAuthPages = ["TheSignupView", "TheLoginView"];
const isAuthRequired = authPages.includes(to.name);
const isNotAuthRequired = notAuthPages.includes(to.name);
if (isAuthRequired && !isLoggedIn.value) {
next({ name: "TheLoginView" });
console.log("로그인 하지 않은 유저는 돌아가시오")
} else if (isNotAuthRequired && isLoggedIn.value) {
next({ name: "TheHomeView" });
} else {
next();
}
});
문제 없이 잘 작동 하는 것을 확인할 수 있다 😀