.env.example파일을 .env파일로 바꿔주고 안의 비밀번호를 초기에 설정한 값으로 세팅을 해준다.
.env파일에 적혀있는 이름대로 데이터베이스를 생성해준다.
mkcert로 받은 인증서를 server폴더에 복사한다.
우선 CORS 및 세션 옵션을 설정해준다.
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;
// TODO: express-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 }));
// TODO: CORS 설정이 필요합니다. 클라이언트가 어떤 origin인지에 따라 달리 설정할 수 있습니다.
// 메서드는 GET, POST, OPTIONS를 허용합니다.
app.use(cors());
/**
* /users 요청에 대해서 라우터를 이용하기 때문에,
* 반드시 아래와 같은 주소와 메서드로 요청을 보내야 합니다.
*
* POST https://localhost:4000/users/login,
* POST https://localhost:4000/users/logout,
* GET https://localhost:4000/users/userinfo
*/
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;
일치하는 유저가 있을경우 세션에 정보를 저장하고, 그렇지 않은경우 요청을 거절한다고 하였으니, db로 부터 받은 userInfo가 일치하는지 일치하지 않는지 조건문을 통해 작성하면 된다. 또한 세션에 저장시 req.session.save메소드를 통하여 저장할 수 있다.
login.js
// 해당 모델의 인스턴스를 models/index.js에서 가져옵니다.
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 },
});
// TODO: userInfo 결과 존재 여부에 따라 응답을 구현하세요.
// 결과가 존재하는 경우 세션 객체에 password를 제외한 유저 정보가 저장되어야 합니다.
if (!userInfo) {
res.status(404).send({message: 'not authorized'})
// your code here
} else {
req.session.save(function(){
req.session.userId = userInfo.userId;
res.json({data: userInfo, message: 'ok'})
})
// your code here
// HINT: req.session을 사용하세요.
}
},
};
세션에 저장한 값이 존재하면 세션을 삭제하는 코드를 작성하면 된다.
logout.js
module.exports = {
post: (req, res) => {
// TODO: 세션 아이디를 통해 고유한 세션 객체에 접근할 수 있습니다.
// 앞서 로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단할 수 있습니다.
// 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.
if (!req.session.userId) {
res.status(400).send();
// your code here
} else {
res.status(200).send();
req.session.destroy();
// your code
// TODO: 로그아웃 요청은 세션을 삭제하는 과정을 포함해야 합니다.
}
},
};
세션에 저장한 값이 존재하면 조회 후 요청에 응답으로 전달하고, 그렇지 않으면 요청을 거절한다.
userinfo.js
const { Users } = require("../../models");
module.exports = {
get: async (req, res) => {
// TODO: 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.
// HINT: 세션 객체에 담긴 정보가 궁금하다면 req.session을 콘솔로 출력해보세요
if (!req.session.userId) {
res.status(400).send({message: 'not authorized'})
// your code here
} else {
const result = await Users.findOne({
where: { userId: req.session.userId },
});
res.status(200).json({data: result, message: 'ok'})
// your code here
// TODO: 세션 객체에 저장되어 있는 사용자 정보를 반환합니다.
}
},
};
클라이언트를 구현하기에 앞서 axios라는 생소한 api를 접하게 되었다. axios는 promis기반의 http클라이언트로써 요청한 get, post를 전송하고 핸들링 하는 api이다.
공식문서 axios
axios를 통하여 Login.js와 MyPage.js를 구현해보자
공식문서를 참고하면 복잡하지 않게 작성해 볼 수 있다.
Login.js
import React, { Component } from 'react';
import axios from 'axios';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
};
this.inputHandler = this.inputHandler.bind(this);
this.loginRequestHandler = this.loginRequestHandler.bind(this);
}
inputHandler(e) {
this.setState({ [e.target.name]: e.target.value });
}
loginRequestHandler() {
// TODO: 로그인 요청을 보내세요.
//
// 로그인에 성공하면
// - props로 전달받은 함수를 호출해, 로그인 상태를 변경하세요.
// - GET /users/userinfo 를 통해 사용자 정보를 요청하세요
//
// 사용자 정보를 받아온 후
// - props로 전달받은 함수를 호출해, 사용자 정보를 변경하세요.
axios({
url: 'https://localhost:4000/users/login',
method: 'post',
data: {
userId: `${this.state.username}`,
password: `${this.state.password}`,
},
}).then((res) => {
this.props.loginHandler();
axios.get('https://localhost:4000/users/userinfo',{
withCredentials: true,
})
this.props.setUserInfo(res.data.data);
});
}
render() {
return (
<div className='loginContainer'>
<div className='inputField'>
<div>Username</div>
<input
name='username'
onChange={(e) => this.inputHandler(e)}
value={this.state.username}
type='text'
/>
</div>
<div className='inputField'>
<div>Password</div>
<input
name='password'
onChange={(e) => this.inputHandler(e)}
value={this.state.password}
type='password'
/>
</div>
<div className='passwordField'>
<button onClick={this.loginRequestHandler} className='loginBtn'>
Login
</button>
</div>
</div>
);
}
}
export default Login;
MyPage.js
import React from 'react';
import axios from 'axios';
function Mypage(props) {
const handleLogout = () => {
// TODO: 서버에 로그아웃 요청을 보낸다음 요청이 성공하면 props.logoutHandler를 호출하여 로그인 상태를 업데이트 해야 합니다.
axios
.post('https://localhost:4000/users/logout', {
withCredentials: true,
})
.then(() => {
props.logoutHandler();
})
};
return props.userData === null ? (
<div>Loading...</div>
) : (
<div>
<div className='mypageContainer'>
<div>
<span className='title'>Mypage</span>
<button className='logoutBtn' onClick={handleLogout}>
logout
</button>
</div>
<hr />
<div>
안녕하세요. <span className='name'>{props.userData.userId}</span>님! 로그인이 완료되었습니다.
</div>
<br />
<div className='item'>
나의 유저 네임: {props.userData.userId}
</div>
<div className='item'>
나의 이메일 주소: {props.userData.email}
</div>
</div>
</div>
);
}
export default Mypage;