쿠키, 세션, JWT

Joowon Jang·2025년 2월 18일

"쿠키", "세션"이라는 단어는 웹 개발자라면 정말 많이 들어봤을 것 같다.
하지만, 그 개념을 정확하게 이해하고 사용하는 사람보다 어렴풋이 아는 사람이 더 많은 것 같다.

특히, 사용자 인증에 많이 사용되는 개념인 만큼 정확하게 아는 것이 중요하다고 생각한다.


쿠키

쿠키(Cookie)란?

클라이언트(브라우저)에 저장되는 작은 데이터 조각(최대 약 4KB).
서버가 HTTP 응답 시 Set-Cookie 헤더를 통해 쿠키를 설정하고, 이후 클라이언트는 서버에 요청할 때 해당 쿠키를 함께 전송한다.

쉽게 말해, 서버에서 필요한 정보를 자체적으로 저장하지 않고 클라이언트에게 맡겨놓는 것이다.

특징

  • 수명 관리: Expires 속성을 통해 쿠키가 삭제/만료되는 시간을 설정할 수 있다.

  • 자동 전송: 동일한 도메인에 대해 요청 시 자동으로 전송됨.

  • 보안 설정 가능: (쿠키 속성 설정)

    • HttpOnly: 자바스크립트로 접근 불가. (브라우저에서 조작 방지)
    • Secure: HTTPS에서만 전송됨.
    • SameSite: 크로스 사이트(타도메인) 요청 제한/허용.
      (외부 도메인을 허용하면 그게 바로 "써드파티 쿠키")

브라우저에서 써드파티 쿠키를 허용하지 않도록 설정해두고 사용하는 사용자도 많고,
모바일 환경은 ios같은 경우 자동으로 차단 설정이 되어있기 때문에 사용하지 않는 것이 좋다.

세션

세션(Session)이란?

서버 측에 저장되는 사용자 상태 정보.
사용자가 로그인처럼 서버와 연결을 맺으면, 서버는 세션을 생성하고 해당 세션에 대한 고유한 식별자(session ID)를 클라이언트에게 전달한다.

이 session ID는 보통 쿠키에 저장되어 이후 요청마다 함께 전송된다.

특징

  • 저장 위치: 서버 메모리, 데이터베이스, Redis 등 서버 측 저장소.

  • 크기 제한 없음: 서버에 저장되므로 구조나 용량에 제한이 적음.

  • 수명: 서버 설정에 따라 만료 시간 설정 가능.

  • 보안성: 클라이언트에는 ID만 저장되고, 실제 데이터(민감한 데이터)는 서버에 있으므로 비교적 안전.

예시 동작흐름

  1. 사용자가 로그인

  2. 서버가 sessionId=abc123 생성.

  3. 응답에 Set-Cookie: sessionId=abc123 포함.
    (HttpOnly, Secure 설정해 브라우저에서 조작 방지)

  4. 클라이언트(브라우저)가 응답을 받으면 쿠키 저장소에 sessionId=abc123 설정.

  5. 이후 클라이언트는 요청에 Cookie: sessionId=abc123 포함.

  6. 서버는 이 ID로 세션 저장소에서 사용자 상태를 조회.

  7. 세션 저장소에 해당 sessionId가 없으면 에러 반환.


JWT(JSON Web Token)

과거에는 페이지를 이동할 때마다 서버가 새 HTML을 렌더링(SSR)하여 전달했으므로, 서버가 직접 사용자의 상태를 관리하는 쿠키·세션 방식이 보안과 검증 측면에서 유리했다.

반면, 최근에는 SPA(Single Page Application)가 대세가 되면서 서버는 데이터만 제공하고 클라이언트가 렌더링을 주도하게 되었다. 이 과정에서 서버의 부하를 줄이고, 여러 도메인이나 모바일 환경에서도 유연하게 인증을 처리하기 위해 서버에 상태를 저장하지 않는 JWT(JSON Web Token) 인증 방식이 널리 사용되고 있다.

JWT란?

아래와 같이 Header(머릿말?), Payload,(본문) Signature(서명) 부분으로 이루어진 문자열이다.

xxxxx.yyyyy.zzzzz
<base64url(header)>.<base64url(payload)>.<base64url(signature)>

JSON 형식의 데이터를 base64방식으로 인코딩하여 만든 문자열이며,
Signature 부분에는 이 데이터가 위/변조 되지 않은 데이터라는 서명이 들어간다.

HS256, RS256 같은 암호화 알고리즘을 사용하며 아래와 같이 Header에 어떤 암호화 방식을 사용하는지 표기한다.

// 헤더 (암호화 방식 등의 설정값)
const header = {
  "alg": "RS256",
  "typ": "JWT"
}

// 본문 (JSON 형식의 데이터)
const payload = {
  userId: 'abc123',
  role: 'user',
  // ...
};

HS256방식(대칭키 방식)을 예로 들면, 서명 부분은 아래와 같이 비밀키를 사용하여 암호화된 문자열을 base64 형식으로 다시 인코딩하는데, 이 secretKey를 알아야만 서명 및 검증이 가능하다.

HMACSHA256(
  base64url(header) + "." + base64url(payload),
  secretKey
)

이 때, secretKey는 충분히 긴 uuid같은 문자열이며, bash 명령어를 통해 아래와 같이 간단하게 생성할 수 있다. (비대칭키 방식은 다름)

openssl rand -base64 32

이렇게 구성되는 JWT는 jsonwebtoken같은 라이브러리를 사용해 쉽게 생성, 검증할 수 있다.

const jwt = require('jsonwebtoken');

// 🔐 대칭키 방식: 비밀키 (서버에만 보관해야 함)
const secretKey = 'my-super-secret-key-1234567890';

const payload = {
  userId: 'abc123',
  role: 'user',
};

// ✍️ JWT 서명
const token = jwt.sign(payload, secretKey, {
  algorithm: 'HS256',
  expiresIn: '1h', // 1시간 후 만료
});

console.log('📦 생성된 JWT:', token);

// ✅ JWT 검증
try {
  const decoded = jwt.verify(token, secretKey);
  console.log('✅ 토큰 유효. 내용:', decoded);
} catch (err) {
  console.error('❌ 토큰 검증 실패:', err.message);
}

JWT가 암호화된 데이터라고 오해하는 사람들이 많은데,
JWT는 단순히 base64 형식으로 인코딩된 문자열일 뿐이기 때문에, 누구나 그 내용을 디코딩해서 볼 수 있다.
하지만, Header와 Payload, secretKey를 가지고 서명된 Signature부분은 암호화되어 있기 때문에, 이 Signature부분을 해독하여 해당 JWT가 위/변조 되지 않은(처음 발급한 JWT의 내용이 그대로 담겨있는지) 것인지 확인할 수 있어 사용하는 것이다.

JWT를 사용한 사용자 인증(인가) 예시 동작흐름

1. 사용자 로그인 요청

  • 사용자가 아이디/비밀번호를 입력하고 서버에 로그인 요청을 보냄.

2. 서버에서 사용자 인증

  • 서버가 아이디/비밀번호를 검증하여 인증 성공 시, 서버는 사용자 정보를 바탕으로 JWT(JSON Web Token)를 생성
    (JWT 토큰은 보통 만료시간(exp)을 포함하여 생성됨)

3. 서버가 응답에 JWT를 포함하여 클라이언트에 전달

  • 쿠키방식: 응답 헤더의 Set-Cookie에 JWT 토큰 설정 (HttpOnly, Secure 설정 권장)
  • 클라이언트에서 직접 저장 방식: 응답 본문(JSON 등)에 JWT 토큰 전달

4. 클라이언트가 JWT를 저장

  • 쿠키 방식: 브라우저가 자동으로 쿠키에 저장함.
  • 클라이언트에서 직접 저장 방식: 로컬스토리지나 세션스토리지에 저장.

5. 클라이언트가 이후 요청 시 JWT를 포함

쿠키 방식: 브라우저가 요청에 자동으로 Cookie: token=... 포함
로컬스토리지 저장 시: 클라이언트 코드가 Authorization: Bearer <JWT> 헤더를 직접 추가해서 요청

6. 서버가 JWT를 검증하여 사용자 인증 수행

  1. 서버는 요청에서 JWT 토큰을 추출 (쿠키 혹은 Authorization 헤더)
  2. 비밀키로 JWT 서명을 검증해 토큰 위변조 확인
  3. 토큰의 만료시간(exp) 등 유효성 검증
  4. 토큰이 정상이라면, 토큰 내 페이로드 정보(예: userId)로 사용자 상태 파악

7. JWT가 유효하지 않거나 만료되었을 경우

  • 서버는 401 Unauthorized 혹은 적절한 에러 반환
profile
깊이 공부하는 웹개발자

0개의 댓글