Cookie API 완전 이해

document.cookie의 읽기·쓰기·삭제 패턴, Secure·HttpOnly·SameSite 보안 속성, Max-Age vs Expires, 현대적 CookieStore API까지 정리합니다.

· 6 min read · PALDYN Team

지난 글에서 localStoragesessionStorage를 살펴봤습니다. 이번에는 Web Storage보다 오래되고 더 복잡한 쿠키(Cookie) API를 정리합니다. 쿠키는 서버와 클라이언트 사이에서 자동으로 주고받는다는 점에서 Web Storage와 근본적으로 다릅니다.


쿠키와 Web Storage의 차이

쿠키의 가장 큰 특징은 HTTP 요청마다 자동으로 서버에 전송된다는 것입니다. 인증 토큰, 세션 ID처럼 서버가 사용자를 식별해야 하는 데이터에 사용됩니다.

항목CookielocalStorage
서버 자동 전송OX
용량~4 KB~5–10 MB
유효기간 설정OX (직접 관리)
보안 속성HttpOnly, Secure, SameSite없음
JS 접근선택적 차단 가능항상 가능

document.cookie — 기본 API

document.cookie는 현재 페이지에서 접근 가능한 모든 쿠키를 name=value; name2=value2 형식의 단일 문자열로 반환합니다.

쓰기 (Set)

document.cookie에 대입하면 기존 쿠키를 덮어쓰는 것이 아니라 새 쿠키를 추가하거나 같은 이름의 쿠키를 업데이트합니다.

// 기본 쿠키 (세션 쿠키 — 탭 닫으면 삭제)
document.cookie = 'theme=dark';

// 속성 포함
document.cookie = 'theme=dark; Max-Age=86400; Path=/; SameSite=Lax';

// 여러 쿠키를 설정하려면 각각 대입
document.cookie = 'lang=ko; Path=/';
document.cookie = 'sidebar=open; Path=/';

읽기 (Get)

function getCookie(name) {
  const entry = document.cookie
    .split(';')
    .map(s => s.trim())
    .find(s => s.startsWith(`${name}=`));
  return entry ? entry.split('=').slice(1).join('=') : null;
}

getCookie('theme'); // 'dark'

값에 =이 포함될 수 있으므로 첫 번째 =만 분리하는 것이 안전합니다.

삭제 (Delete)

쿠키는 Max-Age=0으로 설정해 즉시 만료시키는 방식으로 삭제합니다.

function deleteCookie(name, path = '/') {
  document.cookie = `${name}=; Max-Age=0; Path=${path}`;
}

deleteCookie('theme');

중요: 삭제할 때 Path(와 Domain)를 원래 설정했던 것과 동일하게 지정해야 합니다. 다르면 다른 쿠키로 인식해 삭제되지 않습니다.


보안 속성

쿠키 구조와 보안 속성

Secure

Secure 속성이 있으면 HTTPS 연결에서만 쿠키가 전송됩니다. localhost는 예외적으로 HTTP에서도 작동합니다.

Set-Cookie: session=abc; Secure

HttpOnly

HttpOnly는 JavaScript에서 document.cookie로 해당 쿠키에 접근하지 못하게 합니다. XSS 공격으로 쿠키가 탈취되는 것을 방지합니다. 세션 토큰이나 인증 관련 쿠키에 반드시 적용합니다.

HttpOnly 쿠키는 서버가 Set-Cookie 헤더로 설정해야 하며, JavaScript에서는 설정할 수 없습니다.

SameSite

크로스 사이트 요청에서 쿠키 전송 여부를 제어해 CSRF 공격을 방어합니다.

Strict: 같은 사이트 요청에만 전송
Lax:    탑레벨 GET 요청(링크 클릭)은 허용, 그 외 크로스 사이트는 제한 (기본값)
None:   모든 크로스 사이트 요청 허용 (반드시 Secure 필요)
// JavaScript에서 SameSite 설정
document.cookie = 'pref=ko; SameSite=Lax; Path=/';

유효기간: Max-Age vs Expires

// Max-Age: 초 단위 — 권장
document.cookie = `session=abc; Max-Age=${60 * 60 * 24 * 7}`; // 7일

// Expires: UTC 날짜 문자열 — 클라이언트 시계 의존
const d = new Date();
d.setDate(d.getDate() + 7);
document.cookie = `session=abc; Expires=${d.toUTCString()}`;

// 둘 다 없으면 세션 쿠키 (브라우저 닫으면 삭제)
// Max-Age가 Expires보다 우선순위가 높음

CookieStore API

현대적 비동기 Cookie API입니다. HttpOnly 쿠키는 여전히 접근 불가지만, document.cookie 문자열 파싱 없이 쿠키를 다룰 수 있습니다. Service Worker에서도 사용할 수 있습니다.

// 쓰기
await cookieStore.set({
  name: 'theme',
  value: 'dark',
  maxAge: 60 * 60 * 24,    // 초 단위
  path: '/',
  sameSite: 'lax',
});

// 읽기 — null 대신 undefined 반환
const cookie = await cookieStore.get('theme');
console.log(cookie?.value); // 'dark'

// 전체 목록
const all = await cookieStore.getAll();

// 삭제
await cookieStore.delete('theme');

// 변경 감지
cookieStore.addEventListener('change', (e) => {
  console.log(e.changed); // 변경된 쿠키 배열
  console.log(e.deleted); // 삭제된 쿠키 배열
});

쿠키 조작 코드 패턴


인코딩

쿠키 이름과 값에 특수문자(;, ,, =, 공백 등)가 포함될 수 있다면 encodeURIComponent로 인코딩합니다.

function setCookie(name, value, days = 7) {
  const encodedName = encodeURIComponent(name);
  const encodedValue = encodeURIComponent(value);
  const maxAge = days * 24 * 60 * 60;
  document.cookie = `${encodedName}=${encodedValue}; Max-Age=${maxAge}; Path=/; SameSite=Lax`;
}

function getCookieDecoded(name) {
  const raw = getCookie(encodeURIComponent(name));
  return raw !== null ? decodeURIComponent(raw) : null;
}

정리

속성역할
Max-Age=NN초 후 만료
Path=/해당 경로 이하에서만 전송
Domain=.example.com서브도메인 포함 공유
SecureHTTPS만 전송
HttpOnlyJavaScript 접근 차단
SameSite=LaxCSRF 방어

세션 인증 쿠키에는 반드시 Secure, HttpOnly, SameSite=Lax(또는 Strict)를 조합해 사용합니다.


지난 글: localStorage · sessionStorage 완전 이해


읽어주셔서 감사합니다. 😊