๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Node.js

Node.js / ์ฟ ํ‚ค(Cookie)

์ฟ ํ‚ค(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 ์ฐจ์ด ์š”์•ฝ

ํ•ญ๋ชฉ127.0.0.1localhost
์˜๋ฏธ ๋ฃจํ”„๋ฐฑ 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