브라우저에 데이터 저장하기

345·2023년 11월 19일

🍪 쿠키

쿠키는 HTTP 프로토콜의 일부로 요청 헤더에 함께 넣어 보내지는 문자열인데요,
정보를 name=value 형태로 저장하여 가지고 있습니다.

쿠키는 다음과 같은 과정으로 만들어져 주고받습니다.

  1. 서버가 응답 헤더의 Set-Cookie 에 쿠키 내용을 넣어 전달
  2. 브라우저가 이 내용을 브라우저 자체에 저장 ➡️ 이게 바로 쿠키!
  3. 쿠키를 준 동일한 도메인에 접속하면, 브라우저가 Cookie 헤더에 쿠키를 넣어 요청을 보냄

일단 서버가 Set-Cookie 로 쿠키를 보내주면,
브라우저는 이걸 저장했다가, 동일한 서버에 요청할 때 Cookie 헤더에
그 서버로부터 받은 쿠키를 넣어 보내주는 것이죠.


// Set-Cookie 헤더
Set-Cookie: <cookie-name1>=<cookie-value1>; <cookie-name2>=<cookie-value2>

// Cookie 헤더
Cookie: <cookie-name1>=<cookie-value1>; <cookie-name2>=<cookie-value2>

위처럼... name=value 로 이뤄진 값을 ; 으로 나누어 나열한 문자열이
바로 쿠키입니다.


쿠키 읽기

document.cookie 프로퍼티로 브라우저에서 쿠키에 접근할 수 있습니다.
브라우저가 다루는 쿠키의 특징은 다음과 같습니다.

  • name-value 쌍으로 구성됨
  • 각 쌍은 ; 로 구분
  • 쌍 하나는 하나의 독립된 쿠키
  • name=value 쌍은 4KB 용량을 넘으면 안 됨
  • 도메인 하나 당 저장 가능한 쿠키의 개수는 20여개 정도(브라우저마다 조금씩 다름)

쿠키 쓰기

document.cookie 에 직접 값을 쓰는 것도 가능합니다.
cookie 는 접근자 프로퍼티로, 값을 할당하면 접근자에 지정된 동작을 수행합니다.
바로 값을 받아 쿠키를 갱신하는 것이죠. 다른 쿠키의 값은 변경되지 않습니다.

document.cookie = "user=John";

위를 수행하면 document.cookieuser=John 이라는 쿠키가 하나 추가됩니다.


쿠키의 이름과 값엔 제약이 없지만, 형식을 일관성있게 유지하기 위해
내장함수 encodeURIComponent 를 사용하여 이름과 값을 이스케이프 처리해야 합니다.

// 특수 값(공백)은 인코딩 처리 해줘야 함
let name = "my name";
let value = "John Smith"

// 인코딩 처리를 해, 쿠키를 my%20name=John%20Smith 로 변경
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);

alert(document.cookie); // ...; my%20name=John%20Smith

📌 쿠키 옵션

쿠키에 옵션을 지정할 수 있습니다.
옵션 또한 name=value 형태로 지정되고, ; 로 구분되어 나열됩니다.
옵션은 메인 정보인 key=value 뒤에 ; 로 구분하여 나열합니다.

document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"

user=John 이라는 쿠키에 각종 옵션을 적용한 모습입니다.
이제 어떤 옵션이 있는지 알아봅시다.


path

  • path=/mypath

URL path 로, 이 경로나 이 경로의 하위 경로에 있는 페이지만
쿠키에 접근 가능하도록 합니다.

절대 경로여야 하며, 미 지정시 기본값은 현재 경로입니다.
보통은 path=/ 처럼 루트로 설정하여 해당 사이트의 모든 페이지에서
쿠키에 접근 가능하도록 합니다.


domain

  • domain=site.com

쿠키에 접근 가능한 도메인을 지정합니다. (그 도메인에 요청 시 쿠키를 보냄)
미지정시 쿠키를 설정한 (Set-Cookie 한) 사이트에서만 접근 가능합니다.
그러면 그의 서브 도메인이나, 다른 도메인에서는 쿠키에 접근할 수 없습니다.

서브 도메인에서도 접근하게 하려면❓

domain 옵션을 명시적으로 설정해주면, 서브 도메인의 어디서든
쿠키에 접근할 수 있습니다.

// site.com에서
// 서브 도메인(*.site.com) 어디서든 쿠키에 접속하게 설정
document.cookie = "user=John; domain=site.com"

// 이렇게 설정하면 forum.site.com와 같은 서브도메인에서도 쿠키 접근 가능

expires 와 max-age

  • expires=Tue, 19 Jan 2038 03:14:07 GMT
    • GMT 포맷으로 설정해야 함 (date.toUTCString 으로 포맷 설정)
  • max-age=3600

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

expires 에는 일자를 명시하는데, 그럼 브라우저가 유효 일자까지
쿠키를 유지하다가, 해당 일자에 자동으로 쿠키를 삭제합니다.

max-age 는 현재로부터 만료일시까지의 시간을 초로 환산한 값을 넘겨,
그 시간이 지나면 쿠키를 삭제합니다.

// 1시간 뒤에 쿠키 삭제
document.cookie = "user=John; max-age=3600";

secure

  • secure

이 옵션이 있으면 HTTPS 통신인 경우에만 쿠키를 전송합니다.
옵션이 없다면 HTTP 로 접속해도, HTTPS 여도 모두 쿠키에 접근 가능합니다.

// 설정한 쿠키는 HTTPS 통신시에만 접근할 수 있음
document.cookie = "user=John; secure";

samesite

  • samesite

이 옵션은 크로스 사이트 요청 위조 공격(XSRF)을 막기 위해 만들어졌습니다.
A 사이트에서 B 사이트로 크로스 사이트 요청을 보낼 때, 쿠키 전송을 막기 위함입니다.

XSRF 공격❓

만약 bank.com 이라는 사이트에서 인증 쿠키를 줬다고 합시다.
브라우저는 이 쿠키를 저장했다가 bank.com 에 요청할 때 쿠키를 보내서
서버가 사용자를 식별하도록 했습니다.

근데, 어쩌다 보니 우리가 해커가 만든 사이트에 들어갔습니다.
이 사이트는 bank.com 으로 요청을 보내어 해커의 계좌에 돈을 송금하도록 합니다.
그럼 브라우저가 bank.com 으로 요청을 보낼 때 인증 쿠키를 같이 보내고,
서버는 해커가 아니라 사용자 본인인 줄 알고 요청을 처리하죠.

이런 공격을 크로스 사이트 요청 위조라고 합니다.


samesite 옵션에는 두 가지 값을 설정할 수 있습니다.

  • samesite=strict: 기본 상태. 그냥 samesite 만 설정해도 동일하게 동작
    • 해당 도메인이 아니라 제 3의 도메인에서 요청이 이뤄지면, 쿠키 전송 ❌
    • 요청용 링크를 따로 보관했다가 클릭하면(다른 도메인에서 보낸 요청이라) 쿠키 전송이 안 됨
  • samesite=lax: strict 와 같지만, 다음 조건을 동시에 만족하면 쿠키 전송 ⭕
    1. 안전한 HTTP 메서드인 경우 (읽기 작업만 수행, 데이터 교환, 변경 등은 하지 않음)
      (ex. GET 메서드인 경우)
    2. 작업이 최상위 레벨 탐색에서 이뤄질 때 (브라우저 주소창에서 URL 이 변경되는 등)
      (ex. 특정 URL 로 이동하기)
    • 외부 사이트에서 AJAX 요청을 보내거나, 폼을 전송하는 등의 작업에는 쿠키 전송 ❌

lax 는 사용자 경험을 위해 나온 느슨한 버전의 samesite=strict 입니다.


httpOnly

  • httpOnly

이 옵션은 서버에서 Set-Cookie 헤더로 쿠키를 설정할 때 지정 가능합니다.
httpOnly 를 설정하면, 클라이언트(브라우저...) 에서 쿠키를 읽고 쓸 수 없습니다.

즉, document.cookie 로 쿠키 정보를 읽을 수 없게 되어 쿠키가 보호됩니다.


GDPR

쿠키를 이용해서 사용자를 추적, 식별하는 작업은
개인 정보 보호를 위해 사용자에게 허가를 얻어야만 가능합니다.

EU 에서는 이를 GDPR 법령으로 강제하고 있습니다.

인증 세션 쿠키 사용이나 쿠키 id 추적 등의 기능을 만들고 싶다면
그 전에 사용자의 허가를 받아야 합니다.


✅ localStorage 와 sessionStorage

웹 스토리지 객체인 localStoragesessionStorage
브라우저 내에 key-value 쌍을 저장할 수 있습니다.

페이지를 새로 고침하거나 브라우저를 다시 실행하면
계산한 데이터가 날아가는데, storage 에 저장한 정보는 사라지지 않고 남아있습니다.

  • sessionStorage: 페이지 새로 고침해도 보존 (브라우저를 종료하면 사라짐)
  • localStorage: 브라우저 다시 실행해도 보존

두 스토리지 객체는 동일한 메서드와 프로퍼티를 제공합니다.

  • setItem(key, value): 키-값 쌍을 보관
  • getItem(key): 키에 해당하는 값을 반환
  • removeItem(key): 키에 해당하는 값과 키를 삭제
  • clear(): 모든 것을 삭제
  • key(index): 인덱스(index)에 해당하는 키를 반환
  • length: 저장된 항목의 개수를 반환

쿠키와 다른 점

스토리지 객체는 쿠키처럼 정보를 저장하지만, 쿠키와는 다릅니다.
차이점은 다음과 같습니다.

  • 쿠키는 HTTP 요청 시 서버로 전송되지만, 스토리지 객체는 전송 ❌
    • 그래서 스토리지는 용량이 더 큼. 브라우저는 최소 2MB 를 지원함
  • 서버가 HTTP 헤더로 조작할 수 없음. 자바스크립 내에서만 스토리지 조작 가능
  • 웹 스토리지 객체는 오리진에 묶여있어, 오리진이 다르면 데이터에 접근 ❌

localStorage 특징

localStorage 의 특징은 다음과 같습니다.

  • 오리진이 같은 경우 모든 탭과 창에서 데이터 공유
  • 브라우저나 OS 가 재시작해도 데이터 보존
  • 이터러블 객체가 아님
    • 그래도 lengthkey(index) 등의 메서드로 전체 순회 가능
    • Object.keys(localStorage) 로 전체 키를 받아온 후 getItem(key) 로 순회하는 방법도 있음
  • 키와 값은 문자열이어야 함
    • 다른 자료형을 사용하면 문자열로 자동 변환하여 저장

sessionStorage 특징

sessionStoragelocalStorage 와 동일한 메서드와 프로퍼티를 제공하지만,
기능이 제한적이라 자주 사용되진 않습니다.

sessionStorage 의 특징은 다음과 같습니다.

  • 현재 떠 있는 탭 내에서만 데이터 유지
    • 같은 페이지라도 다른 탭이면 다른 스토리지에 저장
  • 페이지를 새로 고침해도 데이터 보존
  • 브라우저나 탭을 종료하고 새로 열면 데이터 사라짐
    • 오리진뿐만 아니라 브라우저 탭에도 종속됨

storage 이벤트

스토리지 데이터가 갱신되면(데이터 추가, 삭제, 변경 등) storage 이벤트가 실행됩니다.
이벤트는 다음과 같은 프로퍼티를 지원합니다.

  • key: 변경된 데이터의 키(.clear()를 호출했다면 null)
  • oldValue: 이전 값(새로 추가된 키라면 null)
  • newValue: – 새로운 값(키가 삭제되었다면 null)
  • url: 갱신이 일어난 문서의 url
  • storageArea: 갱신이 일어난 스토리지 객체

❗ storage 이벤트는 이벤트를 발생시킨 스토리지를 제외, 스토리지에서 접근 가능한 window 객체 전부에서 일어납니다.

이건 스토리지를 공유하는 모든 문서가 한 문서에서 일어난 스토리지의 갱신을 알 수 있다는 뜻입니다.

어떤 문서에서 스토리지가 갱신되면, 그와 같은 스토리지를 공유하는 다른 모든 문서의
window 객체에서 storage 이벤트가 발생합니다.
물론 이 갱신이 직접 일어난 문서가 어딘지는 event.url 로 알아낼 수 있고요.

이 특징을 이용하여 오리진이 같은 창끼리 싱크를 맞추거나 통신하도록 할 수 있습니다.

profile
기록용 블로그 + 오류가 있을 수 있습니다🔥

0개의 댓글