Zum Hauptinhalt springen

🍪 쿠키와 세션

📖 정의

**쿠키(Cookie)**는 서버가 클라이언트의 브라우저에 저장하는 작은 데이터 조각입니다. **세션(Session)**은 서버가 클라이언트의 상태를 유지하기 위해 서버 측에 저장하는 정보입니다. 둘 다 HTTP의 무상태성(Stateless)을 극복하기 위해 사용됩니다.

🎯 비유로 이해하기

호텔 시스템

쿠키 = 룸 카드
├─ 투숙객이 직접 소지
├─ 카드에 방 번호 기록
├─ 분실하면 재발급
└─ 다른 사람이 훔칠 수 있음

세션 = 호텔 장부
├─ 호텔이 보관
├─ 투숙객 정보 상세 기록
├─ 룸 카드로 장부 조회
└─ 안전하게 관리

과정:
1. 체크인 (로그인)
├─ 호텔: 장부에 투숙객 정보 기록 (세션)
└─ 투숙객: 룸 카드 받음 (쿠키)

2. 서비스 이용
├─ 투숙객: 룸 카드 제시
├─ 호텔: 장부에서 정보 확인
└─ 서비스 제공

3. 체크아웃 (로그아웃)
├─ 룸 카드 반납
└─ 장부 정리

쿠키의 동작 원리

1. 서버 → 클라이언트 (쿠키 설정)
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123
Set-Cookie: userId=456

2. 클라이언트 → 서버 (쿠키 전송)
GET /api/profile HTTP/1.1
Cookie: sessionId=abc123; userId=456

3. 브라우저가 자동으로 저장 및 전송
├─ 같은 도메인에 요청 시 자동 전송
├─ 만료 전까지 저장
└─ 사용자가 직접 관리 불필요

쿠키 설정하기

HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
Set-Cookie: theme=dark; Path=/; Max-Age=31536000
Set-Cookie: lang=ko; Domain=.example.com
// 서버에서 쿠키 설정 (Node.js/Express)
app.post('/login', (req, res) => {
// 로그인 처리...

// 세션 쿠키
res.cookie('sessionId', 'abc123', {
httpOnly: true, // JavaScript 접근 차단 (XSS 방어)
secure: true, // HTTPS에서만 전송
sameSite: 'strict', // CSRF 방어
maxAge: 3600000 // 1시간 (밀리초)
});

// 테마 쿠키
res.cookie('theme', 'dark', {
maxAge: 365 * 24 * 60 * 60 * 1000 // 1년
});

res.json({ message: '로그인 성공' });
});
// 클라이언트에서 쿠키 읽기/쓰기 (JavaScript)
// ⚠️ HttpOnly 쿠키는 읽을 수 없음

// 쿠키 설정
document.cookie = "theme=dark; max-age=3600; path=/";
document.cookie = "lang=ko; max-age=31536000; path=/";

// 쿠키 읽기
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift();
}
}

const theme = getCookie('theme');
console.log(theme); // "dark"

// 쿠키 삭제
document.cookie = "theme=; max-age=0; path=/";

쿠키 속성

Name=Value
├─ 쿠키 이름과 값
└─ 예: sessionId=abc123

Expires / Max-Age
├─ Expires: 만료 날짜 (절대 시간)
├─ Max-Age: 만료까지 시간 (초 단위, 상대 시간)
├─ 둘 다 없으면 세션 쿠키 (브라우저 종료 시 삭제)
└─ 예: Max-Age=3600

Domain
├─ 쿠키가 유효한 도메인
├─ .example.com → 모든 하위 도메인 포함
├─ example.com → example.com만
└─ 기본값: 현재 도메인

Path
├─ 쿠키가 유효한 경로
├─ /admin → /admin 하위 경로만
├─ / → 모든 경로
└─ 기본값: /

HttpOnly
├─ JavaScript로 접근 불가
├─ XSS 공격 방어
└─ document.cookie로 읽을 수 없음

Secure
├─ HTTPS에서만 전송
├─ 중간자 공격 방어
└─ HTTP에서는 쿠키 전송 안 됨

SameSite
├─ Strict: 같은 사이트에서만
├─ Lax: 일부 크로스 사이트 허용 (링크 클릭 등)
├─ None: 모든 크로스 사이트 허용 (Secure 필수)
└─ CSRF 공격 방어

쿠키 사용 예시

// 로그인
app.post('/login', async (req, res) => {
const { username, password } = req.body;

// 인증 처리
const user = await authenticate(username, password);

if (user) {
// 세션 ID 생성
const sessionId = generateSessionId();

// 서버에 세션 저장
sessions[sessionId] = {
userId: user.id,
createdAt: Date.now()
};

// 쿠키 설정
res.cookie('sessionId', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000 // 24시간
});

res.json({ message: '로그인 성공' });
} else {
res.status(401).json({ message: '인증 실패' });
}
});

// 인증 미들웨어
function authenticate(req, res, next) {
const sessionId = req.cookies.sessionId;

if (!sessionId || !sessions[sessionId]) {
return res.status(401).json({ message: '인증 필요' });
}

req.user = sessions[sessionId];
next();
}

// 보호된 라우트
app.get('/profile', authenticate, (req, res) => {
res.json({
userId: req.user.userId,
message: '프로필 조회 성공'
});
});

// 로그아웃
app.post('/logout', (req, res) => {
const sessionId = req.cookies.sessionId;

// 세션 삭제
delete sessions[sessionId];

// 쿠키 삭제
res.clearCookie('sessionId');

res.json({ message: '로그아웃 성공' });
});

🗄️ 세션 (Session)

세션의 동작 원리

1. 클라이언트 로그인
├─ username, password 전송
└─ 서버가 인증

2. 서버가 세션 생성
├─ 세션 ID 생성 (abc123)
├─ 서버 메모리/DB에 저장
└─ {
sessionId: 'abc123',
userId: 456,
createdAt: '2025-01-26T10:00:00Z'
}

3. 서버가 세션 ID를 쿠키로 전송
└─ Set-Cookie: sessionId=abc123

4. 클라이언트가 세션 ID 저장
└─ 브라우저 쿠키에 저장

5. 이후 요청마다 세션 ID 전송
├─ Cookie: sessionId=abc123
└─ 서버가 세션 조회하여 사용자 식별

6. 로그아웃 시 세션 삭제
├─ 서버에서 세션 데이터 삭제
└─ 쿠키 삭제

세션 저장소

1. 메모리 (Memory)
├─ 가장 빠름
├─ 서버 재시작 시 소실
├─ 다중 서버 환경에서 공유 불가
└─ 개발 환경에 적합

2. Redis
├─ 빠른 성능
├─ 영속성 옵션 제공
├─ 다중 서버 간 공유 가능
└─ 프로덕션 환경에 적합

3. 데이터베이스 (MySQL, PostgreSQL)
├─ 영속성 보장
├─ 상대적으로 느림
├─ 다중 서버 간 공유 가능
└─ 장기 세션에 적합

4. 파일 시스템
├─ 간단한 구현
├─ 느린 성능
├─ 다중 서버 환경 복잡
└─ 소규모 애플리케이션

Redis를 사용한 세션 관리

const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');

const app = express();

// Redis 클라이언트 생성
const redisClient = createClient({
host: 'localhost',
port: 6379
});
redisClient.connect();

// 세션 미들웨어
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 24시간
}
}));

// 로그인
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await authenticate(username, password);

if (user) {
// 세션에 사용자 정보 저장
req.session.userId = user.id;
req.session.username = user.username;

res.json({ message: '로그인 성공' });
} else {
res.status(401).json({ message: '인증 실패' });
}
});

// 프로필 조회
app.get('/profile', (req, res) => {
if (!req.session.userId) {
return res.status(401).json({ message: '인증 필요' });
}

res.json({
userId: req.session.userId,
username: req.session.username
});
});

// 로그아웃
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ message: '로그아웃 실패' });
}
res.clearCookie('connect.sid'); // 세션 쿠키 삭제
res.json({ message: '로그아웃 성공' });
});
});

🔐 JWT vs 세션

JWT (JSON Web Token)

JWT 구조:
Header.Payload.Signature

예시:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOjQ1NiwidXNlcm5hbWUiOiJob25nIn0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

특징:
├─ 자체 포함 (Self-contained)
├─ 서버 저장소 불필요
├─ 무상태 (Stateless)
└─ 확장성 좋음

장점:
├─ 서버 메모리 절약
├─ 다중 서버 환경 적합
├─ 마이크로서비스 적합
└─ 모바일 앱 적합

단점:
├─ 토큰 크기가 큼
├─ 토큰 무효화 어려움
├─ 만료 전 강제 로그아웃 불가
└─ 민감 정보 암호화 필요
const jwt = require('jsonwebtoken');

// JWT 생성
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await authenticate(username, password);

if (user) {
// 토큰 생성
const token = jwt.sign(
{
userId: user.id,
username: user.username
},
'your-secret-key',
{ expiresIn: '24h' }
);

res.json({ token });
} else {
res.status(401).json({ message: '인증 실패' });
}
});

// JWT 검증 미들웨어
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

if (!token) {
return res.status(401).json({ message: '토큰 필요' });
}

jwt.verify(token, 'your-secret-key', (err, user) => {
if (err) {
return res.status(403).json({ message: '토큰 무효' });
}
req.user = user;
next();
});
}

// 보호된 라우트
app.get('/profile', authenticateToken, (req, res) => {
res.json({
userId: req.user.userId,
username: req.user.username
});
});

세션 vs JWT 비교

┌──────────────┬─────────────────┬─────────────────┐
│ 항목 │ 세션 │ JWT │
├──────────────┼─────────────────┼─────────────────┤
│ 저장 위치 │ 서버 │ 클라이언트 │
│ 확장성 │ 낮음 (공유 필요)│ 높음 (무상태) │
│ 메모리 사용 │ 많음 │ 적음 │
│ 토큰 크기 │ 작음 (ID만) │ 큼 (데이터 포함)│
│ 보안 │ 높음 │ 중간 │
│ 무효화 │ 쉬움 │ 어려움 │
│ 만료 제어 │ 유연함 │ 토큰 재발급 │
│ 적합한 환경 │ 웹 애플리케이션 │ API, 모바일 │
└──────────────┴─────────────────┴─────────────────┘

언제 사용?

세션:
├─ 전통적인 웹 애플리케이션
├─ 강력한 보안 요구
├─ 실시간 세션 무효화 필요
└─ 단일 서버 또는 세션 공유 가능

JWT:
├─ RESTful API
├─ 마이크로서비스 아키텍처
├─ 모바일 애플리케이션
├─ 확장성 중요
└─ 무상태 필요

🛡️ 보안 고려사항

XSS (Cross-Site Scripting) 방어

// ❌ 위험: JavaScript로 접근 가능한 쿠키
document.cookie = "sessionId=abc123";
// 공격자가 <script>alert(document.cookie)</script>로 탈취 가능

// ✅ 안전: HttpOnly 쿠키
res.cookie('sessionId', 'abc123', {
httpOnly: true // JavaScript 접근 차단
});

// ✅ 안전: JWT는 localStorage 대신 httpOnly 쿠키에 저장
res.cookie('token', jwtToken, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});

CSRF (Cross-Site Request Forgery) 방어

// ❌ 위험: SameSite 없이 쿠키 전송
res.cookie('sessionId', 'abc123');
// 다른 사이트에서 요청 시에도 쿠키 전송됨

// ✅ 안전: SameSite 설정
res.cookie('sessionId', 'abc123', {
sameSite: 'strict' // 같은 사이트에서만 쿠키 전송
});

// ✅ 안전: CSRF 토큰 사용
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.post('/transfer', csrfProtection, (req, res) => {
// CSRF 토큰 검증됨
res.json({ message: '이체 성공' });
});

중간자 공격 방어

// ❌ 위험: HTTP에서 쿠키 전송
res.cookie('sessionId', 'abc123');

// ✅ 안전: HTTPS에서만 쿠키 전송
res.cookie('sessionId', 'abc123', {
secure: true // HTTPS만
});

// ✅ 안전: HSTS 헤더 설정
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000');
next();
});

세션 고정 공격 방어

// 로그인 시 세션 ID 재생성
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = authenticate(username, password);

if (user) {
// 기존 세션 ID 무효화하고 새로 생성
req.session.regenerate((err) => {
if (err) {
return res.status(500).json({ message: '로그인 실패' });
}

req.session.userId = user.id;
res.json({ message: '로그인 성공' });
});
}
});

💡 실전 예시

완전한 인증 시스템

const express = require('express');
const session = require('express-session');
const bcrypt = require('bcrypt');
const helmet = require('helmet');

const app = express();
app.use(express.json());
app.use(helmet()); // 보안 헤더

// 세션 설정
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // 프로덕션에서는 HTTPS
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24시간
sameSite: 'strict'
}
}));

// 회원가입
app.post('/register', async (req, res) => {
try {
const { username, password } = req.body;

// 비밀번호 해싱
const hashedPassword = await bcrypt.hash(password, 10);

// 사용자 저장 (DB)
await saveUser({ username, password: hashedPassword });

res.status(201).json({ message: '회원가입 성공' });
} catch (error) {
res.status(500).json({ message: '회원가입 실패' });
}
});

// 로그인
app.post('/login', async (req, res) => {
try {
const { username, password } = req.body;

// 사용자 조회
const user = await findUser(username);
if (!user) {
return res.status(401).json({ message: '사용자를 찾을 수 없습니다' });
}

// 비밀번호 검증
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ message: '비밀번호가 틀렸습니다' });
}

// 세션 재생성 (세션 고정 공격 방어)
req.session.regenerate((err) => {
if (err) {
return res.status(500).json({ message: '로그인 실패' });
}

// 세션에 사용자 정보 저장
req.session.userId = user.id;
req.session.username = user.username;

res.json({
message: '로그인 성공',
user: {
id: user.id,
username: user.username
}
});
});
} catch (error) {
res.status(500).json({ message: '로그인 실패' });
}
});

// 인증 미들웨어
function requireAuth(req, res, next) {
if (!req.session.userId) {
return res.status(401).json({ message: '로그인이 필요합니다' });
}
next();
}

// 프로필 조회
app.get('/profile', requireAuth, async (req, res) => {
try {
const user = await findUserById(req.session.userId);
res.json({
id: user.id,
username: user.username,
email: user.email
});
} catch (error) {
res.status(500).json({ message: '프로필 조회 실패' });
}
});

// 로그아웃
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ message: '로그아웃 실패' });
}
res.clearCookie('connect.sid');
res.json({ message: '로그아웃 성공' });
});
});

// 세션 정보 확인
app.get('/session', (req, res) => {
if (req.session.userId) {
res.json({
isAuthenticated: true,
userId: req.session.userId,
username: req.session.username
});
} else {
res.json({ isAuthenticated: false });
}
});

🤔 자주 묻는 질문

Q1. 쿠키와 로컬 스토리지의 차이는?

A:

쿠키 (Cookie)
├─ 서버로 자동 전송
├─ 4KB 제한
├─ 만료 시간 설정 가능
├─ HttpOnly 설정 가능 (XSS 방어)
└─ 도메인/경로 제한

로컬 스토리지 (localStorage)
├─ 서버로 자동 전송 안 됨
├─ 5-10MB 제한
├─ 영구 저장 (수동 삭제 필요)
├─ JavaScript로만 접근 (XSS 취약)
└─ 같은 오리진만

세션 스토리지 (sessionStorage)
├─ 탭 단위 저장
├─ 탭 닫으면 삭제
└─ 나머지는 localStorage와 동일

언제 사용?
쿠키: 인증 토큰 (자동 전송 필요)
localStorage: 사용자 설정, 캐시 데이터
sessionStorage: 임시 폼 데이터

Q2. 세션 타임아웃은 어떻게 구현하나?

A:

// 1. 고정 타임아웃 (express-session 기본)
app.use(session({
cookie: {
maxAge: 30 * 60 * 1000 // 30분
}
}));

// 2. 슬라이딩 타임아웃 (활동 시 갱신)
app.use((req, res, next) => {
if (req.session.userId) {
// 마지막 활동 시간 업데이트
req.session.lastActivity = Date.now();
}
next();
});

// 3. 절대 타임아웃 (로그인 후 고정 시간)
app.use((req, res, next) => {
if (req.session.userId && req.session.loginTime) {
const elapsed = Date.now() - req.session.loginTime;
const maxDuration = 8 * 60 * 60 * 1000; // 8시간

if (elapsed > maxDuration) {
req.session.destroy();
return res.status(401).json({ message: '세션 만료' });
}
}
next();
});

// 4. 유휴 타임아웃 (일정 시간 활동 없으면)
app.use((req, res, next) => {
if (req.session.userId && req.session.lastActivity) {
const idle = Date.now() - req.session.lastActivity;
const maxIdle = 30 * 60 * 1000; // 30분

if (idle > maxIdle) {
req.session.destroy();
return res.status(401).json({ message: '세션 만료 (유휴)' });
}

// 활동 시간 업데이트
req.session.lastActivity = Date.now();
}
next();
});

Q3. Remember Me 기능은 어떻게 구현하나?

A:

app.post('/login', async (req, res) => {
const { username, password, rememberMe } = req.body;
const user = await authenticate(username, password);

if (user) {
req.session.userId = user.id;

if (rememberMe) {
// "Remember Me" 체크 시 쿠키 유효 기간 연장
req.session.cookie.maxAge = 30 * 24 * 60 * 60 * 1000; // 30일
} else {
// 기본: 브라우저 종료 시 삭제
req.session.cookie.maxAge = null;
}

res.json({ message: '로그인 성공' });
}
});

// 또는 별도의 remember token 사용
app.post('/login', async (req, res) => {
const { username, password, rememberMe } = req.body;
const user = await authenticate(username, password);

if (user) {
// 세션 쿠키 (단기)
req.session.userId = user.id;

if (rememberMe) {
// Remember Me 토큰 생성 (장기)
const rememberToken = generateSecureToken();

// DB에 토큰 저장
await saveRememberToken(user.id, rememberToken);

// 쿠키로 전송
res.cookie('rememberToken', rememberToken, {
httpOnly: true,
secure: true,
maxAge: 30 * 24 * 60 * 60 * 1000 // 30일
});
}

res.json({ message: '로그인 성공' });
}
});

// Remember Me 토큰 검증
app.use(async (req, res, next) => {
// 세션이 없지만 remember token이 있으면
if (!req.session.userId && req.cookies.rememberToken) {
const user = await findUserByRememberToken(req.cookies.rememberToken);

if (user) {
// 세션 재생성
req.session.userId = user.id;

// 보안: 새 토큰 발급
const newToken = generateSecureToken();
await updateRememberToken(user.id, newToken);
res.cookie('rememberToken', newToken, {
httpOnly: true,
secure: true,
maxAge: 30 * 24 * 60 * 60 * 1000
});
} else {
// 유효하지 않은 토큰 삭제
res.clearCookie('rememberToken');
}
}

next();
});

Q4. 다중 디바이스 로그인 관리는?

A:

// 세션 테이블
/*
sessions
- id
- userId
- sessionId
- deviceInfo (User-Agent)
- ipAddress
- createdAt
- lastActivity
*/

// 로그인 시 세션 기록
app.post('/login', async (req, res) => {
const user = await authenticate(req.body.username, req.body.password);

if (user) {
req.session.regenerate(async (err) => {
req.session.userId = user.id;

// 세션 정보 저장
await saveSessionInfo({
userId: user.id,
sessionId: req.sessionID,
deviceInfo: req.headers['user-agent'],
ipAddress: req.ip,
createdAt: new Date()
});

res.json({ message: '로그인 성공' });
});
}
});

// 활성 세션 목록 조회
app.get('/sessions', requireAuth, async (req, res) => {
const sessions = await getActiveSessions(req.session.userId);

res.json({
current: req.sessionID,
sessions: sessions.map(s => ({
id: s.sessionId,
device: parseUserAgent(s.deviceInfo),
location: s.ipAddress,
lastActivity: s.lastActivity,
isCurrent: s.sessionId === req.sessionID
}))
});
});

// 특정 세션 종료
app.delete('/sessions/:sessionId', requireAuth, async (req, res) => {
const { sessionId } = req.params;

// 본인 세션만 삭제 가능
const session = await getSession(sessionId);
if (session.userId !== req.session.userId) {
return res.status(403).json({ message: '권한 없음' });
}

// Redis에서 세션 삭제
await redisClient.del(`sess:${sessionId}`);

// DB에서 세션 기록 삭제
await deleteSession(sessionId);

res.json({ message: '세션 종료됨' });
});

// 모든 세션 종료 (다른 디바이스 로그아웃)
app.post('/logout-all', requireAuth, async (req, res) => {
const sessions = await getActiveSessions(req.session.userId);

// 모든 세션 삭제
for (const session of sessions) {
if (session.sessionId !== req.sessionID) {
await redisClient.del(`sess:${session.sessionId}`);
await deleteSession(session.sessionId);
}
}

res.json({ message: '모든 디바이스에서 로그아웃됨' });
});

Q5. 쿠키 없이 인증할 수 있나?

A:

가능합니다! Authorization 헤더 사용

1. Bearer Token (JWT 등)
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

장점:
├─ 쿠키 제약 없음
├─ 모바일 앱 적합
├─ CORS 문제 없음
└─ 토큰 저장소 선택 가능

단점:
├─ localStorage 사용 시 XSS 취약
├─ 매 요청마다 토큰 포함 필요
└─ 자동 전송 안 됨

2. API Key
Authorization: ApiKey abc123xyz
X-API-Key: abc123xyz

장점:
├─ 간단한 구현
├─ 서버 간 통신 적합
└─ 쿠키 불필요

단점:
├─ 보안 취약 (키 노출 위험)
├─ 사용자 인증보다는 앱 인증
└─ 키 갱신 어려움

권장:
- 웹: HttpOnly 쿠키 + JWT
- 모바일: Authorization 헤더 + JWT
- 서버 간: API Key

🎓 실습하기

1. 쿠키 테스트

// 쿠키 설정
document.cookie = "theme=dark; max-age=3600; path=/";

// 쿠키 읽기
console.log(document.cookie);

// 쿠키 파싱
function getAllCookies() {
return document.cookie.split('; ').reduce((acc, cookie) => {
const [name, value] = cookie.split('=');
acc[name] = value;
return acc;
}, {});
}

console.log(getAllCookies());

// 쿠키 삭제
document.cookie = "theme=; max-age=0; path=/";

2. httpstat.us를 사용한 세션 테스트

// 세션 쿠키 테스트
async function testSession() {
// 로그인 (세션 생성)
const loginRes = await fetch('https://api.example.com/login', {
method: 'POST',
credentials: 'include', // 쿠키 포함
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'test',
password: 'password'
})
});

console.log('Login:', loginRes.status);

// 프로필 조회 (세션 쿠키 자동 전송)
const profileRes = await fetch('https://api.example.com/profile', {
credentials: 'include'
});

const profile = await profileRes.json();
console.log('Profile:', profile);

// 로그아웃
const logoutRes = await fetch('https://api.example.com/logout', {
method: 'POST',
credentials: 'include'
});

console.log('Logout:', logoutRes.status);
}

testSession();

🔗 관련 문서

🎬 마치며

쿠키와 세션은 HTTP의 무상태성을 극복하고 사용자 인증을 구현하는 핵심 기술입니다. 보안을 고려한 올바른 사용이 중요합니다!

HTTP 시리즈를 모두 완료했습니다! 이제 HTTP의 핵심 개념을 모두 이해하셨을 것입니다. 실전 프로젝트에 적용해보세요!