Language/JavaScript

[JavaScript] 쿠키(Cookie) 어떻게 다루는가?

DevL1 2023. 7. 13. 23:15

쿠키는 브라우저에 저장되는 작은 크기의 문자열로, RFC 6265 명세에서 정의한 HTTP 프로토콜의 일부입니다.

서버가 HTTP 응답 헤더(header)의 Set-Cookie에 내용을 넣어 전달하면, 이 내용을 자체적으로 브라우저에 저장합니다. 브라우저는 사용자가 쿠키를 생성하도록 한 동일 서버(사이트)에 접속할 때마다 쿠키의 내용을 Cookie 요청 헤더에 넣어서 함께 전달합니다.

  • 사용자가 로그인하면 서버는 HTTP 응답 헤더의 Set-Cookie에 담긴 세션 식별자 정보를 사용해 쿠키를 설정합니다.
  • 사용자가 동일 도메인에 접속하려고 하면 브라우저는 HTTP Cookie 헤더에 인증 정보가 담긴 세션 식별자을 함께 실어 서버에 요청을 보냅니다.
  • 서버는 브라우저가 보낸 요청 헤더의 세션 식별자를 읽어 사용자를 식별합니다.

 

위 설명 보고 울지 마세요

쿠키와 세션의 차이는?

쿠키는 사용자가 브라우저의 쿠키 데이터를 비우거나 금지하지 않는다면 계속해서 유지가 되는 반면, 세션의 경우 방문자가 접속해 세션이 발생하고 지정된 시간이 지나게 되면 세션은 만료된다는 점이 있습니다.

 

간단한 쿠키의 접근

Cookie read

document.cookie는 name=value 쌍으로 구성되어 있고, 각 쌍은 세미콜론(;)으로 구분한다.

이때, 쌍 하나는 하나의 독립된 쿠키를 나타낸다. 세미콜론을 기준으로 document.cookie의 값을 분리하면 원하는 쿠키를 찾을 수 있다.

alert( document.cookie ); // cookie1=value1; cookie2=value2;...

Cookie write

document.cookie = "name=banana"; // 이름이 'name'인 쿠키의 값만 갱신함
alert(document.cookie); // 모든 쿠키 보여주기

코드를 실행하면 여러 개의 쿠키가 출력되는 것을 확인할 수 있습니다.

이를 통해 document.cookie= 연산은 모든 쿠키를 덮어쓰지 않고, 명시된 쿠키인 name의 값만 갱신한 것을 알 수 있습니다.

 

쿠키의 이름과 값엔 특별한 제약이 없기 때문에 모든 글자가 허용됩니다.

하지만 형식의 유효성을 일관성 있게 유지하기 위해 반드시 내장 함수 encodeURLComponent를 사용하여 이름과 값을 이스케이프 처리해 줘야 합니다.

// 특수 값(공백)은 인코딩 처리해 줘야 합니다.
let name = "my name";
let value = "Dev Banana";

// 인코딩 처리를 해, 쿠키를 my%20name=Dev%20Banana 로 변경하였습니다.
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);

alert(document.cookie); // ...; my%20name=Dev%20Banana

 

! 쿠키의 한계
encodeURLComponent로 인코딩한 이후의 name=value 쌍은 4KB를 넘을 수 없습니다. 이 용량을 넘는 정보는 쿠키에 저장할 수 없습니다.

도메인 하나당 저장할 수 있는 쿠키의 개수는 20여 개 정도로 한정되어 있습니다.
개수는 브라우저에 따라 조금씩 다릅니다.

 

Cookie Options

 

Path

path=/mypath

URL path(경로)의 접두사로, 이 경로나 이 경로의 하위 경로에 있는 페이지만 쿠키에 접근할 수 있습니다. 절대 경로이어야 하고, (미 지정 시) 기본값은 현재 경로입니다.

path=/admin 옵션을 사용하여 설정한 쿠키는 /admin과 /admin/something에선 볼 수 있지만, /home이나 /adminpage에선 볼 수 없습니다.

특별한 경우가 아니라면, path 옵션을 path=/같이 루트로 설정해 웹사이트의 모든 페이지에서 쿠키에 접근할 수 있도록 합시다.

 

Domain

domain=site.com

쿠키에 접근 가능한 도메인을 지정할 수 있습니다. 다만 제약이 있어 아무 도메인이나 지정할 수 없습니다.

 

domain 옵션에 아무 값도 넣지 않았다면, 쿠키를 설정한 도메인에서만 쿠키에 접근할 수 있습니다. site.com에서 설정한 쿠키는 other.com에서 얻을 수 없습니다.

// site.com에서 쿠키를 설정함
document.cookie = "name=banana"

// site.com의 서브도메인인 forum.site.com에서 user 쿠키에 접근하려 함
alert(document.cookie); // 찾을 수 없음

 

expires와 max-age

유효일자(expires), 만료기간(max-age) 옵션이 지정되어 있지 않으면, 브라우저가 닫힐 때 쿠키도 함께 삭제됩니다.

이런 쿠키를 "세션 쿠키(Session Cookie)"라고 부릅니다.

 

expires 나 max-age 옵션을 설정하면 브라우저를 닫아도 쿠키가 삭제되지 않습니다.

expires=Thu, 13 Jul 2025 22:46:35 GMT

브라우저는 설정된 유효 일자까지 쿠키를 유지하다가, 해당 일자가 도달하면 쿠키를 자동으로 삭제합니다.

반드시 GMT(Greenwich Mean Time) 포맷으로 설정해야 합니다.

 

해당, date.toUTCString 내장 함수를 사용하여 포맷을 쉽게 변경할 수 있습니다

// 지금으로부터 하루 후
let date = new Date(Date.now() + 86400e3);
date = date.toUTCString();
document.cookie = "name=banana; expires=" + date;

expires 옵션 값을 과거로 지정하면 쿠키는 삭제됩니다.

 

max-age=3600

max-age는 expires 옵션의 대안으로, 쿠키 만료 기간을 설정할 수 있게 해 줍니다. 현재부터 설정하고자 하는 만료일시기까지의 시간을 초로 환산한 값을 설정합니다.

 

0이나 음수 값을 설정하면 쿠키는 바로 삭제됩니다.

// 1시간 뒤에 쿠키가 삭제됩니다.
document.cookie = "name=banana; max-age=3600";

// 만료 기간을 0으로 지정하여 쿠키를 바로 삭제함
document.cookie = "name=banana; max-age=0";

 

secure

이 옵션을 설정하면 HTTPS로 통신하는 경우에만 쿠키가 전송됩니다.

 

secure 옵션이 없으면 기본 설정이 적용되어 https://site.com에서 서 생성한 쿠키를 https://site.com에서 읽을 수 있고, htt [s://site.com에서 생성한 쿠키도 https://site.com에서 읽을 수 있습니다.

 

쿠키는 기본적으로 도메인만 확인하지 프로토콜을 따지진 않기 때문입니다.

 

하지만 secure 옵션이 설정된 경우, https://site.com에서 설정한 쿠키는 https://site.com에서 설정한 쿠키는 http://site.com에서 접근할 수 없습니다. 쿠키에 민감한 내용이 저장되어 있어 암호화되지 않은 HTTP 연결을 전달되는 걸 원치 않는다면 이 옵션을 사용하면 됩니다.

// (https:// 로 통신하고 있다고 가정 중)
// 설정한 쿠키는 HTTPS 통신시에만 접근할 수 있음
document.cookie = "name=banana; secure";

 

samesite

또 다른 보안 속성인 samesite 옵션은 크로스 사이트 요청 위조(cross-site request forgery, XSRF) 공격을 막기 위해 만들어진 옵션입니다. 쿠키의 samesite 옵션을 이용하면 “XSRF 보호 토큰” 없이도 이론상으로 크로스 사이트 요청 위조를 막을 수 있습니다.

 

  • "None"은 동일 사이트와 크로스 사이트 모두에 쿠키 전송이 가능합니다.
  • "Strict"는 서로 다른 도메인에서는 아예 전송이 불가능해지기 때문에 CSRF를 100% 방지할 수 있으나 사용자 편의성을 해친다.
  • "Lax"는 Strict 설정에 일부 예외를 두어 HTTP get method /  a href / link href 와 같은 특수한 경우에만 쿠키를 전송한다.

samesite 설정 - JavaScript

document.cookie = "safeCookie1=foo; SameSite=Lax";
document.cookie = "safeCookie2=foo";

Samesite를 "None" 으로 설정할 경우 쿠키에 암호화된 HTTPS 연결이 필요함을 나타내는 Secure 속성을 태깅해야 합니다.

document.cookie = "crossCookie=bar; SameSite=None; Secure";

 

httpOnly

이 옵션은 자바스크립트와 전혀 관계가 없지만, 쿠키를 보호 하는데 있어 중요한 역할을 하기 때문에 간단하게 언급 하겠습니다.

 

httpOnly 옵션이 설정된 쿠키는 document.cookie로 쿠키 정보를 읽을 수 없기 때문에 쿠키를 보호할 수 있습니다. httpOnly 옵션은 웹서버에서 Set-Cookie 헤더를 이용해 쿠키를 설정할 때 지정할 수 있습니다.

 

이 옵셥은 클라이언트 측 스크립트에서 document.cookie를 통해 쿠키를 볼 수도 없고 조작할 수도 없게합니다.

 

악의적인 스크립트 코드를 페이지에 삽입하고 사용자가 그 페이지에 접속하기를 기다리는 방식의 공격을 예방할 때 이 옵션을 사용합니다.

 

 

이제 끝났습니다

 

 

유용한 함수들

getCookie(name)

쿠키에 접근하는 가장 짧은 방법은 정규 표현식을 사용하는 것입니다.
아래 getCookie(name) 함수는 주어진 name의 쿠키를 반환합니다.
function getCookie(name) {
  let matches = document.cookie.match(new RegExp(
    "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
  ));
  return matches ? decodeURIComponent(matches[1]) : undefined;
}

 

setCookie(name, value, options)

현재 경로(path=/)를 기본으로, 주어진 name과 value를 가진 쿠키를 설정합니다.
다른 기본 값을 추가할 수 있습니다.
function setCookie(name, value, options = {}) {

  options = {
    path: '/',
    // 필요한 경우, 옵션 기본값을 설정할 수도 있습니다.
    ...options
  };

  if (options.expires instanceof Date) {
    options.expires = options.expires.toUTCString();
  }

  let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);

  for (let optionKey in options) {
    updatedCookie += "; " + optionKey;
    let optionValue = options[optionKey];
    if (optionValue !== true) {
      updatedCookie += "=" + optionValue;
    }
  }

  document.cookie = updatedCookie;
}

// Example of use:
setCookie('user', 'John', {secure: true, 'max-age': 3600});

 

deleteCookie(name)

만료 기간을 음수로 설정하면 쿠키를 삭제할 수 있습니다.
function deleteCookie(name) {
  setCookie(name, "", {
    'max-age': -1
  })
}

갱신이나 삭제는 동일한 Domain과 Path에서만 해야 합니다.

 

 

 

 

 

 

 

https://ko.javascript.info/cookie

 

쿠키와 document.cookie

 

ko.javascript.info