사전 작업
환경 변수 설정
.env
파일 생성 후 데이터 베이스 관련 환경변수 설정
데이터베이스 생성 및 마이그레이션
시퀄라이즈 마이그레이션 및 시드를 적용한다.
mysql > DATABASE CREATE authentication
npx sequelize-cli db:migrate // 시퀄라이즈 마이그레이션
// mkcert 설치
sudo apt install libnss3-tools
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64
chmod +x mkcert
sudo cp mkcert /usr/local/bin/
// 인증서 생성
mkcert -install
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1
// 127.0.0.1(IPv4), ::1(IPv6)
서버 구현
index.js
const express = require('express');
const cors = require('cors');
const session = require('express-session');
const logger = require('morgan');
const fs = require('fs');
const https = require('https');
const usersRouter = require('./routes/user');
const app = express();
const FILL_ME_IN = 'FILL_ME_IN';
const PORT = process.env.PORT || 4000;
// Texpress-session 라이브러리를 이용해 쿠키 설정
app.use(
session({
secret: '@codestates',
resave: false,
saveUninitialized: true,
cookie: {
domain: "localhost",
path: "/",
maxAge: 24 * 6 * 60 * 10000,
sameSite: 'None',
httpOnly: true,
secure: true,
},
})
);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// CORS 설정이 필요
// 메서드는 GET, POST, OPTIONS를 허용
app.use(cors({
origin : 'https://localhost:4000',
method : ['GET', 'POST', 'OPTIONS'],
credentials : true // 쿠키 응답 메시지 설정
}));
app.use('/users', usersRouter);
let server;
// 인증서 파일들이 존재하는 경우에만 https 프로토콜을 사용하는 서버를 실행
// 만약 인증서 파일이 존재하지 않는경우, http 프로토콜을 사용하는 서버를 실행
// 파일 존재여부를 확인하는 폴더는 서버 폴더의 package.json이 위치한 곳
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
server = https
.createServer(
{
key: fs.readFileSync(__dirname + `/` + 'key.pem', 'utf-8'),
cert: fs.readFileSync(__dirname + `/` + 'cert.pem', 'utf-8'),
},
app
)
.listen(PORT);
} else {
server = app.listen(PORT)
}
module.exports = server;
controller/login.js (POST /users/login)
index.js
파일은 클라이언트에서 로그인 요청이 들어오면, 요청된 id와 password가 DB와 일치하게 되면 응답을 돌려주게 된다. 데이터를 session이라는 곳에 저장하고 session_id를 클라이언트에 전달하면, 클라이언트에서는 데이터를 요청할 때 마다 id를 확인하고 서버에 데이터를 받게 된다. 또한, 현재 실습에서는 session에 저장한 데이터가 모두 지워지게 되는 일회성 방식인 express-session 미들웨어를 사용한다.
// 해당 모델의 인스턴스를 models/index.js에서 가져온다.
const { userinfo } = require('.');
const { Users } = require('../../models');
module.exports = {
post: async (req, res) => {
// userInfo는 유저정보가 데이터베이스에 존재하고, 완벽히 일치하는 경우에만 데이터가 존재
// 만약 userInfo가 NULL 혹은 빈 객체라면 전달받은 유저정보가 데이터베이스에 존재하는지 확인
const userInfo = await Users.findOne({
where: { userId: req.body.userId, password: req.body.password },
});
// userInfo 결과 존재 여부에 따라 응답
// 결과가 존재하는 경우 세션 객체에 userId가 저장
if (!userInfo) {
res.status(400).send({ data: null, message: 'not authorized'})
} else {
req.session.userId = userInfo.userId;
res.status(200).json({ data: userInfo, message: 'ok' })
}
}
}
controller/logout.js (POST /users/logout)
login과 같은 방식으로 구현한다.
module.exports = {
post: (req, res) => {
// 세션 아이디를 통해 고유한 세션 객체에 접근가능
// 앞서 로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단
// 세션 객체에 담긴 값의 존재 여부에 따라 응답 구현
if (!req.session.userId) {
res.status(400).send({ data: null, message: 'not authorized' })
} else {
// 로그아웃 요청은 세션을 삭제하는 과정을 포함
req.session.destroy();
res.status(200).json({ data: null, message: 'ok'})
}
},
};
controller/userinfo.js (POST /users/userinfo)
req.session에 들어오는 값이 일치하면, DB에서 값을 찾아 클라이언트에게 보내주게 된다.
const { Users } = require('../../models');
module.exports = {
get: async (req, res) => {
// 션 객체에 담긴 값의 존재 여부에 따라 응답을 구현
if (!req.session.userId) {
res.status(400).send({ data: null, message: 'not authorized' })
} else {
// 데이터베이스에서 로그인한 사용자의 정보를 조회한 후 응답
const result = await Users.findOne({
where: { userId: req.session.userId }
});
res.status(200).json({ data: result, message: 'ok' })
}
},
};