[CS] 쿠키 vs 세션 vs JWT 완벽 정리 - 인증의 모든 흐름과 차이점
쿠키(Cookie)와 세션(Session)은 웹에서 사용자 상태 정보를 유지하기 위한 수단이다. HTTP는 기본적으로 무상태(stateless) 프로토콜이기 때문에, 클라이언트(브라우저)와 서버 간의 이전 요청/응답의 상태를 기억하지 않는다. 이런 특성을 보완하기 위해 쿠키와 세션이 사용된다.
1. 쿠키(Cookie)
🔎 쿠키란?
- 쿠키는 클라이언트(주로 웹 브라우저)에 저장되는 key-value 형식의 데이터이다.
- 쿠키는 브라우저가 서버와 통신할 때 HTTP 요청과 응답 헤더를 통해 주고받는 정보이다.
- 브라우저는 서버로부터 받은 쿠키를 저장하고, 다음 요청 때 자동으로 다시 서버로 전송한다.
🔄 쿠키의 흐름
🧩 서버가 쿠키를 설정하는 과정
서버는 응답 시 다음과 같은 Set-Cookie
헤더를 이용하여 쿠키를 생성한다:
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Max-Age=3600
- 이 응답이 브라우저에 도착하면, 브라우저는
sessionId=abc123
이라는 쿠키를 저장한다. - 이 쿠키는 지정된
Path
(시작 경로 기준)와 일치하는 모든 요청에 대해 서버로 함께 전송된다.
🧩 클라이언트가 쿠키를 서버에 전송하는 방식
이후 브라우저가 동일한 도메인과 경로로 다시 요청을 보낼 경우:
GET /dashboard HTTP/1.1
Host: example.com
Cookie: sessionId=abc123
- 서버는 이 요청에 담긴
sessionId
를 읽고, 해당 사용자가 어떤 사용자였는지를 식별한다.
❓ 쿠키는 왜 중요한가?
- 상태 없는 HTTP를 보완: HTTP는 매 요청마다 이전 상태를 기억하지 않음. 쿠키는 사용자를 식별할 수 있는 정보를 담아 상태 유지를 가능하게 함.
- 사용자 식별/추적: 로그인 유지, 자동 로그인, 방문자 카운팅, 광고 타겟팅 등 사용자를 식별하거나 추적하는 데 활용됨.
- 서버 부담 완화: 서버는 상태 정보를 유지하지 않아도 되며, 클라이언트 측에서 간단한 상태 정보를 들고 다니게 함.
📋 주요 쿠키 속성 정리
속성 | 설명 |
---|---|
Name=Value | 쿠키의 키와 값 |
Expires/Max-Age | 쿠키 유효 기간 설정 (없으면 브라우저 세션 종료 시 삭제) |
Path | 어떤 경로에서 이 쿠키가 유효한지 설정 |
Domain | 어떤 도메인에서 이 쿠키가 유효한지 설정 |
HttpOnly | 자바스크립트에서 접근 불가 (XSS 방지용) |
Secure | HTTPS 연결에서만 전송됨 |
SameSite | 크로스사이트 요청에서의 쿠키 제한 (Strict, Lax, None) |
⚠️ XSS 공격이란?
XSS는 공격자가 악성 자바스크립트를 삽입하여, 다른 사용자의 브라우저에서 이를 실행하게 만드는 공격이다. 이를 통해 세션 쿠키 탈취, 로컬 스토리지 접근, 키 입력 감시, 위장된 요청 전송 등 다양한 피해를 유발할 수 있다.
🧭 실제 예시 흐름
- 사용자가
https://example.com/login
에 로그인 - 서버는 인증 성공 시
Set-Cookie: authToken=xyz123; HttpOnly; Secure
를 응답 헤더에 포함 - 브라우저는 쿠키를 저장하고, 이후 요청 시 해당 쿠키를
Cookie
헤더에 포함시켜 전송 - 서버는 이 쿠키로 인증된 사용자임을 판별
2. 세션(Session)
🔎 세션이란?
- 세션(Session)은 서버 측에서 유지되는 사용자 상태 정보 컨테이너이다.
- HTTP는 본래 무상태(stateless) 프로토콜이기 때문에, 요청 간 사용자를 구분하려면 상태 정보 저장 방식이 필요하다.
- 이 문제를 해결하기 위해 서버는 사용자가 접속할 때 고유한 세션 ID를 발급하고, 그 ID에 대응하는 데이터를 서버 메모리(또는 외부 저장소)에 저장한다.
- 클라이언트는 이후 요청 시 세션 ID를 통해 자신이 누구인지 서버에 알려준다.
🔄 세션의 흐름
🧩 최초 요청
- 사용자가 로그인 또는 최초 접근 시, 서버는 내부적으로 세션 객체 생성
- 이때 생성된 세션 ID를 클라이언트에게 쿠키 형태로 전달 (
Set-Cookie: JSESSIONID=abcd1234
)
🧩 이후 요청
- 브라우저는 서버로 요청할 때
Cookie: JSESSIONID=abcd1234
를 포함 - 서버는 해당 ID를 가진 세션 정보를 찾아 사용자를 식별함
🧩 예시
응답 헤더:
Set-Cookie: JSESSIONID=abcd1234; Path=/; HttpOnly
요청 헤더:
Cookie: JSESSIONID=abcd1234
📦 세션에 저장되는 정보 예시
- 로그인한 사용자 ID
- 사용자 권한 정보 (예: admin 여부)
- 장바구니 품목 목록
- 인증 토큰
- 페이지 이동 상태, 임시 폼 정보 등
이 정보들은 브라우저가 아닌 서버의 메모리나 외부 저장소에 저장되므로 클라이언트는 내용에 접근할 수 없다.
🛠️ 세션의 저장 방식 (서버 측)
- 메모리 (기본): 빠르지만 서버 재시작 시 세션 초기화
- 파일 기반 저장: 파일에 객체 직렬화하여 저장
- DB 기반 저장: 세션을 데이터베이스에 저장하여 다중 서버 간 공유
- Redis/Memcached: 대용량 고속 세션 공유에 적합 (실제 서비스에서 일반적)
⏱️ 세션 유지 시간과 만료
- 세션에는 유효 시간이 있음 (
timeout
)- 예: 30분 동안 아무 활동이 없으면 자동 만료
- 설정 위치는 프레임워크 및 서버마다 다름
- Spring Boot 예시:
server.servlet.session.timeout=30m
- Spring Boot 예시:
🔗 세션과 쿠키의 관계
- 쿠키는 세션 ID를 저장하는 데 사용되는 전달 수단일 뿐이다.
- 실제 중요한 데이터는 서버 세션 저장소에 존재하며,
- 클라이언트는 세션 ID만 알고, 그 외의 내부 정보는 알 수 없다.
⚖️ 세션의 장점과 단점
항목 | 장점 | 단점 |
---|---|---|
보안성 | 데이터가 서버에 있으므로 조작이 불가능 | 세션 ID 탈취 시 위험 존재 |
구조 | 민감 정보 숨김 가능 | 서버가 상태를 유지해야 함 (리소스 사용) |
유연성 | 로그인, 장바구니, 다중 페이지 상태 관리 등 적합 | 서버 확장 시 세션 클러스터링 필요 |
3. 쿠키 vs 세션 비교
항목 | 쿠키 | 세션 |
---|---|---|
저장 위치 | 클라이언트 | 서버 |
주요 역할 | 클라이언트가 직접 상태 정보 보관 | 상태 정보는 서버가 관리, 클라이언트는 ID만 보관 |
보안성 | 낮음 (JS 접근 가능, 조작 가능) | 높음 (내용은 서버에만 존재) |
유효성 | 만료 시간 설정에 따라 브라우저 종료 후에도 유지 가능 | 일반적으로 브라우저 종료/타임아웃 시 만료 |
4. 실전 사용 예시
1. 로그인 유지 (세션 기반 인증)
🎯 시나리오
- 사용자가
login
API를 통해 로그인 - 서버는 사용자 인증 후 세션을 생성
- 세션 ID를 쿠키에 담아 클라이언트에 전달 (
Set-Cookie: JSESSIONID=...
) - 이후 클라이언트는 모든 요청에 쿠키를 자동으로 포함
🔹 핵심 포인트
- 세션에 사용자 ID, 권한 정보, 상태 등을 저장
- 클라이언트는 오직 세션 ID만 소유 -> 실제 인증 정보는 서버에 있음
- 보안성 높음 (클라이언트 조작 불가)
🔹 예시
POST /login HTTP/1.1
→ Set-Cookie: JSESSIONID=abc123; HttpOnly; Secure
GET /mypage HTTP/1.1
→ Cookie: JSESSIONID=abc123
→ 서버: 세션 조회 → 사용자 정보 확인 → 응답
2. 자동 로그인 (쿠키 활용)
🎯 시나리오
- 사용자가 로그인 시 “자동 로그인” 옵션을 선택
- 서버는 로그인 성공 시:
- Access Token을 발급하고, 클라이언트 메모리(예: 앱 상태)에 저장
- Refresh Token을 발급하고, 보안 설정이 적용된 쿠키(HttpOnly 등)로 브라우저에 저장
- 이후 사용자가 다시 방문하면:
- Access Token은 만료되어도,
- 브라우저가 자동으로 쿠키에 저장된 Refresh Token을 서버에 전송
- 서버는 이 토큰이 유효하면 새로운 Access Token을 재발급
- 사용자는 로그인 없이 자동으로 인증 상태로 진입
🔹 핵심 포인트
- 보통
AccessToken
은 메모리 또는 세션,RefreshToken
은HttpOnly
쿠키에 저장 - 쿠키가 유효하면 자동 로그인 처리, 토큰 갱신 로직 동작
HttpOnly
,Secure
,SameSite=Lax
등 보안 설정 필수
3. 장바구니 (비로그인 상태에서의 세션 활용)
🎯 시나리오
- 비회원도 장바구니 사용 가능
- 서버는 세션에 장바구니 항목 저장
- 세션 ID로 사용자를 식별해 요청에 응답
🔹 구현 방식
- 장바구니는 로그인 여부와 무관하게 세션에 저장
- 로그인 시 세션 장바구니를 DB로 병합 가능
4. 다단계 입력폼 (세션으로 임시 상태 저장)
🎯 시나리오
- 회원 가입 시, 여러 단계를 거침 (1단계: 정보 입력 -> 2단계: 본인 인증 -> 3단계: 약관 동의)
- 각 단계에서 입력된 정보는 서버 세션에 저장
🔹 이유
- 각 단계에서 폼 데이터를 완전히 전송하지 않고, 세션에 누적 저장하여 유실 방지
- 마지막 단계에서만 최종 제출
5. 광고 트래킹 / 방문 분석 (쿠키 활용)
🎯 시나리오
- 사용자의 방문 횟수, 마지막 방문 시간, 특정 캠페인 클릭 여부 등 기록
- 쿠키를 통해 브라우저 단위로 사용자를 추적
🔹 예시
Set-Cookie: visitId=xyz789; Max-Age=2592000; Path=/
- Google Analytics, Facebook Pixel 등도 쿠키를 이용해 사용자 식별 및 행동 추적
6. 사용자 설정 저장 (쿠키 활용)
🎯 시나리오
- 사용자가 웹사이트의 다크모드/언어/정렬 순서 등을 설정
- 서버에 저장하지 않고, 쿠키에 로컬 설정 정보 저장
🔹 장점
- 비회우너 사용자도 환경 설정 유지 가능
- UX 향상 + 서버 리소스 절약
Set-Cookie: theme=dark; Max-Age=31536000
5. 보안 고려사항
- 쿠키의 경우 민감 정보 저장 금지 (대신 세션 ID 또는 토큰 사용)
HttpOnly
,Secure
,SameSite
속성 적극 활용- 세션은 일정 시간 동안 유효하고, 주기적 갱신 필요
- 서버 측 세션 관리는 GC 설정, 메모리 관리 전략 함께 고려해야 함
6. JWT (JSON Web Token)
📌 JWT의 정의
JWT는 인증 정보를 자체적으로 포함하는 토큰 기반 인증 방식이다. 사용자가 로그인에 성공하면 서버는 토큰을 만들어 클라이언트에게 전달하며, 클라이언트는 이후 이 토큰을 요청 헤더에 포함시켜 서버에 인증 정보를 전달한다. 서버는 이 토큰을 검증함으로써 무상태(stateless) 인증을 수행한다.
🧬 JWT의 구조
JWT는 세 부분으로 구성된 문자열이다. .
으로 구분된다.
<헤더(header)>.<페이로드(payload)>.<서명(signature)>
- Header: 토큰 유형 (typ)과 서명 알고리즘 (alg) 정보
- Payload: 사용자 정보, 권한 등 클레임(claim)이 포함됨
- Signature: Header + Payload를 기반으로 서버 비밀키로 생성한 HMAC 서명
🔁 JWT 인증 흐름
🧩 로그인 요청
- 클라이언트가 아이디/비밀번호로 로그인
- 서버가 사용자 인증 후 JWT를 생성
🧩 토큰 발급
- JWT를 생성 후 응답으로 전달
- 클라이언트는 이 토큰을 로컬스토리지 또는 쿠키에 저장
🧩 이후 요청
- 클라이언트가 API 호출 시 HTTP 헤더에 토큰 포함:
Authorization: Bearer <JWT>
🧩 서버는 토큰 검증
- 서버는 비밀 키로 서명을 검증하고, 페이로드를 파싱해서 사용자 인증
⚖️ JWT vs 세션 기반 인증
항목 | 세션 기반 인증 | JWT 기반 인증 |
---|---|---|
상태 관리 | 서버가 세션을 저장 (유상태) | 서버는 토큰 저장 안 함 (무상태) |
저장 위치 | 서버 메모리 or 외부 저장소 | 클라이언트 (로컬스토리지, 쿠키 등) |
확장성 | 세션 클러스터링 필요 | 서버 확장 쉬움, 공유 필요 없음 |
성능 | 상태 조회 비용 존재 | 서명 검증만 하면 됨 (빠름) |
보안성 | 세션 ID 탈취 방지 필요 | 토큰 탈취 → 만료 전까지 재사용 가능 |
만료/폐기 | 서버에서 세션 강제 만료 가능 | 발급된 JWT는 폐기 불가 (단, 블랙리스트 방식으로 대응 가능) |
🌟 JWT의 장점
- 무상태(stateless) 인증: 서버에서 세션 유지 안 해도 됨
- 분산 시스템에 강함: 여러 서버에서 인증 공유가 필요 없음
- 전송 용이성: 모든 HTTP 클라이언트에서
Authorization
헤더로 간단히 전송 가능 - 서버 간 통합 인증 수단으로도 활용 가능 (SSO)
⚠️ JWT의 단점
- 탈취 시 취약: 유출되면 만료 전까지 누구나 사용할 수 있음
- 폐기 어려움: 세션처럼 서버에서 강제로 끊을 수 없음
- 토큰 크기 큼: 페이로드가 포함되어 있어 헤더 부담 증가
- 서버 시간에 의존: 시간 차이로 유효성 판단에 문제 생길 수 있음
7. Access Token과 Refresh Token
항목 | Access Token | Refresh Token |
---|---|---|
목적 | API 요청 인증 | Access Token 재발급 |
수명 | 짧음 (수분~수십 분) | 김 (수 시간~수 주) |
저장 위치 | 보통 쿠키(권장), 로컬스토리지 | 쿠키, DB(서버 저장 권장) |
보안 중요도 | 중간 (유출되면 제한된 시간만 위험) | 매우 높음 (장기 노출 시 위험) |
서버 저장 | 저장 ❌ (무상태) | 저장 ✅ (서버 상태 필요) |
폐기 가능 여부 | 폐기 불가 (단, 블랙리스트 예외) | 서버에서 직접 폐기 가능 |
🧭 동작 흐름 (로그인부터 만료까지)
- 사용자가 로그인 -> 서버에서 Access + Refresh Token 생성
- 클라이언트는 Access Token으로 API 요청 수행
- Access Token이 만료되면:
- 클라이언트가 Refresh Token으로 새로운 Access Token 요청
- 서버는 Refresh Token의 유효성 확인 -> Access Token 재발급
- Refresh Token도 만료됐거나 폐기된 경우 -> 다시 로그인 필요
🛠️ 실전 설계 포인트
- 저장 위치: 가능한 HttpOnly 쿠키 사용 (로컬스토리지 X)
- 만료 정책: 짧은 Access Token + 긴 Refresh Token 조합
- 폐기 대응: 서버에 블랙리스트 저장소(redis 등)를 두고 처리
- HTTPS 필수: 토큰은 암호화되지 않으므로 반드시 TLS 사용
📍 Refresh Token을 주로 Redis에서 관리하는 이유
✅ 이유 | 설명 |
---|---|
자동 만료 관리 | TTL로 만료 처리 자동화 가능 |
빠른 성능 | 메모리 기반, 요청 많아도 병목 없음 |
상태 추적 | 사용자별 토큰/디바이스 관리 용이 |
보안 유연성 | 블랙리스트, 로그아웃, 회수 등 처리 쉬움 |
분산 환경 대응 | 다중 서버에서도 중앙화된 토큰 관리 가능 |
단순한 운영 | 명령어 직관적, 유지 보수 용이 |
8. 결론
- 쿠키(Cookie): 클라이언트에 데이터를 저장하고 자동으로 서버에 전송하는 수단
- 세션(Session): 서버 측에서 사용자의 상태를 저장하기 위한 구조
- JWT(JSON Web Token): 사용자 인증 정보와 클레임을 JSON 형태로 담고, 서명된 토큰 형식
✅ 세션 방식과 JWT 방식은 인증을 처리하는 “방식”에 대한 구분이고, ✅ 쿠키는 이 인증 정보를 “어떻게 클라이언트와 주고받을지”에 대한 “전달 수단”이다.
댓글남기기