쿠키(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를 읽고, 해당 사용자가 어떤 사용자였는지를 식별한다.

쿠키는 왜 중요한가?

  1. 상태 없는 HTTP를 보완: HTTP는 매 요청마다 이전 상태를 기억하지 않음. 쿠키는 사용자를 식별할 수 있는 정보를 담아 상태 유지를 가능하게 함.
  2. 사용자 식별/추적: 로그인 유지, 자동 로그인, 방문자 카운팅, 광고 타겟팅 등 사용자를 식별하거나 추적하는 데 활용됨.
  3. 서버 부담 완화: 서버는 상태 정보를 유지하지 않아도 되며, 클라이언트 측에서 간단한 상태 정보를 들고 다니게 함.

📋 주요 쿠키 속성 정리

속성 설명
Name=Value 쿠키의 키와 값
Expires/Max-Age 쿠키 유효 기간 설정 (없으면 브라우저 세션 종료 시 삭제)
Path 어떤 경로에서 이 쿠키가 유효한지 설정
Domain 어떤 도메인에서 이 쿠키가 유효한지 설정
HttpOnly 자바스크립트에서 접근 불가 (XSS 방지용)
Secure HTTPS 연결에서만 전송됨
SameSite 크로스사이트 요청에서의 쿠키 제한 (Strict, Lax, None)

⚠️ XSS 공격이란?
XSS는 공격자가 악성 자바스크립트를 삽입하여, 다른 사용자의 브라우저에서 이를 실행하게 만드는 공격이다. 이를 통해 세션 쿠키 탈취, 로컬 스토리지 접근, 키 입력 감시, 위장된 요청 전송 등 다양한 피해를 유발할 수 있다.

🧭 실제 예시 흐름

  1. 사용자가 https://example.com/login에 로그인
  2. 서버는 인증 성공 시 Set-Cookie: authToken=xyz123; HttpOnly; Secure를 응답 헤더에 포함
  3. 브라우저는 쿠키를 저장하고, 이후 요청 시 해당 쿠키를 Cookie 헤더에 포함시켜 전송
  4. 서버는 이 쿠키로 인증된 사용자임을 판별

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

🔗 세션과 쿠키의 관계

  • 쿠키는 세션 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은 메모리 또는 세션, RefreshTokenHttpOnly 쿠키에 저장
  • 쿠키가 유효하면 자동 로그인 처리, 토큰 갱신 로직 동작
  • 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(서버 저장 권장)
보안 중요도 중간 (유출되면 제한된 시간만 위험) 매우 높음 (장기 노출 시 위험)
서버 저장 저장 ❌ (무상태) 저장 ✅ (서버 상태 필요)
폐기 가능 여부 폐기 불가 (단, 블랙리스트 예외) 서버에서 직접 폐기 가능

🧭 동작 흐름 (로그인부터 만료까지)

  1. 사용자가 로그인 -> 서버에서 Access + Refresh Token 생성
  2. 클라이언트는 Access Token으로 API 요청 수행
  3. Access Token이 만료되면:
    • 클라이언트가 Refresh Token으로 새로운 Access Token 요청
    • 서버는 Refresh Token의 유효성 확인 -> Access Token 재발급
  4. Refresh Token도 만료됐거나 폐기된 경우 -> 다시 로그인 필요

🛠️ 실전 설계 포인트

  • 저장 위치: 가능한 HttpOnly 쿠키 사용 (로컬스토리지 X)
  • 만료 정책: 짧은 Access Token + 긴 Refresh Token 조합
  • 폐기 대응: 서버에 블랙리스트 저장소(redis 등)를 두고 처리
  • HTTPS 필수: 토큰은 암호화되지 않으므로 반드시 TLS 사용

📍 Refresh Token을 주로 Redis에서 관리하는 이유

✅ 이유 설명
자동 만료 관리 TTL로 만료 처리 자동화 가능
빠른 성능 메모리 기반, 요청 많아도 병목 없음
상태 추적 사용자별 토큰/디바이스 관리 용이
보안 유연성 블랙리스트, 로그아웃, 회수 등 처리 쉬움
분산 환경 대응 다중 서버에서도 중앙화된 토큰 관리 가능
단순한 운영 명령어 직관적, 유지 보수 용이

8. 결론

  • 쿠키(Cookie): 클라이언트에 데이터를 저장하고 자동으로 서버에 전송하는 수단
  • 세션(Session): 서버 측에서 사용자의 상태를 저장하기 위한 구조
  • JWT(JSON Web Token): 사용자 인증 정보와 클레임을 JSON 형태로 담고, 서명된 토큰 형식

✅ 세션 방식과 JWT 방식은 인증을 처리하는 “방식”에 대한 구분이고, ✅ 쿠키는 이 인증 정보를 “어떻게 클라이언트와 주고받을지”에 대한 “전달 수단”이다.

태그:

카테고리:

업데이트:

댓글남기기