์ฟ ํค(Cookie)
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ์ ์กํ๋ ์์ ๋ฐ์ดํฐ ์กฐ๊ฐ.
๋ธ๋ผ์ฐ์ ์์๋ ๊ทธ ๋ฐ์ดํฐ ์กฐ๊ฐ๋ค์ ์ ์ฅํ๋ค๊ฐ ๋์ผํ ์๋ฒ์ ์ฌ ์์ฒญ ์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ์์ผ๋ก ์ฌ์ฉ๋๋ค.
์ด๋ฅผ ์ด์ฉํด HTTP์ ๋ฌด์ํ์ฑ์ ๋ณด์ํด์ค๋ค.
์ฟ ํค์ ํ์ฉ ์ฌ๋ก
- ์ธ์ ๊ด๋ฆฌ(Session management) : ์๋ฒ์ ์ ์ฅํด์ผ ํ ๋ก๊ทธ์ธ, ์ฅ๋ฐ๊ตฌ๋, ๊ฒ์ ์ค์ฝ์ด ๋ฑ์ ์ ๋ณด ๊ด๋ฆฌ
- ๊ฐ์ธํ(Personalization) : ์ฌ์ฉ์ ์ ํธ, ํ ๋ง ๋ฑ์ ์ธํ
- ํธ๋ํน(Tracking) : ์ฌ์ฉ์ ํ๋์ ๊ธฐ๋กํ๊ณ ๋ถ์ํ๋ ์ฉ๋
์ค์ต
* express๋ก ์๋ฒ๋ฅผ ๊ตฌ์ถํ๊ณ , axios๋ก ์๋ฒ๋ฅผ ์ฐ๊ฒฐํด ์ฟ ํค๋ฅผ ์ถ๊ฐํ๊ณ ์ญ์ ํ๋ ์ค์ต์ด๋ค.
์์ฑํ ํ์ผ์ index.html, cookie.js, server.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="set_cookie">์ฟ ํค ์ถ๊ฐ</button>
<button id="delete_cookie">์ฟ ํค ์ญ์ </button>
<!-- axios cdn -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="cookie.js"></script>
</body>
</html>
ํฐ๋ฏธ๋
npm init -y #์๋ก์ด package.json ํ์ผ ๋ง๋ค๊ธฐ, ๋ชจ๋ ์ด๊ธฐ ์ธํ
npm i express cors cookie-parser #express, cors, cookie-parser ์ค์น
* cookie-parser : Express์์ ์ฟ ํค๋ฅผ ํ์ฑํ๊ณ ์ฝ์ ์ ์๊ฒ ๋์์ฃผ๋ ๋ฏธ๋ค์จ์ด
๊ธฐ๋ณธ ์ค์
server.js
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
// ์๋ฒ ์์ฑ
const app = express();
app.use(
cors({
origin: ['http://127.0.0.1:5500', 'http://localhost:5500'],
methods: ['GET', 'DELETE', 'OPTIONS'],
credentials: true, // ์ธ์ฆ์ ๋ณด. true๋ก ํด์ผ ํด๋ผ์ด์ธํธ์ ์ฟ ํค ์ ์ฅ์ด ๊ฐ๋ฅํ๋ค!
}),
);
// ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฐ์์จ ์ฟ ํค๋ฅผ ์ฝ๊ฒ ํ์
ํ ์ ์๊ฒ ํด์ฃผ๋ ๋ชจ๋ ์ฌ์ฉ
app.use(cookieParser());
// ๋ผ์ฐํ
app.get('/', (req, res) => {
res.cookie('test-cookie', 'my cookie');
res.send('์ฟ ํค ์์ฑ ์๋ฃ');
});
app.listen(3000, () => console.log('์๋ฒ ์คํ'));
cookie.js
const setCookieBtn = document.getElementById('set_cookie');
const deleteCookieBtn = document.getElementById('delete_cookie');
// ํด๋ผ์ด์ธํธ์์ ์ฟ ํค๋ฅผ ์ฃผ๊ณ ๋ฐ๊ธฐ ์ํด ํ์ํ ์ต์
axios.defaults.withCredentials = true;
* live server๋ก ์ด ๋ 127.0.0.1์ localhost๋ก ๋ฐ๊ฟ์ผ ํ๋ ์ด์
๋๋ต : ์ฑ์งํผํฐ...
127.0.0.1์ด๋ localhost๋ ๊ฒฐ๊ณผ์ ์ผ๋ก **๊ฐ์ IP ์ฃผ์(๋ฃจํ๋ฐฑ ์ฃผ์)**๋ฅผ ๊ฐ๋ฆฌํค์ง๋ง, ์ค์ ๋ก ๊ฐ๋ฐํ ๋๋ ๋ ๊ฐ์ง๊ฐ ๋ฏธ๋ฌํ๊ฒ ๋ค๋ฅด๊ฒ ๋์ํ ์ ์์ด. ํนํ ์ฟ ํค, CORS, SameSite ์ ์ฑ ๊ด๋ จํด์ ์ฐจ์ด๊ฐ ์๊ฒจ!
โ 127.0.0.1 vs localhost ์ฐจ์ด ์์ฝ
์๋ฏธ | ๋ฃจํ๋ฐฑ IP ์ฃผ์ | ๋ฃจํ๋ฐฑ ๋๋ฉ์ธ ์ด๋ฆ |
๋ธ๋ผ์ฐ์ ์ธ์ | IP ์ฃผ์ | ๋๋ฉ์ธ |
์ฟ ํค ๋๋ฉ์ธ ์ฒ๋ฆฌ | 127.0.0.1๋ก ์ ์ฅ | localhost๋ก ์ ์ฅ |
CORS origin ์ฒ๋ฆฌ | http://127.0.0.1:5500 | http://localhost:5500 |
์ฟ ํค์ CORS ์ถฉ๋ ์ํ | ๋ ๋ง์ โ ๏ธ | ๋ ์์ ์ โ |
๐ ์ localhost๊ฐ ๋ ๋์๊น?
1. ๋๋ฉ์ธ ์ผ์น ๋ฌธ์ ๋ก ์ฟ ํค ๊ณต์ ๊ฐ ๊นจ์ง ์ ์์
- localhost์์ ๋ง๋ ์ฟ ํค๋ 127.0.0.1์์ ์ ๋ณด์ด๊ณ ๊ทธ ๋ฐ๋๋ ๋ง์ฐฌ๊ฐ์ง์ผ.
- ์๋ฒ์์ localhost๋ก ๋ง๋ ์ฟ ํค๋ 127.0.0.1์์๋ ์ ์ก์ด ์ ๋ผ.
- ๊ทธ๋์ ํ ์คํธํ ๋ ๋ธ๋ผ์ฐ์ ์์ ์ ์ํ๋ ์ฃผ์์ ์๋ฒ์ origin์ด ์๋ฒฝํ ์ผ์นํด์ผ ํด.
์: ์ฟ ํค Domain์ด localhost์ด๋ฉด → 127.0.0.1์์๋ ์ฟ ํค๊ฐ ์ ๋ถ์.
2. SameSite ์ ์ฑ ์ ์๊ฒฉํจ
- SameSite=Lax ๋๋ Strict์ผ ๋,
๋ธ๋ผ์ฐ์ ๋ origin์ด ๋ค๋ฅด๋ฉด ์ฟ ํค๋ฅผ ๋ณด๋ด์ง ์์. - localhost vs 127.0.0.1์ ๋ธ๋ผ์ฐ์ ์ ์ฅ์์ ๋ค๋ฅธ origin์ด์ผ.
- → ์ฟ ํค ์ ๋ถ๊ณ CORS ์ค๋ฅ๋๊ฑฐ๋ clearCookie()๊ฐ ๋์ ์ ํ ์ ์์ด.
3. ์๋ฒ์์ CORS origin ์ค์ ์ ๋ถ์ผ์น
cors({
origin: ['http://localhost:5500'],
credentials: true
});
→ ๊ทธ๋ฐ๋ฐ ํด๋ผ์ด์ธํธ๊ฐ 127.0.0.1:5500์ผ๋ก ์ ์ํ๋ฉด? โ CORS ๋งํ
โ ๊ฒฐ๋ก
๊ฐ๋ฐ ์ค์๋ ํญ์ localhost๋ก ํต์ผํด์ ์ ์ํ๋ ๊ฒ ์์ ํ๊ณ ํธํ์ฑ ๋ฌธ์ ๋ฅผ ์ค์ผ ์ ์์ด!
๐ก ํ: ์ค์ ๋ฐฐํฌ ํ๊ฒฝ์์ ?
- ๋ณดํต ๋๋ฉ์ธ ์ด๋ฆ์ ์ฐ๋๊น localhost ๋ฐฉ์์ด ์ค์ ์๋น์ค์ ๋ ๊ฐ๊น์.
- 127.0.0.1์ ์ฃผ๋ก ๋คํธ์ํฌ ๋๋ฒ๊น ์ด๋ ๋ด๋ถ ์๋ฒ ํ ์คํธ์ ์ฌ์ฉ๋ผ.
์ฟ ํค ์ถ๊ฐ
cookie.js
setCookieBtn.onclick = () => {
axios.get('http://localhost:3000').then((res) => console.log(res));
};
๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฟ ํค๋ฅผ ์์ฑํ๊ณ ์ฝ์์ ์ถ๋ ฅํ๋๋ก ํ๋ค.
์์ฑ๋ ์ฟ ํค๋ ๋คํธ์ํฌ๋ ์ ํ๋ฆฌ์ผ์ด์ ํญ์์ ํ์ธํด๋ณผ ์ ์๋ค
์ฟ ํค ์ต์
- Expires / Max-Age : ์ฟ ํค๊ฐ ์ธ์ ๋ง๋ฃ๋๋์ง, ์ผ๋ง๋ ์ง์๋๋์ง๋ฅผ ๋ํ๋ด๋ ๋ง๋ฃ ๋ ์ง/์๊ฐ.
ํ์ฌ ๊ฐ ์ธ์ ์ ์๋ฌด ์ค์ ๋ ํ์ง ์์ ์๋์ผ๋ก ๋ค์ด์จ ๊ฐ์ธ๋ฐ, ์ด๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ซ์ผ๋ฉด ์ฟ ํค๊ฐ ๋ฐ๋ก ๋ ์๊ฐ๊ฒ ๋๋ค. - HttpOnly : ์ฟ ํค๋ฅผ JavaScript (document.cookie)๋ก ์ ๊ทผํ ์ ์๊ฒ ๋ง๋ ๋ณด์ ์ต์
, ํด๋ผ์ด์ธํธ์์ ์ง์ ์ฝ๊ฑฐ๋ ์์ ํ ์ ์์ผ๋ฉฐ ์๋ฒ์์๋ง ์ ๊ทผ ๊ฐ๋ฅ
ํ์ฌ๋ ์๋ฌด ์ค์ ๋ ํ์ง ์์์ ๋ธ๋ผ์ฐ์ ์ ์ฝ์์ฐฝ์์ document.cookie๋ฅผ ์ ๋ ฅํ๋ฉด ๋ฐ๋ก ๋ด ์ฟ ํค๋ค์ด ํคํ ๋ค์ฌ๋ค๋ณด์ด๊ฒ ๋๋ค.... => ๋๋ฌธ์ true๋ก ๊ผญ ์ค์ ํด์ค์ผํจ! - Secure : HTTPS ์ฐ๊ฒฐ์์๋ง ์ ์ก๋๋๋ก ํ๋ ์ต์
์ง๊ธ์ localhost๋ผ์ ๊ฐ๋ฐํ ๋ ๋ง์ด ํ์ธํ๊ธฐ ๋๋ฌธ์ ํฌ๋กฌ ๋ด๋ถ์์๋ http๋ผ๋ ์ฟ ํค๊ฐ ์ ์ก๋๋ ๊ฒ์ ํ์ธํ ์ ์์ง๋ง ๊ทธ ์ด์ธ๋ ์๋๋ค๊ณ ํ๋ค.
server.js (์ต์ ์ถ๊ฐ)
// ๋ผ์ฐํ
app.get('/', (req, res) => {
res.cookie('test-cookie', 'my cookie', {
maxAge: 100000, // 100์ด ๋ค์ ๋ง๋ฃ๋จ
httpOnly: true,
secure: true,
});
res.send('์ฟ ํค ์์ฑ ์๋ฃ');
});
์ฟ ํค ์ญ์
cookie.js
deleteCookieBtn.onclick = () => {
axios.delete('http://localhost:3000').then((res) => console.log(res));
};
server.js
app.delete('/', (req, res) => {
res.clearCookie('test-cookie', {
maxAge: 100000, // ์ ๋์ ์ธ ์ต์
์ด๋ผ maxAge๋ ํ์๋ก ์ ์ด์ฃผ์ง ์์๋ ๋๋ค
httpOnly: true,
secure: true,
});
});
localhost์์ ํ ์คํธ ์์๋ ์ต์ ์ ์ ์ด์ฃผ์ง ์์๋ ์ฟ ํค ์ญ์ ๊ฐ ์ ๋์ํ๋ค!
'Node.js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Node.js / ํ ํฐ(Token) (0) | 2025.05.15 |
---|---|
Node.js / ์ธ์ (Session) (0) | 2025.05.14 |
Node.js / ๋คํธ์ํฌ ๊ธฐ์ด (0) | 2025.05.12 |