웹 스토리지와 쿠키 + 개발자 도구 Storage + 전역 상태 관리

하머·2022년 9월 30일
0
post-thumbnail
post-custom-banner

🤔 앞서서...

이전 포스팅에서 HTTP의 무상태성 이므로 저장소(로컬/세션 스토리지, 쿠키)가 필요하다고 했는데 이 저장소들은 무엇이고, 어떻게/왜/언제 쓰는지, 각자 장단점은 어떠한지 알아보고자 한다. 이와 더불어 크롬 개발자 도구의 Application탭의 storage와 프론트엔드 전역 상태 관리 라이브러리들은 상태를 어떻게 저장하는지 알아 보도록 한다.

1. HTTP의 무상태성(Stateless)과 비 연결성(Connectionless)

  • HTTP는 서버가 클라이언트의 상태를 보존하지 않는 무상태성과 연결을 유지하지 않는 비연결성이라는 특징을 지니고 있다.
  • 무상태성 - Stateless
    • 장점: 상태를 보관하지 않음으로써 한서버에만 유지되지 않고 다른 서버로부터도 데이터를 받아올 수 있음
    • 단점: 상태를 저장하지 않으므로 클라이언트는 추가 데이터를 전송 해야함(인증 정보)
  • 비 연결성 - Connectionless
    • 장점: 서버와의 연결을 계속 유지하는 방법 대비 서버는 연결을 유지하지 않는 다면 최소한의 자원을 사용하도록 하여 서버를 효율적으로 운용할 수 있다.
    • 단점: TCP/IP 연결을 계속 새로 맺어야하고 JS, CSS, Image와 같은 수많은 자원이 함께 다운로드됨 => 자원을 각각 보낼때마다 연결을 끊고 다시 연결하는 것은 비효율 적이므로 연결이 이루어지고 난 뒤 각각의 자원들을 요청하고 모든 응답이 돌아온 후 연결을 종료하는 HTTP 지속 연결(Persistent Connections)로 해결하였다.
  • 이러한 무상태성과 비연결성 때문에 클라이언트는 유저정보를 갖고 같은 유저임을 인증받을 정보를 저장하는 저장소가 필요해졌다 => 웹 스토리지, 쿠키에 저장

2. 개발자 도구 애플리케이션탭의 Storage

((크롬기준), 개발자도구-application-storage)

  • 종류
    • 로컬 스토리지(Local Storage) *
    • 세션 스토리지(Session Storage) *
    • 쿠키(Cookies) *
    • IndexedDB
      • 파일이나 블롭 등 많은 양의 구조화된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API
      • 웹 스토리지는 적은 양의 데이터를 저장하는데 유용하고, IndexedDB는 많은 양의 구조화된 데이터에 적합하다.(최대저장공간은 사용가능한 디스크 공간의 50%)
      • JS 기반의 객체지향 데이터베이스, 인덱스 키를 사용해 저장 및 검색할 수 있음
      • DB 오픈 => 객체 저장소 생성 => 트랜잭션 시작 및 DB 작업 요청 => 이벤트 리스너로 요청 완료까지 기다림(비동기) => Do Something
    • Web SQL
      • IndexDB와 비슷한 많은 양의 구조화된 데이터를 넣기 위한 저장소
      • IndexDB와의 차이점은 SQL이라는 이름에서 보이듯 관계형 저장소이다.
      • 더이상 사용이 권장되지 않는다
    • Trust Tokens
      • 출처가 신뢰하는 사용자에게 암호화 토큰을 발급해주어 추적없이 스캠을 방지하고 사람과 봇을 구별하는데 도움을 주는 크로미움 프로젝트의 일부
      • 브라우저에 저장되어 사용자를 식별해 타 사이트와 ID를 연결하지 않고 사용자의 신뢰를 다른 웹사이트로 전달 할 수 있다.
      • (https://developer.chrome.com/docs/privacy-sandbox/trust-tokens/)
    • Interest Groups
      • 크로미움의 FLEDGE API(https://developer.chrome.com/ko/docs/privacy-sandbox/fledge/)에 사용되는 저장소
      • FLEDGE API는 사용자가 제품이나 서비스를 광고하려는 사이트의 페이지를 방문하면 사이트는 사용자의 브라우저에 특정 기간 동안 특정 관심그룹(Interest Groups)과 사용자를 연결하도록 요청할수 있는데 그 그룹을 저장하는 저장소이다.
      • FLEDGE의 작동 방법: 온라인 신발 판매사이트 방문 => Interest Group에 호스트이름(신발 사이트), 입찰 로직, 입찰 신호 액세스 URL 데이터 등록 => 신발 광고를 게재하는 뉴스 사이트 방문 => 광고 슬롯에 들어갈 광고를 선택하기 위해 '경매'를 실행해 낙찰된 광고를 렌더링하여 광고 표시

3. 쿠키

  • 주로 서버에서 세팅해주고, 클라이언트는 요청 시 Headers에 전송
  • 같은 도메인에서 만들어진 쿠키만 전송하게 됨 => 서버 도메인이 다를 시 서버에서 클라이언트의 쿠키 설정이 되지않는다
  • 만료 기간을 설정 가능하다
Expires=<date\> 혹은 Max-Age=<number\>
  • 만료기간이 있어서 기간이 끝난다면 삭제되는 영구 쿠키와 만료기간이 없이 브라우저가 종료되면 삭제되는 세션 쿠키로 나뉨
  • 문제점
    • CSRF(Cross Site Request Forgery): 사용자의 권한을 이용한 공격(비밀번호 변경, 결제 요청)
      • SameSite 옵션으로 같은 도메인의 요청에만 쿠키를 전송하도록 한다.
      • Referer 검증으로 요청 온 사이트의 도메인을 확인할 수 있다.
    • XSS(Cross-Site Scripting): 사용자의 민감한 정보(토큰) 탈취
      • HttpOnly 옵션으로 자바스크립트를 막아 해결 가능
    • 부족한 저장 용량(4KB)
    • 모든 HTTP 요청 헤더에 담겨 전송되므로 불필요한 트래픽이 증가한다.
  • 다시보지 않기 팝업 창, n일 동안 보지않기 등으로 쓰인다.

4. 웹 스토리지

  • 쿠키의 단점을 보완한 HTML5에 등장한 저장소
  • 쿠키의 단점 보완
    • 5MB의 큰 저장 용량
    • 요청 시 Headers에 전송하지 않아 트래픽 증가 문제 해결
    • 문자열만 저장할 수 있는데 JSON형태로 직렬화 할 경우 객체 및 배열 저장 가능
// 잘못 저장하는 예시
localStorage.setItem("obj", {a:1,b:2});
console.log("obj"); // '[object Object]'

// JSON으로 직렬화 하여 저장 하는 예시
localStorage.setItem("JSON", JSON.stringify({key1: '과자', key2: 123}));
console.log(JSON.parse(localStorage.getItem("JSON"))); // parse로 역직렬화
/*
{key1: '과자', key2: 123}
key1: "과자"
key2: 123
[[Prototype]]: Object
*/
  • 브라우저를 종료해도 데이터가 유지되는 로컬 스토리지와 브라우저의 탭마다 개별적으로 데이터가 저장되어 탭이 종료 시 삭제되는 세션 스토리지로 나뉜다.
  • 주의점: 사파리 시크릿 모드 같은 웹 스토리지가 비활성화 된 경우를 찾아 에러 처리를 해줘야 한다.
function storageAvailable(type) {
    let storage;
    try {
        storage = window[type];
        const x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
    }
    catch (e) {
        return e instanceof DOMException && (
            // Firefox를 제외한 모든 브라우저
            e.code === 22 ||
            // Firefox
            e.code === 1014 ||
            // 코드가 존재하지 않을 수도 있기 떄문에 이름 필드도 확인합니다.
            // Firefox를 제외한 모든 브라우저
            e.name === 'QuotaExceededError' ||
            // Firefox
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
            // 이미 저장된 것이있는 경우에만 QuotaExceededError를 확인하십시오.
            (storage && storage.length !== 0);
    }
}

/*
if (storageAvailable('localStorage')) {
  // localStorage 사용가능
}
else {
  // localStorage 사용불가
}
세션 스토리지는 storageAvailable('sessionStorage')
*/
// 

mdn - Web Storage API 사용하기

  • 웹 스토리지의 문제점
    • XSS - 자바스크립트로 접근 가능함
      • 사용자의 입력이 자바스크립트 코드로 실행되지 않도록 innerHTML를 사용하지 않는다면 해결 가능하다.
      • 만일 innerHTML를 사용한다면 XSS보안 라이브러리(sanitize-html, DOMPurify)를 사용한다.
      • React의 경우에는 기본적으로 막혀 있지만 dangerouslySetInnerHTML 같은 방법으로 넣을 수 있는데 XSS를 막기 위해 사용해선 안된다.
    • 독립된 스토리지 - 브라우저/탭(세션 스토리지) 간 공유 불가 ex) 다른 브라우저, 모바일/데스크탑
    • 만료 기간 설정 불가
    • 동기적으로 실행 되므로 용량이 커지면 저장되는 동안 메인 스레드를 막는다
      • 용량이 크다면 IndexedDB를 사용
  • 세션 스토리지의 경우에는 이전 페이지 저장, 이전 스크롤 위치 저장, 비로그인 장바구니 등의 저장소로 쓰인다.
  • 로컬 스토리지의 경우에는 사용자 설정 저장, 자동 로그인, 글 임시 저장 등의 저장소로 쓰인다.

5. 전역 상태 관리 라이브러리와의 차이점

  • 그렇다면 웹 저장소들과 React, Vue 같은 라이브러리에서 쓰이는 전역 상태 관리 라이브러리의 차이점은 뭘까?
    1. 저장 공간
      • 웹 스토리지 와 쿠키: 디스크에 저장 (브라우저가 저장된 폴더의 내부 폴더 안에 저장됨)
      • 상태 관리 라이브러리: 메모리에 저장 (RAM에 저장)
      • 저장 공간이 다르므로 속도 또한 다르다
    2. 지속성
      • 웹 스토리지 와 쿠키: 브라우저에 계속 남아있고 브라우저/탭 단위로 값을 저장한다.
      • 상태 관리 라이브러리: 메모리에 남아 있으므로 휘발되어 지속되지 않고, 새로고침, 페이지 닫기, 태그 변경 등이 있을 때 값이 삭제되거나 삭제되고 다시 생성한다. => 웹 스토리지와 연계해주는 라이브러리(redux-persist, recoil-persist)를 사용하거나 직접 로컬/세션스토리지에 저장하는 함수를 작성하여 해결 가능
    3. React, Vue와 같은 UI 프레임워크의 상태 관리
      • 웹 스토리지는 말그대로 저장소 그 자체이다. 저장만 할 뿐이지 상태관리에서처럼 상태가 바뀌었을 때의 효과 즉 SideEffect가 없다.
      • Redux와 MobX, VueX는 Flux 패턴, Recoil은 atom과 selector 등으로 상태를 저장 후 값을 변경하고 React와 Vue 값이 변경되는 것을 참조해 DOM을 리렌더링 한다. 그 에 반해 웹 스토리지와 쿠키로 리렌더링 하기 위해서는 이것만으로 변경이 되지 않고 React와 Vue 안에서 SideEffect를 만들어 주어야 한다.

6. 마치며

  • 이전에 dangerouslySetInnerHTML를 사용했던 기억이 있는데 XSS 때문에 사용해서는 안된다.
  • 결국 웹스토리지와 쿠키는 각자의 특성을 가진 저장소 이므로 적재적소에 사용해야 한다.
  • 평소에도 개발자도구의 application-storage 탭을 자주 사용했는데 웹스토리지와 쿠키 외의 것들이 궁금했었는데, 이번 기회에 전부 구글링하여 정리하니 궁금증이 해결되었고, 사용처를 알았으니 이후에 사용할 기회가 생긴다면 사용할 수 있을 것 같다.
  • 예전 프로젝트에서 새로고침을 하면 값이나 컴포넌트가 이상하게 나오곤 했는데, 5번 자료조사에서 전역상태 지속성 이라는 개념을 알았고 전역 상태를 localStorage에 저장해야 한다는걸 깨달았다.
  • 개발 환경의 쿠키는 정상적으로 서버에서 잘 저장해줬지만, 배포 환경에서의 쿠키는 정상적으로 저장되지 않았던 이유가 프론트 서버와 백 서버의 도메인이 다르므로 서버 쪽에서 저장이 되지 않았다. 그 당시 CORS에러 인줄 알았으나 쿠키의 특성 때문에 되지 않았던 것이였다. 이 글을 정리하며 당시의 문제 상황을 좀더 정확히 볼수 있었다

참고자료

post-custom-banner

0개의 댓글