개발환경: Windows10, VS Code
지난 포스팅에 이어서 진행한다.
Backend 작업
1. backend 프로젝트를 열고, 터미널에서 express-session과 passport, passport-local을 설치한다.
> npm i express-session passport passport-local
- passport는 express-session을 사용하므로 express-session도 설치해야한다.
- 유저의 아이디와 비밀번호를 통한 로그인을 구현할 것이므로 passport-local을 설치한다.
(그외에 strategy들을 추가 설치하면 구글, 페이스북, 네이버, 카카오 등을 이용한 로그인도 구현할 수 있다.
자세한건 www.passportjs.org 를 통해 확인한다.)
2. 여러가지 설정값에 대한 관리를 위해 dotenv 패키지도 설치한다.
> npm i dotenv
3. 프로젝트 폴더에 '.env' 파일을 생성하고, 쿠키 비밀키를 입력한다.
COOKIE_SECRET = (아무거나 입력)
4. app.js 파일을 열고 dotenv를 적용한다.
require('dotenv').config()
- dotenv의 config()가 호출되면 '.env' 파일의 설정값들이 process.env에 저장된다.
이후 process.env.COOKIE_SECRET 처럼 설정값들을 사용할 수 있다.
5. app.js에 express-session을 적용하면서 쿠키 비밀키를 설정해준다.
(cookieParser와 express-session은 동일한 쿠키 비밀키를 사용해야 한다.)
var session = require('express-session');
....
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
}
}));
6. passport 폴더를 만들고 index.js 파일을 만들어서 passport 설정을 위한 코드를 작성한다.
const LocalStrategy = require('passport-local').Strategy;
const users = require('../data/users.json');
exports.config = (passport) => {
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
const result = users.filter((user) => user.id === id);
if (result.length > 0) {
done(null, result[0]);
}
});
passport.use(new LocalStrategy({
usernameField: 'id',
passwordField: 'password',
}, (id, password, done) => {
const result = users.filter((user) => user.id === id);
if (result.length > 0) {
const user = result[0];
if (password === user.password) {
done(null, user);
} else {
done(null, false, { message: "비밀번호 틀림" });
}
} else {
done(null, false, { message: "가입되지 않은 회원"});
}
}));
};
- 'data/user.json' : 아직 DB를 사용하지 않으므로 이전 시간에 만들어둔 json 파일을 임시 사용한다.
- passport.serializeUser() : 로그인을 하면 user 정보를 세션에 저장하기위해 호출된다.
user의 모든 정보를 저장할 필요는 없으므로, id만 세션에 저장하도록 설정한다.
- passport.deserializeUser() : 매 요청시 호출되면서 세션에 저장된 정보(위 코드에서는 id)를 불러온다.
그 id에 해당하는 user를 찾아서 done()에 넣어주면 req.user에 유저 정보가 저장된다.
- usernameField, passwordField: 로그인시 설정된 이름으로 req.body에서 값을 읽어온다.
(위 코드에서는 req.body.id를 username으로, req.body.password를 password로 인식하게 된다.)
7. app.js에 passport를 적용한다.
var passport = require('passport');
require('./passport').config(passport); // passport 설정
....
app.use(passport.initialize()); // req에 passport의 설정값들 적용
app.use(passport.session()); // session 정보 저장 (req.session, req.user)
8. routes/login.js 파일을 열어서, 로그인 요청시의 처리를 작성한다.
const express = require('express');
const passport = require('passport');
const router = express.Router();
router.get('/', function(req, res, next) {
if (req.isAuthenticated() && req.user) {
return res.json({ user: req.user });
}
return res.json({ user: null });
});
router.post('/', function(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/');
}
passport.authenticate('local', (authError, user, info) => {
if (authError) {
console.error(authError);
return next(authError);
}
if (!user) {
return res.json(info);
}
return req.login(user, (loginError) => {
if (loginError) {
console.error(loginError);
return next(loginError);
}
return res.json({ user });
});
})(req, res, next); // 미들웨어 호출
});
module.exports = router;
- get 요청시, 로그인중이라면 (passport의 deserializeUser()에서 저장한) req.user의 유저 정보를 보낸다.
- post 요청시, local 전략으로 로그인을 시도한다. 이때 사용되는 username과 password는 LocalStrategy에서 설정한대로 req.body.id와 req.body.password가 된다. 로그인 성공시 user 정보를 보내주고, 로그인 실패시 그 이유를 알기위해 info 값을 보내준다.
- passport.authenticate()가 반환하는 값은 미들웨어이므로 반드시 뒤에 (req, res, next)를 붙여서 호출해주어야 한다.
Frontend 작업
1. frontend 프로젝트를 열고, 터미널에서 vuex 모듈을 설치한다.
> npm i vuex
2. src/store.js 파일을 만들고, vuex로 유저 정보를 저장할 저장소를 생성한다.
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
user: null
},
getters: {
user: (state) => { return state.user; }
},
mutations: {
setUser(state, user) { state.user = user; }
},
});
3. src/main.js 파일을 열고, vue 인스턴스에 생성한 store를 추가한다.
import { store } from './store';
....
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
4. components/IndexPage.vue 파일을 열고, 코드를 작성한다.
<template>
<div v-if="user">
<h1>접속한 유저</h1>
<p>아이디 : {{ user.id }}</p>
<p>비밀번호 : {{ user.password }}</p>
<p>이름 : {{ user.name }}</p>
</div>
</template>
<script>
export default {
created() {
this.$http.get("/api/login")
.then((res) => {
const user = res.data.user;
if (user) {
this.$store.commit("setUser", user);
} else {
this.$router.push( { name: "LoginPage" });
}
})
.catch((err) => {
console.error(err);
});
},
computed: {
user() { return this.$store.getters.user; }
}}
</script>
<style></style>
- created(): IndexPage 실행시 로그인을 체크한다. 만약 로그인되어 있다면 유저 정보를 store에 저장하고, 로그인되어 있지 않다면 LoginPage로 이동한다.
5. components/LoginPage.vue 파일을 만들고, 코드를 작성한다.
<template>
<div>
<form @submit.prevent="onSubmit">
<p>아이디</p>
<input type="text" name="id" v-model="id">
<p>비밀번호</p>
<input type="password" name="password" v-model="password">
<p></p>
<button>로그인</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
id: '',
password: '',
}
},
methods: {
onSubmit() {
const id = this.id;
const password = this.password;
this.$http.post("api/login", { id, password, }, { "Content-Type": "application-json" })
.then((res) => {
if (res.data.user) {
this.$store.commit("setUser", res.data.user);
this.$router.push( { name: "IndexPage" });
} else if (res.data.message) {
alert(res.data.message);
}
})
.catch((err) => {
console.error(err);
});
}
}
}
</script>
<style></style>
- onSubmit(): 로그인 버튼 클릭시 호출되며, 입력한 아이디와 비밀번호를 백엔드에 전송한다. 백엔드의 Passport-local 에서는 req.body로부터 username과 password를 읽어오므로, 전송시 { "Content-Type": "application-json" } 옵션값을 주어야 한다. (req.params는 인식하지 않는다.)
6. router/index.js 파일을 열고 라우터를 설정한다.
import Vue from 'vue'
import Router from 'vue-router'
import IndexPage from '@/components/IndexPage'
import LoginPage from '@/components/LoginPage'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'IndexPage',
component: IndexPage
},
{
path: '/Login',
name: 'LoginPage',
component: LoginPage
}
]
})
마무리
1. frontend 프로젝트에서 빌드한다.
frontend> npm run build
2. backend 프로젝트를 열고 서버를 실행한다.
backend> npm start
3. localhost:3000에 접속하여 확인한다.
로그인 구현 완료!