728x90
쿠키 vs 세션 vs 토큰(JWT) 완벽 정리 — 개념, 동작 원리, 코드, 보안까지
웹 개발에서 "로그인 상태를 어떻게 유지하는가?"는 가장 기본적이면서도 중요한 질문이다. HTTP는 기본적으로 무상태(Stateless) 프로토콜이라 요청마다 "나 누구예요"를 다시 알려줘야 한다.
이 문제를 해결하는 세 가지 방식이 쿠키, 세션, 토큰(JWT)이다. 이 글에서는 각각의 개념, 동작 원리, 실전 코드, 보안 주의사항까지 한 번에 정리한다.
한눈에 보는 비교표
| 구분 | 쿠키(Cookie) | 세션(Session) | 토큰(JWT) |
|---|---|---|---|
| 저장 위치 | 클라이언트(브라우저) | 서버(메모리/DB) | 클라이언트(브라우저) |
| 식별 방식 | Cookie 헤더로 키-값 전달 | JSESSIONID 쿠키로 세션 조회 | Authorization 헤더로 토큰 전달 |
| 수명 | Max-Age/Expires 지정 | 유휴 시간 초과 시 만료 | 토큰 내 exp 클레임 |
| 서버 부담 | 없음 | 세션 저장소 필요 | 없음 (Stateless) |
| 확장성 | 좋음 | 서버 늘리면 세션 공유 필요 | 좋음 |
| 보안 위험 | XSS, 탈취 | 세션 고정 공격 | 토큰 탈취 시 만료까지 유효 |
| 주 용도 | 아이디 기억, 다크모드 | 로그인 상태, 장바구니 | API 인증, SPA, 마이크로서비스 |
1. 쿠키(Cookie) — 브라우저가 들고 다니는 메모
개념
서버가 브라우저에 "이거 저장해둬"라고 건네는 작은 데이터다. 브라우저는 이후 같은 서버에 요청할 때마다 쿠키를 자동으로 함께 보낸다.
동작 흐름
1. 브라우저 → 서버: 로그인 요청
2. 서버 → 브라우저: 응답 + Set-Cookie: rememberId=hong
3. 이후 모든 요청: 브라우저가 Cookie: rememberId=hong 자동 전송
쿠키 구성
Set-Cookie: rememberId=hong; Path=/; Domain=.example.com; Max-Age=2592000; Secure; HttpOnly; SameSite=Lax
| 속성 | 설명 |
|---|---|
name=value |
쿠키 이름과 값 |
Path |
이 경로 아래에서만 전송 |
Domain |
이 도메인에서만 전송 |
Max-Age |
초 단위 유효기간. 없으면 브라우저 종료 시 삭제(세션 쿠키) |
Secure |
HTTPS에서만 전송 |
HttpOnly |
JavaScript로 접근 차단 (XSS 완화) |
SameSite |
크로스사이트 전송 제어 (Lax/Strict/None) |
코드 예제 (서블릿)
// 쿠키 생성 (로그인 시 "아이디 기억하기")
Cookie remember = new Cookie("rememberId",
URLEncoder.encode(userId, StandardCharsets.UTF_8));
remember.setPath("/");
remember.setMaxAge(60 * 60 * 24 * 30); // 30일
remember.setHttpOnly(true);
remember.setSecure(true);
resp.addCookie(remember);
// 쿠키 읽기
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("rememberId".equals(c.getName())) {
String id = URLDecoder.decode(c.getValue(), StandardCharsets.UTF_8);
// 아이디 기억하기 처리
}
}
}
2. 세션(Session) — 서버가 기억하는 사용자 상태
개념
서버가 사용자별로 데이터를 저장하고, 식별용 ID(JSESSIONID)만 쿠키로 브라우저에 건네는 방식이다. 민감한 정보(로그인 상태, 권한)는 서버에만 있고, 브라우저에는 열쇠(세션 ID)만 있다.
동작 흐름
1. 브라우저 → 서버: 로그인 요청 (id/pw)
2. 서버: 인증 성공 → HttpSession에 사용자 정보 저장
3. 서버 → 브라우저: Set-Cookie: JSESSIONID=abc123
4. 이후 요청: 브라우저가 JSESSIONID 전송 → 서버가 해당 세션 찾아서 사용자 식별
5. 로그아웃: session.invalidate() → 세션 파기
코드 예제 (서블릿)
// 로그인 성공 시 세션 생성
HttpSession session = req.getSession();
session.setAttribute("loginUser", user);
session.setMaxInactiveInterval(30 * 60); // 30분 유휴 시 만료
// 세션 고정 공격 방지: 인증 후 세션 재발급
session.invalidate();
HttpSession newSession = req.getSession(true);
newSession.setAttribute("loginUser", user);
// 로그아웃
HttpSession session = req.getSession(false);
if (session != null) {
session.invalidate();
}
resp.sendRedirect("/");
세션의 한계
- 서버 메모리 사용: 동시 접속자가 많으면 메모리 부담
- Scale-out 어려움: 서버를 여러 대로 늘리면 세션 공유가 필요
- 해결책 1: 스티키 세션 — 같은 사용자를 같은 서버로 라우팅
- 해결책 2: 외부 세션 저장소 — Redis 같은 공유 저장소에 세션 보관
3. 토큰(JWT) — 서버가 안 기억해도 되는 인증
개념
JWT(JSON Web Token)는 사용자 인증 정보를 토큰 자체에 담아서 클라이언트에게 건네는 방식이다. 서버가 세션을 저장할 필요가 없어서(Stateless) 확장성이 좋다.
JWT 구조
xxxxx.yyyyy.zzzzz
│ │ │
Header Payload Signature
| 부분 | 내용 |
|---|---|
| Header | 알고리즘(HS256 등), 토큰 타입(JWT) |
| Payload | 사용자 ID, 권한, 만료 시간 등 (클레임) |
| Signature | Header + Payload + 서버 비밀키로 생성한 서명 |
동작 흐름
1. 브라우저 → 서버: 로그인 요청 (id/pw)
2. 서버: 인증 성공 → JWT 생성 → 클라이언트에 전달
3. 브라우저: JWT를 localStorage 또는 Cookie에 저장
4. 이후 요청: Authorization: Bearer <JWT> 헤더로 전송
5. 서버: JWT 서명 검증 → Payload에서 사용자 정보 추출
토큰의 장단점
| 장점 | 단점 |
|---|---|
| 서버가 상태를 안 저장 (Stateless) | 토큰 탈취 시 만료까지 유효 |
| 서버 확장(Scale-out)에 유리 | 토큰 크기가 세션 ID보다 큼 |
| 마이크로서비스 간 인증 전달 쉬움 | 서버에서 강제 로그아웃 어려움 |
| 모바일 앱과 SPA에 적합 | Payload는 Base64 인코딩일 뿐 암호화 아님 |
Access Token + Refresh Token 전략
실무에서는 토큰 탈취 위험을 줄이기 위해 두 종류의 토큰을 함께 사용한다.
| 토큰 | 용도 | 유효기간 | 저장 위치 |
|---|---|---|---|
| Access Token | API 인증 | 짧게 (15분~1시간) | 메모리 또는 Cookie |
| Refresh Token | Access Token 재발급 | 길게 (7일~30일) | HttpOnly Cookie |
1. Access Token 만료됨
2. 브라우저 → 서버: Refresh Token으로 재발급 요청
3. 서버: Refresh Token 검증 → 새 Access Token 발급
4. 브라우저: 새 Access Token으로 API 요청 계속
보안 체크리스트
쿠키 보안
-
HttpOnly설정 — JavaScript 접근 차단 (XSS 완화) -
Secure설정 — HTTPS에서만 전송 -
SameSite=Lax또는Strict— CSRF 완화 - 민감 정보(비밀번호, 토큰) 직접 저장 금지
세션 보안
- 로그인 성공 시 세션 ID 재발급 — 세션 고정 공격 방지
- 세션 만료 시간 적절히 설정 (보통 30분)
- 분산 환경에서 세션 저장소 보안 확보
토큰 보안
- Payload에 민감 정보 넣지 않기 (Base64 디코딩 가능)
- Access Token 유효기간 짧게 설정
- Refresh Token은 HttpOnly 쿠키에 저장
- HTTPS 필수
실무에서 언제 뭘 쓸까?
| 상황 | 추천 방식 | 이유 |
|---|---|---|
| 전통적인 웹 서비스 (SSR) | 세션 + 쿠키 | 서버 렌더링에 자연스러움 |
| SPA (React, Vue 등) | JWT | API 기반 통신에 적합 |
| 모바일 앱 | JWT | 쿠키 관리가 어려운 환경 |
| 마이크로서비스 | JWT | 서비스 간 인증 전달 용이 |
| "아이디 기억하기" | 쿠키 단독 | 인증과 무관한 편의 기능 |
| 다크모드/언어 설정 | 쿠키 단독 | 사용자 설정 저장 |
마무리 체크리스트 (면접 대비)
- 쿠키/세션/토큰의 저장 위치와 역할 차이를 10초 안에 설명할 수 있는가?
- JSESSIONID가 무엇이고 어떤 역할을 하는지 아는가?
- 세션 고정 공격이 무엇이고 어떻게 방지하는지 아는가?
- JWT의 구조(Header/Payload/Signature)를 설명할 수 있는가?
- Access Token + Refresh Token 전략을 설명할 수 있는가?
- HttpOnly/Secure/SameSite 각각의 역할을 아는가?
- 분산 환경에서 세션 유지 방법(스티키 세션 vs Redis)의 장단점을 아는가?
- SPA에서 JWT를 쓰는 이유를 설명할 수 있는가?
728x90
'Computer Science > NetWork' 카테고리의 다른 글
| CORS 동작 원리와 Spring Boot 설정 방법 (0) | 2026.07.03 |
|---|---|
| REST API 완벽 정리 — 개념, 설계 원칙, HTTP 메서드, 실전 예제까지 한방에 (0) | 2026.06.29 |
| REST API 입문 가이드: 서버와 클라이언트가 데이터를 주고받는 기본 원리 (0) | 2026.06.09 |
| 📌"브라우저가 HTML을 어떻게 화면에 띄울까?" (1) | 2025.08.09 |
| 📌"브라우저가 URL을 입력하면 서버까지 무슨 일이 벌어질까 (3) | 2025.08.09 |