โ ์ง์ฐ๋๋ ํ๋ก์ ํธ์ ์ธ๋ ฅ์ ๋ ํฌ์ ํ๋ฉด ์คํ๋ ค ๋ ๋ฆ์ด์ง๋ค. โ
- Frederick Philips Brooks
Mythical Man-Month ์ ์

์ธ์ ๋์์์
- ํด๋ผ์ด์ธํธ ์์ฒญ (์ฌ์ฉ์๊ฐ ์น์ฌ์ดํธ ์ ๊ทผ)
- ์๋ฒ๋ ์ ๊ทผํด๋ผ์ด์ธํธ์ Request-Headerํ๋์ธ cookie๋ฅผ ํ์ธํ์ฌ, ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ์ธ์ ID๋ฅผ ๋ณด๋๋์ง ํ์ธ
- ์ธ์ ID๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ฉด, ์๋ฒ๋ ์ธ์ ID๋ฅผ ์์ฑํด ํด๋ผ์ด์ธํธ์๊ฒ ์ ์ก.
- ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์ค ์ธ์ ID๋ฅผ ์ฟ ํค๋ฅผ ์ฌ์ฉํด ์๋ฒ์ ์ ์ฅํ๋ค.
- ํด๋ผ์ด์ธํธ๋ ์ฌ์ ์์, ์ด ์ฟ ํค๋ฅผ ์ด์ฉํ์ฌ ์ธ์ ID๊ฐ์ ์๋ฒ์ ์ ๋ฌํ๋ค.

express-session ๋ชจ๋
์ธ์ ๊ด๋ฆฌ์ฉ ๋ฏธ๋ค์จ์ด.
๋ก๊ทธ์ธ ๋ฑ์ ์ด์ ๋ก ์ธ์ ์ ๊ตฌํํ๊ฑฐ๋, ํน์ ์ฌ์ฉ์๋ฅผ ์ํ ๋ฐ์ดํฐ๋ฅผ ์์์ ์ผ๋ก ์ ์ฅํด๋ ๋ ๋งค์ฐ ์ ์ฉํ๋ค.
์ธ์ ์ ์ฌ์ฉ์๋ณ๋ก req.session ๊ฐ์ฒด ์์ ์ ์ง๋๋ค.
app.use(cookieParser(process.env.COOKIE_SECRET);
app.use(session({
secure: ture, // https ํ๊ฒฝ์์๋ง session ์ ๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ก์ฒ๋ฆฌ
secret: process.env.COOKIE_SECRET, // ์ํธํํ๋ ๋ฐ ์ฐ์ผ ํค
resave: false, // ์ธ์
์ ์ธ์ ๋ ์ ์ฅํ ์ง ์ค์ ํจ
saveUninitialized: true, // ์ธ์
์ ์ ์ฅํ ๋ด์ญ์ด ์๋๋ผ๋ ์ฒ์๋ถํฐ ์ธ์
์ ์์ฑํ ์ง ์ค์
cookie: { //์ธ์
์ฟ ํค ์ค์ (์ธ์
๊ด๋ฆฌ ์ ํด๋ผ์ด์ธํธ์ ๋ณด๋ด๋ ์ฟ ํค)
httpOnly: true, // ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ์ธ์
์ฟ ํค๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ํจ
Secure: true
},
name: 'session-cookie' // ์ธ์
์ฟ ํค๋ช
๋ํดํธ๊ฐ์ connect.sid์ด์ง๋ง ๋ค๋ฅธ ์ด๋ฆ์ ์ค์๋ ์๋ค.
}));
app.get('/', (req, res, next) => {
// ์ธ์
์ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ๋ฉด, ๋ชจ๋ ์ธ์
์ด ์ค์ ๋๋๊ฒ์๋๋ผ, ์์ฒญ ๋ฐ์ ๊ณ ์ ์ ์ธ์
์ฌ์ฉ์์ ๊ฐ๋ง ์ค์ ๋๋ค.
// ์ฆ, ๊ฐ์ธ์ ์ ์ฅ ๊ณต๊ฐ์ด ์๊ธด ๊ฒ๊ณผ ๊ฐ๋ค.
req.session.id = "hello";
}
express-session์ ์ธ์ ๊ด๋ฆฌ ์ ํด๋ผ์ด์ธํธ์ ์ธ์ ์ฟ ํค๋ฅผ ๋ณด๋ธ๋ค.
์์ ํ๊ฒ ์ฟ ํค๋ฅผ ์ ์กํ๋ ค๋ฉด ์ฟ ํค์ ์๋ช ์ ์ถ๊ฐํด์ผํ๊ณ , ์ฟ ํค๋ฅผ ์๋ช ํ ๋ secret์ ๊ฐ์ด ํ์ํ๋ค.
cookie-parser์ secret๊ณผ ๊ฐ๊ฒ ์ค์ ํ๋ ๊ฒ์ด ์ข๋ค.
session ์ ๊ทผํ๊ธฐ
์ฑ์์๋ ์ด ์ธ์ ๊ฐ์ฒด์ req.session์ผ๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
๊ฐ๋ น ์ฌ์ฉ์๊ฐ ์ด ํ์ด์ง์ ๋ช ๋ฒ์ด๋ ๋ค์ด์๋์ง ๋ณด์ฌ์ฃผ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฑ์ ๊ตฌ์ฑํ ์๋ ์๋ค.
app.get('/', (req, res, next) => {
if (req.session.num === undefined) // ์ธ์
์ด ์๋ค๋ฉด
req.session.num = 1; // ์ธ์
๋ฑ๋ก
else
req.session.num += 1;
res.send(`${req.session.num}๋ฒ ์ ์`);
});
๊ฐ์ ์ธ์ ์ ์ ์งํ๊ณ ์๋ ํ ๋ฃจํธ path๋ก ์ ์์ ํ ๋๋ง๋ค req.session.num์ด 1์ฉ ๋์ด๋๊ฒ ๋๋ค.
์ด๋ฐ ์์ผ๋ก req.session.is_logined๋ฅผ ํ๋ฉด ๋ก๊ทธ์ธ์ ํ๋์ง ์ ํ๋์ง ๊ตฌํํ ์๋ ์๋ค.
์ธ์ ๊ฐ์ฒด๋ฅผ ์์ ๊ณ ์ถ๋ค๋ฉด(๊ฐ๋ น ๋ก๊ทธ์์์ ํด์ ์ธ์ ์ ์ ์งํ ํ์๊ฐ ์์ด์ง๋ค๋ฉด) req.session.destroy๋ฅผ ํ๋ฉด ๋๋ค.
req.session.destroy(err => {
if (err) throw err;
res.redirect(302, '/'); // ์นํ์ด์ง ๊ฐ์ ์ด๋
});
๋ง์ฝ ์ธ์ ์คํ ์ด๊ฐ ๋ฐ์ ๋ ๋ฆฌ๋ค์ด๋ ์ ์ ํ๋ฉด ๋ฐ๋ก ๊ฒฐ๊ณผ๊ฐ ๋ฐ์๋์ง ์์ ์๋ ์๋ค.
๊ทธ๋ด ๋๋ req.session.save(err => {})๋ฅผ ํ๋ฉด ๋๋ค.
req.session.save๋ฅผ ์คํํ๋ฉด ์ฆ์ session ๋ฐ์ดํฐ ์ ์ฅ์ ํ๊ณ , err๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๊ฐ๋ ์ฝ๋ฐฑ ํจ์๋ save๊ฐ ๋ชจ๋ ๋๋๋ฉด ์์ ํ ๋ด์ฉ๋ค์ ๋ด๊ณ ์๋ค.
if (email === authData.email && pwd === authData.pwd) {
req.session.is_logined = true;
req.session.nickname = authData.nickname;
req.session.save(err => {
if (err) throw err;
res.redirect(302, '/');
});
} else {
res.end("Who?");
}
์ธ์ ์ ์๋ฒ ๋ฉ๋ชจ๋ฆฌ(MemoryStore)์ ์ ์ฅ๋๋ค.
๋ง์ธ์ฆ์จ ์๋ฒ๊ฐ ํ ๋ฒ ๋ด๋ ค๊ฐ๋ฉด ๋ชจ๋ ์ด๊ธฐํ๋ผ์ ์์ด์ง๋ค๋ ๋ป์ด๋ค.
Session Store
์ธ์ ์คํ ์ด๋ ์ธ์ ์ด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ณณ์ ๋งํ๋ค.
๋ํ์ ์ผ๋ก Memory Store, File Store, Mongo Store ๊ฐ ์๋ค.
default๊ฐ์ Memory Store์ด๋ค. ์์์ ๋งํ๋ฏ์ด, ๋ฉ๋ชจ๋ฆฌ๋ ์๋ฒ๋ ํด๋ผ์ด์ธํธ๋ฅผ ๊ป๋ค ํค๋ฉด ์ฌ๋ผ์ง๋ ํ๋ฐ์ฑ์ด๋ค.
๊ทธ๋์ ์ธ์ ์ ์ ์ฅํ ๊ณ ์ ์ ์ฅ์๋ฅผ ๋ฐ๋ก ์ง์ ํ ์ ๊ฐ ์๋๋ฐ, ์ค์ ์๋น์ค ๋ฐฐํฌ ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๊ฒฐํด์ ์ธ์ ์ ์ ์งํ๋ฉด ์ข๋ค. ๋ณดํต Redis๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํ๋ค.
๋ญ ์ฌ์ฉํ ์ง์ ๋ฐ๋ผ์ store๋ฅผ ์ด๋ค ๊ฑธ ์ค์นํด์ผ ๋ ์ง๋ ๋ค๋ฅด๋ค.
๊ฐ๋ น mySQL์ ์ ์ฅํ ๊ฑฐ๋ผ๋ฉด express-mysql-session์ด ์ ๋นํด๋ณด์ด๊ณ , Redis๋ MongoDB์ ์ ์ฅํ ๊ฑฐ๋ผ๋ฉด fortune-session์ด ๊ด์ฐฎ์ ๋ณด์ธ๋ค.
API ๋ฌธ์๋ฅผ ๋ณด๊ณ ๊ณ ๋ฅด๋ฉด ๋๋ค.
๊ฐ์ฅ ํํ๊ฒ ์ฐ์ด๋ ํ์ผ์ ์ด์ฉํด, ์ธ์ ๊ฐ์ file๋ก ์ ์ฅํด์ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ์ ๊ตฌ์ถํด๋ณด์.
์ผ๋จ session file store๋ก ์ค์นํด ๋ณด๊ฒ ๋ค.
> npm install --save session-file-store
const express = require('express');
const app = express();
const session = require('express-session'); //์ธ์
๊ด๋ฆฌ์ฉ ๋ฏธ๋ค์จ์ด
const fileStore = require('session-file-store')(session); // session file store
app.use(session({
secret: 'secret key', // ์ํธํ
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
},
store: new fileStore() // ์ธ์
๊ฐ์ฒด์ ์ธ์
์คํ ์ด๋ฅผ ์ ์ฉ
}));
app.get('/', (req, res, next) => {
console.log(req.session);
if(!req.session.num){ // ํด๋น ์ธ์
ํค๊ฐ ์๋ค๋ฉด
req.session.num = 1; // ์ธ์
์์ฑ
} else {
req.session.num = req.session.num + 1;
}
res.send(`Number : ${req.session.num}`);
});
app.listen(3000, () => {
console.log('listening 3000port');
});
์ด๋ ๊ฒ ํ๊ณ ๋์ ์ฒ์ ์๋ฒ๋ฅผ ์ฌ๋ฆฌ๋ฉด sessions ๋๋ ํ ๋ฆฌ๊ฐ ์๊ธด๋ค.
์ฌ์ฉ์๊ฐ ์๋ฒ์ ์ ์ํ ๋ ์ด sessions ๋๋ ํ ๋ฆฌ ์๋ ์ธ์ ํ์ผ์ด ์๊ธด๋ค.

์ธ์ ์ฟ ํค

์๋ช ์ด ๋ถ์ ์ฟ ํค๋ name=name.sign ํํ๊ฐ ๋๋ค๊ณ ์ ๊ฐ์์์ ์ธ๊ธํ๋ค.
๋ณดํต ์ธ์ ์ฟ ํค ์ด๋ฆ์ connect.sid๋ก ๋์ด์์ผ๋ฉฐ, ์ธ์ ๋ ์ง ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ value๋ ์ธ์ฝ๋ฉ๋ ๊ฐ์ธ๋ฐ ์ ๋ฌธ์์ด์ ๋์ฝ๋ฉ ํ๋ฉด,
decodeURIComponent("s%3AGuJZu03OykNcNf6KvCBog5uLjpdsvMQW.GSubUlKEXU0XniHSLXAyuQfrrxnEhfbVA26noc4E7bo");
// 's:GuJZu03OykNcNf6KvCBog5uLjpdsvMQW.GSubUlKEXU0XniHSLXAyuQfrrxnEhfbVA26noc4E7bo'
์ด๋ฐ์์ผ๋ก ์์์๋ ๋ฌธ์์ด.๋ฌธ์์ด ์์ผ๋ก ๊ตฌ์ฑ๋๊ฑธ ์ ์ ์๋ค.
์ ์์์๋ ๋ฌธ์์ด ์์๋ฆฌ์ ์ธ์ ํ์ผ์ ํ์ผ๋ช ๊ณผ ๊ฐ์๊ฑธ ์์ ์๋ค.
์ธ์ ํ์ผ

๋ธ๋ผ์ฐ์ ์ ์ํ๋ฒณ ๋ฐฐ์ด์ ๋์ํ๋ ์ฟ ํค ๊ฐ์ด ํ์ผ๋ก ์ ์ฅ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋ก๊ณ ์นจ ํ ๋ ๋ง๋ค ์ฟ ํค ๊ฐ์ num์ด 1์ฉ ์ฆ๊ฐํ๋ค.
๋ฒ์ธ
์ธ์ ๋ ์๋ฒฝํด๋ณด์ด๊ธด ํ์ง๋ง, ์ธ์ ์ ์๋ฒ์ ์ ์ฅํ๋ค๊ณ ์น๋ฉด ๋์์ ์์๊ฐ ๋ง์์ง ๋ ์๋ฒ์ ๊ทธ๋งํผ ์ธ์ ์ด ๋ง์ด ์์ด๊ฒ ๋๋ฏ๋ก ๊ณผ๋ถํ๊ฐ ์ฌ ์ ์๋ค๋ ๋จ์ ์ด ์๋ค.
๋ํ ์ธ์ ํ์ด์ฌํน์ด๋ผ๊ณ ์ธ์ ์ ์ธ์ฆํ๊ธฐ ์ํ ์ธ์ id ์์ฒด๋ฅผ ๋นผ์๋ ์ธ์ ๊ฐ๋ก์ฑ๊ธฐ ๊ณต๊ฒฉ๋ ์๋ค.
์๋ฅผ ๋ค๋ฉด ํํ์ด์ง ๊ด๋ฆฌ์์ ์ธ์ ์์ด๋๋ฅผ ํ์ทจํด์ ์ฟ ํค๊ฐ์ ๊ด๋ฆฌ์์ ์ธ์ ์์ด๋๋ก ๋ณ๊ฒฝํ๋ ์ ์ข์ ์ํฉ์ด ์๊ธธ ์๋ ์๋ค.
์ด ๊ฒฝ์ฐ์๋ ์ธ์ id๋ฅผ ๋บ์ผ๋ฉด ID์ ์ํธ๋ฅผ ๋ชฐ๋ผ๋ ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ์ํํ๋ค.
์ด๊ฒ์ ์๋ฐฉํ๊ธฐ ์ํด์๋ ์ธ์ ์ ๋ก๊ทธ์ธํ์ ๋์ IP ๊ฐ์ ์ ์ฅํ๊ณ , ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค ํ์ฌ IP์ ์ธ์ ์ IP/๋ธ๋ผ์ฐ์ ์ ๋ณด๊ฐ ๊ฐ์์ง ๊ฒ์ฌํ๋ ๋ฐฉ๋ฒ์ด ์๋ค๊ณ ํ๋ค.
# Reference
์ด ๊ธ์ด ์ข์ผ์ จ๋ค๋ฉด ๊ตฌ๋ & ์ข์์
์ฌ๋ฌ๋ถ์ ๊ตฌ๋
๊ณผ ์ข์์๋
์ ์์๊ฒ ํฐ ํ์ด ๋ฉ๋๋ค.