티스토리 뷰
vue 정리 - login(로그인) part1
vue.config.js 설정을 끝낸 후 여러 가지 것들을 셋팅해야 하지만 우선
로그인 부분을 먼저 다루어야 vuex / vue-router 등을 자연스럽게 한번에 세팅 할 수 있다.
대체적으로 아래 이미지와 같은 비즈니스 로직으로 처리 된다.

사항요약해 보면 다음과 같다.
1. login call ---> 로그인 화면에서 특정 아이디/패스워드로 로그인 시도.-> 서버측에 로그인 요청
2. token 처리 ---> 요청한 아이디/패스워드가 멤버가 맞다면 jwt(json web token) 값을 client 측에 전송
전송받은 jwt(json web token) 을 client 측에서 localstorage 에 담아 두고
요청 headers 의 Authorization 에 jwt(json web token)을 넣어서 로그인한 사용자 정보를 요청 전송한다.
3. me call ---> 최종적으로 서버에서 jwt(json web token)을 확인하고 인증처리하여 로그인 사용자 정보를 client 측에 전달해 준다.
4. user 정보 ---> client 에서 사용자 확인 후 main 으로 router 를 변경해 준다.
4가지로 요약했지만 그 사이사이에 많은 것들이 숨어있다.
일단 위 그림처럼 하려면 먼저 준비할 것들이 필요하다.
a. vuex-module-decorators( vuex 모듈 플러그인 )
b. vuex-class ( vuex 모듈 네임 바인딩 처리 플러그인 )
c. axios ( api 통신 라이브러리 )
위 3가지를 npm install 로 설치한다.
더 상세하게 기술해 보겠다.
1번에서 login call 말 그대로 단순히 로그인 정보를 전송하는 게 전부이다.
물론 전송 하는 부분은 스토어의 action 을 이용하긴 한다.
하지만 여기서 그것보다 전송 후가 중요한 부분이다. 다시 한번 더 짚어 보면 아래와 같다.
- 로그인 데이터 전송 >
- vue store action 과 연동되어 있는 login api 통신(axios 이용)으로 서버 접근. 사용자 인증 >
- 인증 후 서버에서 전달받은 token 을 vue store 에 token 정보 저장 >
- vue 에서 token 정보 저장 후 바로 headers 의 Authorization 에 token 을 담아서 사용자 정보 호출 >
- 서버에서 Authorization 체크하여 해당 사용자 정보 client ( vue ) 에 전송
이제 위 사항들을 하나씩 살펴 보겠다.
우선 로그인 데이터 전송부분 부터 살펴 보자.
LoginForm.ts
import { Vue, Component } from 'vue-property-decorator';
import { namespace } from 'vuex-class';
import { UserActionType } from '@/store/moduleType/AuthActionTypes';
......................중략.............................
import WithRender from './LoginForm.html';
const Auth=namespace( 'Auth' );
@WithRender
@Component( {
components: {
......................중략.............................
}
} )
export default class LoginForm extends Vue {
@Auth.Action
private [UserActionType.LOGIN]!: (payload: { id: string, password: string })=>Promise<any>;
private userId: string='';
private userPw: string='';
get isFieldRequired(): boolean {
return this.userId !== '' && this.userPw !== '';
}
private validate(): void {
if (this.isFieldRequired) {
this[UserActionType.LOGIN]( { id: this.userId, password: this.userPw } )
.then( (data: any)=>{
this.$router.push('/');
} );
} ).catch( (error: any)=>{
// 로그인 에러의 경우
......................중략.............................
} );
} else {
// validation 체크 통과 못한 경우
......................중략.............................
}
}
}
LoginForm.html
<div class="container">
.....................중략............................
<div class="contents-wrap">
<div class="contents">
<div class="login-form">
<div class="form-cnt">
<div class="form-input">
<div class="form-group">
<txt-field size="100%"
label-type="form-label"
label="ID"
id="userID"
placeholder="아이디를 입력해 주세요."
v-model="userId"
rules="required"
valid-name="아이디"></txt-field>
</div>
<div class="form-group mgt-20">
<txt-field input-type="password"
size="100%"
label-type="form-label"
label="Password"
id="userPw"
placeholder="비밀번호를 입력해 주세요."
v-model="userPw"
rules="required"
valid-name="비밀번호"
@keyup.enter.native="validate"></txt-field>
</div>
<div class="login-btn d-grid mgt-35">
<btn type="primary" @click="validate">Login</btn>
</div>
</div>
</div>
</div>
</div>
</div>
.....................중략............................
</div>
LoginForm.ts 에서 보면
해당 부분은 Store 의 Action 을 통해 통신을 주고 받고 성공시 라우터를 main페이지로 보낸다.
@Auth.Action
private [UserActionType.LOGIN]!: (payload: { id: string, password: string })=>Promise<any>;
라는 구문이 나온다. 이 부분은 store action 부분이다.
@Auth.Action 이라고 데코레이터 속성이다.
해당 부분은 스토어의 Auth 모듈 내에 action 을 선언했던 부분 그대로 가져와 똑같은 타입선언만 해주면 된다.
정말 간편하다.
const Auth=namespace( 'Auth' ); 라고 상단에 선언한 vuex-class와
vuex 모듈 클래스에서 사용하는 vuex-module-decorators 플러그인 덕분이다.
vuex 모듈에 선언해둔 store 클래스들을 손쉽게 모듈네임을 변경하여 쓸 수 있게 한다.
만약에 해당 플러그인이 없다면 좀 손이 많이 가게 된다. 아래와 같이 component 선언 부분에 mapActions 라는
promise 를 반환하는 Mapper 타입 함수를 써야만 한다.
@Component({
computed: {
...mapGettes({
checkCount: `Auth/checkCount`,
}),
},
methods: {
...mapActions({
[UserActionType.LOGIN]: `Auth/${UserActionType.LOGIN}`,
}),
},
})
하지만 vuex-class 의 namespace 를 사용하면 아래와 같이 명확해진다.
import { Vue, Component } from 'vue-property-decorator';
import { namespace } from 'vuex-class';
import { UserActionType } from '@/store/moduleType/AuthActionTypes';
......................중략.............................
import WithRender from './LoginForm.html';
const Auth=namespace( 'Auth' );
@WithRender
@Component( {
components: {
......................중략.............................
}
} )
export default class LoginForm extends Vue {
@Auth.Action
private [UserActionType.LOGIN]!: (payload: { id: string, password: string })=>Promise<any>;
......................중략.............................
private validate(): void {
if (this.isFieldRequired) {
//Action 부분
this[UserActionType.LOGIN]( { id: this.userId, password: this.userPw } )
.then( (data: any)=>{
this.$router.push('/');
} );
} ).catch( (error: any)=>{
// 로그인 에러의 경우
......................중략.............................
} );
} else {
// validation 체크 통과 못한 경우
......................중략.............................
}
}
}
참고로 아래 코드는 store/index.ts 에 있는 내용이다.
아래 코드에선 AuthModule 클래스로 되어 있는 것을 Auth 라고 네임을 변경해서 쓴다.
import Vue from 'vue';
import Vuex from 'vuex';
import { TokenActionType } from '@/store/moduleType/AuthActionTypes';
import { getToken } from '@/core/auth/utils';
import AuthModule from '@/store/AuthModule';
Vue.use( Vuex );
const store=new Vuex.Store( {
state: {},
modules: {
Auth: AuthModule,
................중략..................
}
} );
const { token, refreshToken }=localStorage;
//새로고침시 토큰이 있는지 체크
if (getToken()) {
//token 이 로컬스토리지에 존재할 경우 action 을 통해 토큰값을 다시 저장해 둔다.
store.dispatch( `Auth/${ TokenActionType.SIGN_IN_BY_TOKEN }`, { token, refreshToken } )
.catch( (error: any)=>{
console.log( 'SIGN_IN_BY_TOKEN=', error );
} );
}
export default store;
위 store/index.ts 마지막에 getToken() 함수로 토큰이 있는지 여부 체크하는 부분이 있는데 해당 부분은
만약 새로고침시 store 에 담아 두었던 데이터는 몽땅 리셋이 되어 버리기 때문에 token 을 다시 저장해 두어야 연동되는 다른 컨포넌트들이 지장을 안받는다. 해당 getToken() 함수는 아래 utils.ts 에 지정되어 있다.
auth 에 쓰이는 utils.ts
import AuthConfig from '@/core/auth/config';
..................중략...........................
export const isUser=()=>{
return localStorage.getItem( AuthConfig.ME_KEY );
};
// token 가져오기
export const getToken=()=>{
return localStorage.getItem( AuthConfig.TOKEN_KEY );
};
// refresh token 가져오기
export const getRefreshToken=()=>{
return localStorage.getItem( AuthConfig.REFRESH_TOKEN_KEY );
};
..................중략...........................
아래는 vuex 모듈 클래스 인 AuthModule.ts 와 type 별 선언한 클래스들이다.
AuthMutationType.ts
enum TokenMutationType {
GET_TOKEN='GET_TOKEN',
GET_REFRESH_TOKEN='GET_REFRESH_TOKEN',
SIGN_IN_BY_TOKEN='SIGN_IN_BY_TOKEN'
}
enum UserMutationType {
INFO='INFO',
LOGOUT='LOGOUT',
LOG_IN='LOG_IN',
ERROR_LOG='ERROR_LOG',
INFO_UPDATE='INFO_UPDATE'
}
enum PageAuthMutationType{
IS_AUTH='IS_AUTH'
}
export { TokenMutationType, UserMutationType, PageAuthMutationType };
AuthActionTypes.ts
enum UserActionType{
LOGIN='LOGIN'
}
enum TokenActionType{
SIGN_IN_BY_TOKEN='SIGN_IN_BY_TOKEN'
}
export { UserActionType, TokenActionType };
TokenMutationType.GET_TOKEN, AuthConfig.ME_KEY 등 함수이름, 인자값 등으로 입력되는 문자열들을 볼 수 있다.
대문자를 통해 해당 값이 상수값이다 라고 유추해 볼 수 있는 데...상수값이라 하면 일단 변하지 말아야 하는 값이다.
그렇다. 해당 값은 절대로 변해서 안되는 값인 것이다.
말인 즉슨 해당 값이 매번 어떠한 이유로 변한다면 무지막지한 에러 및 실행이 불가하다.
특히나 토큰정보/유저정보 등의 여러 컨포넌트 및 라우터에서 수시로 체크하는 것들은 오타가 발생할 수도 있고
잘못된 정보를 입력할 수 있다. 그럴 때 오류 및 에러를 방지하는 목적이 있다.
또 반대로 한번 설정하면 변경하지 말아야 할 저 상수값들을 프로젝트 어떠한 시점에 수정해야만 하는 경우가 간혹 생기기도 한다.
그럴 때 선언된 상수 문자열들은 그대로 두고 값만 변경하면 되기에 관리면에서도 유용하다.
전역적으로 혹은 자주 사용되는 값들은 상수로 선언. 즉 한 곳에서 중앙 관리가 키포인트이다.
AuthModule.ts
import { Action, Module, Mutation, VuexModule } from 'vuex-module-decorators';
import { PageAuthMutationType, TokenMutationType, UserMutationType } from '@/store/moduleType/AuthMutationTypes';
import { TokenActionType, UserActionType } from '@/store/moduleType/AuthActionTypes';
import { AuthService } from '@/restApi';
import { IAdminUserMe } from '@/model/admin/AdminUserModel';
import { AdminService } from '@/restApi/service/AdminService';
import AuthConfig from '@/core/auth/config';
import axios from 'axios';
import { LoadingMutationType } from '@/store/moduleType/LoadingMutationType';
import { isUser } from '@/core/auth/utils';
@Module( {
namespaced: true,
} )
export default class AuthModule extends VuexModule {
//멤버 변수는 state 로 이용된다.
public token!: string | null;
public me: IAdminUserMe | null=null;
private refreshToken: string | null=null;
get isAuth(): boolean {
return !!this.token;
}
get isRefreshAuth(): boolean {
return !!this.refreshToken;
}
get isMe(): IAdminUserMe | null {
return this.me;
}
@Mutation
public [TokenMutationType.GET_TOKEN](token: string | null): void {
if (token !== null) {
this.token=token;
localStorage.setItem( AuthConfig.TOKEN_KEY, this.token );
}
}
@Mutation
public [TokenMutationType.GET_REFRESH_TOKEN](refreshToken: string | null): void {
if (refreshToken !== null) {
this.refreshToken=refreshToken;
localStorage.setItem( AuthConfig.REFRESH_TOKEN_KEY, this.refreshToken );
}
}
@Mutation
public [UserMutationType.INFO](me: IAdminUserMe): void {
this.me=me;
localStorage.setItem( AuthConfig.ME_KEY, JSON.stringify( this.me ) );
}
.................중략...........................
//새로고침시 localstorage 에 있는 token 값 존재 확인 후 저장
@Action( { rawError: true } )
public [TokenActionType.SIGN_IN_BY_TOKEN](payload: { token: string, refreshToken: string }) {
const { token, refreshToken }=payload;
this.context.commit( TokenMutationType.GET_TOKEN, token );
this.context.commit( TokenMutationType.GET_REFRESH_TOKEN, refreshToken );
}
/**
* 로그인~
* @param payload
*/
@Action( { rawError: true } )
public [UserActionType.LOGIN](payload: { id: string, password: string }): Promise<any> {
const { id, password }=payload;
//LOGIN_ACTION
return AuthService.login( id, password )
.then( (data: any)=>{
this.context.commit( TokenMutationType.GET_TOKEN, data.accessToken );
this.context.commit( TokenMutationType.GET_REFRESH_TOKEN, data.refreshToken );
return AdminService.getMe()
.then( (data: any)=>{
this.context.commit( UserMutationType.INFO, data );
return Promise.resolve( 'signin status' );
} );
} ).catch( (error: any)=>{
return Promise.reject( error );
} );
}
}
위 코드에서 보면
this.context.commit( TokenMutationType.GET_TOKEN, data.accessToken );
this.context.commit( TokenMutationType.GET_REFRESH_TOKEN, data.refreshToken )
라는 부분이 인증 후 서버에서 전달받은 token 을 vue store 에 token 정보 저장 단계이다.
이제 나머지 jwt 와 관련된 부분을 할 차례이다.
( 아래 다음 파트로 이어짐 )
vue 정리 - login (로그인) part2
vue 정리 - login (로그인) part2
vue 정리 - login (로그인) part2 login par1. 에 이어서 jwt 즉 json web token 설정에 대해서 정리해보고자 한다. 이제 vue 에서 token 정보 저장 후 바로 headers 의 Authorization 에 token 을 담아서..
uxicode.tistory.com
'Programming language > vuejs' 카테고리의 다른 글
vue 정리 - login (로그인) part2 (0) | 2022.05.19 |
---|---|
vue 정리 part3 - Vue-router (0) | 2022.05.17 |
vue 정리 part1 - vue 설정. (0) | 2022.05.02 |
Vue.js 요약 (0) | 2019.04.04 |
vue-cli 및 typescript 설정 (0) | 2019.04.03 |
- Total
- Today
- Yesterday
- icon font
- 리프래시토큰
- Angular
- React.StrictMode
- RefreshToken
- CSS
- IntrinsicElements
- JsDoc
- 앵귤러
- svg 폰트
- react
- 반복문
- svg모션
- 태그
- react-router-dom
- Intrinsic
- Vue3
- interceptors
- 자바스크립트
- 아이콘 폰트 만들기
- Aptana
- vue-router
- for of 구문
- git checkout -b
- cordova
- anime.js
- 코도바
- git
- svg icon font
- 내장요소
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |