Vue.js 3 & NodeJS/Vue 3

Vue CLI 로그인 처리 1(로그인 모듈, 레스트 API 인증 연동) - Vue CLI Vuex Login Store, REST API Authentication)

carrotweb 2021. 9. 23. 19:02
728x90
반응형

로그인 Vue와 로그인 Store를 생성하고 내비게이션 가드(Navigation Guards)로 Login 후 처리되게 합니다.

Login Vue 생성하기

1. C:\workspaces\nodeserver\testvue\src\views 폴더에 Login.vue 파일을 생성합니다.

 

Login.vue 파일을 오픈하여 로그인 컴포넌트로 코딩합니다.

<template>
	<div class="login">
		<h1>This is an Login page</h1>
		<form class="loginform">
			<p>
				<label for="memberIdInput">아이디</label>
				<input type="text" id="memberIdInput" class="input_text" ref="memberIdInput" v-model.trim="memberId" placeholder="아이디를 입력하세요." />
			</p>
			<p>
				<label for="memberPasswordInput">패스워드</label>
				<input type="password" id="memberPasswordInput" class="input_text" ref="memberPasswordInput" v-model.trim="memberPassword" placeholder="패스워드를 입력하세요." />
			</p>
			<p class="buttons">
				<button @click.prevent="doLogin" class="button blue">로그인</button>
				<button @click.prevent="doCancel" class="button">취소</button>
			</p>
		</form>
		<p>{{ errorMessage }}</p>
	</div>
</template>

<script>
export default {
	name: 'Login',
	data : function() {
		return {
			memberId : '',
			memberPassword : '',
			errorMessage : ''
		};
	},
	methods : {
		doLogin() {
			if (this.memberId == "") {
				alert("아이디를 입력하세요.");
				this.$refs.memberIdInput.focus();
				return;
			} else if (this.memberPassword == "") {
				alert("패스워드를 입력하세요.");
				this.$refs.memberPasswordInput.focus();
				return;
			}
		},
		doCancel() {
			this.$router.push('../');
		}
	},
	mounted() {
		this.$refs.memberIdInput.focus();
	}
};
</script>

<style scoped>
.login { width:800px; margin:20px auto; }
.loginform { width:400px; margin:auto; }
.loginform p > label { display:inline-block; width:100px; font-size:14px; padding-right:10px; }
.loginform p > .input_text { width:200px; font-size:14px; height:32px; }
.buttons { position:relative; height:32px; margin-top:20px; }
.buttons > .button { overflow:visible; cursor:pointer; min-width:125px; height:32px; margin:0 2px; padding:0 15px; line-height:32px; font-size:14px; border:1px solid #dfdfdf; background:#fff; border-radius:10px; }
.buttons > .button.blue { color:#fff; border-color:#0099d2 !important; background:#0099d2 !important; }
</style>

@click에 prevent를 추가하면 form의 input에서 엔터키를 누를 때 form이 submit 되는 것을 막을 수 있습니다.

form의 submit으로 사용할 경우에는 다음과 같이 하면 됩니다.

<form class="loginform" @submit.prevent="doLogin()">
	<p class="buttons">
		<input type="submit" value="로그인" class="button blue" />
	</p>
</form>

 

2. C:\workspaces\nodeserver\testvue\src\router 폴더에서 index.js 파일을 오픈하여 const routes에 추가합니다.

{
  path: '/login',
  name: 'Login',
  component: () => import('../views/Login.vue')
}

 

3. 콘솔을 실행하고 Vue 프로젝트가 있는 C:\workspaces\nodeserver\testvue 폴더로 이동합니다. 그리고 콘솔에서 npm run 명령어를 실행합니다.

npm run serve

 

4. 웹 브라우저에서 "http://localhost:8080/login"를 입력합니다.

 

 

 

Login Store 모듈 생성하기

1. C:\workspaces\nodeserver\testvue\src\store\modules 폴더에 login.js 파일을 생성합니다.

 

login.js 파일을 오픈하여 기본 코딩합니다.

const loginStore = {
	namespaced: true,
	state: {
	},
	getters: {
	},
	mutations: {
	},
	actions: {
	}
};

export default loginStore;

 

액시오시(axios)를 사용하기 위해 import 하여 가져옵니다.

import axios from 'axios';

 

아이디와 Login 후 전달되는 accessToken를 저장하기 위해 state에 선언합니다.

state: {
	memberId: '',
	accessToken: ''
}

 

Login 여부를 확인하기 위해 state.accessToken를 확인하는 메서드를 추가합니다.

getters: {
	// 로그인 여부를 가져옵니다.
	isLogin(state) {
		return state.accessToken == '' ? false : true;
	}
}

 

memberId와 state.accessToken를 변경하기 위해 추가합니다.

mutations: {
	// memberId를 설정합니다.
	setMmemberId(state, memberId) {
		state.memberId = memberId;
	},
	// accessToken를 설정합니다.
	setAccessToken(state, accessToken) {
		state.accessToken = accessToken;
	},
	// 초기화시킵니다.
	reset(state) {
    	state.memberId = '';
		state.accessToken = '';
	}
}

 

반응형

 

Login 처리를 위해 Node.js 레스트 API 서버의 login API를 호출하고 Login이 성공하면 setAccessToken() 메서드를 commit 하고 실패하면 에러 메시지를 설정합니다.

리턴은 Promise로 하여 Login.vue에서 성공(resolve)과 실패(reject)로 처리할 수 있게 합니다.

actions: {
	// 로그인합니다.
	async doLogin({ commit }, memberInfo) {
		let result = false;
		let resultErr = null;
		try {
			let res = await axios.post("http://localhost:9000/members/login", memberInfo);
			if (res.data.success == true) {
				console.log("로그인되었습니다.");
				commit('setMmemberId', memberInfo.id);
				commit('setAccessToken', res.data.accessToken);
				result = true;
			} else {
				console.log("로그인되지 않았습니다.");
				let err = new Error("Request failed with status code 401");
				err.response = {data:{"success":false, "errormessage":"로그인되지 않았습니다."}};
				resultErr = err;
			}
		} catch(err) {
			console.log(err);
			if (!err.response) {
				err.response = {data:{"success":false, "errormessage":err.message}};
			}
			resultErr = err;
		}
		return new Promise((resolve, reject) => {
			if (result) {
				resolve();
			} else {
				reject(resultErr);
			}
		});
	},
	// 로그아웃합니다.
	doLogout({commit}) {
		commit('reset');
	}
}

 

2. index.js 파일에 login.js 파일을 import로 가져오고 modules에 loginStore를 키(사용할 이름):값(import로 가져온 이름)으로 추가합니다.

import { createStore } from 'vuex';
import scoreStore from '@/store/modules/score.js';
import loginStore from '@/store/modules/login.js'; --> 추가

export default createStore({
	modules: {
		scoreStore: scoreStore,
		loginStore: loginStore --> 추가
	}
});

 

 

Login 연동하기(내비게이션 가드(Navigation Guards)로 처리)

1. C:\workspaces\nodeserver\testvue\src\router 폴더에 index.js 파일을 오픈하여 import로 store을 가져옵니다.

import store from '../store';
or
import store from '@/store';

 

테스트로 경로가 "/score"일 때만 Login를 처리하겠습니다.

라우트 전역 처리

export 전에 router.beforeEach를 추가합니다. 경로가 "/score"일 때만 적용합니다.

router.beforeEach((to, from, next) => {
  if (to.path == "/score") {
    const isLogin = store.getters['loginStore/isLogin'];
    if (!isLogin) {
      next('/login?returnUrl=' + to.fullPath);
    } else {
      next();
    }
  } else {
    next();
  }
});

to는 이동할 URL에 대한 객체입니다.

form은 현재 URL에 대한 객체입니다.

next는 이동하기 위한 메서드입니다.

loginStore의 isLogin() 메서드를 사용하여 Login 여부를 가져오고 로그인이 되어 있지 않으면 Login Vue로 이동하게 합니다. 파라미터로 이동할 URL의 풀 경로를 전달합니다.

 

next() 메서드를 객체(object)로 처리해도 됩니다.

next({path: '/login', query: { returnUrl: to.fullPath }});

to.path를 제거하면 전체 URL에 Login이 처리되어야 이동할 수 있습니다.

 

라우트 별 처리

Score 라우트에 beforeEnter를 추가합니다.

{
  path: '/score',
  name: 'Score',
  component: () => import('../views/Score.vue'),
  beforeEnter: (to, from, next) => {
    const isLogin = store.getters['loginStore/isLogin'];
    if (!isLogin) {
      next('/login?returnUrl=' + to.fullPath);
    } else {
      next();
    }
  }
}

또는 인증 메서드를 생성하고 Score 라우트에 beforeEnter로 인증 메서드를 호출하게 합니다.

const Authentication = () => (to, from, next) => {
  const isLogin = store.getters['loginStore/isLogin'];
    if (!isLogin) {
      next('/login?returnUrl=' + to.fullPath);
    } else {
      next();
    }
};
{
  path: '/score',
  name: 'Score',
  component: () => import('../views/Score.vue'),
  beforeEnter: Authentication()
}

 

2. C:\workspaces\nodeserver\testvue\src\views 폴더에 Login.vue 파일을 오픈하여 <script>의 methods에 doLogin()에 추가합니다.

let memberInfo = { id: this.memberId, password: this.memberPassword };
this.$store.dispatch("loginStore/doLogin", memberInfo).then(() => {
	const returnUrl = window.location.search.replace(/^\?returnUrl=/, "");
	this.$router.push(returnUrl);
}).catch((err) => {
	this.errorMessage = err.response.data.errormessage;
});

"로그인"바튼을 누르면 아이디와 패스워드 정보를 담아 loginStore의 doLogin() 액션 메서드를 호출합니다.

로그인이 성공하면 returnUrl로 이동하고 실패하면 에러 메시지를 설정합니다.

3. 웹 브라우저에서 "http://localhost:8080"를 입력합니다.

 

Score를 클릭하면 로그인 페이지로 이동합니다. URL이 "http://localhost:8080/login?returnUrl=/score"으로 변경됩니다.

 

아이디는 "1", 패스워드는 "1"를 입력하면 아이디와 패스워드가 일치하지 않는다는 메시지가 나타납니다.

{"success":false,"errormessage":"id and password are not identical"}

 

아이디는 "testid1", 패스워드는 "testpwd1"를 입력하면 Score로 이동됩니다.

728x90
반응형