Web Storage API는 무엇이고 localStorage와 sessionStorage는 언제 어떻게 써야 할까

Frontend

프론트엔드 개발을 하다 보면 브라우저 안에 무언가를 저장하고 싶은 순간이 자주 옵니다.

  • 다크모드 설정을 유지하고 싶을 때
  • 최근 본 탭 상태를 기억하고 싶을 때
  • 폼 입력 중간값을 잠깐 저장하고 싶을 때
  • 새로고침 뒤에도 일부 상태를 복원하고 싶을 때

이럴 때 가장 먼저 떠오르는 것이 보통 localStoragesessionStorage입니다.

그런데 막상 쓰려고 하면 헷갈리는 지점도 많습니다.

  • 둘은 정확히 무엇이 다른지
  • 어떤 데이터까지 넣어도 되는지
  • 객체는 어떻게 저장하는지
  • 탭을 닫으면 어떻게 되는지
  • 인증 토큰 같은 민감한 값도 넣어도 되는지

이 글에서는 Web Storage API를 아래 흐름으로 정리해보겠습니다.

  1. Web Storage API는 무엇인지
  2. localStoragesessionStorage는 무엇이 다른지
  3. 언제 어떤 데이터를 저장하면 좋은지
  4. 실무에서 자주 하는 실수는 무엇인지

한눈에 보면

먼저 짧게 정리하면 이렇습니다.

  • Web Storage API는 브라우저에 key-value 형태로 문자열 데이터를 저장하는 API입니다.
  • localStorage는 비교적 오래 남고, sessionStorage는 탭 세션에 더 가깝습니다.
  • 둘 다 자바스크립트에서 직접 읽고 쓸 수 있습니다.
  • 사용자 설정, 임시 UI 상태, 비민감한 캐시 데이터에 자주 쓰입니다.
  • 하지만 민감한 인증 정보 저장소로는 보안 관점을 더 신중히 봐야 합니다.

즉, Web Storage API는 브라우저 안에 간단한 상태를 남길 수 있는 편한 도구이지만, 무엇을 저장해도 되는 만능 저장소는 아닙니다.

Web Storage API는 정확히 무엇일까?

브라우저에서는 보통 아래 두 객체를 통해 접근합니다.

window.localStorage;
window.sessionStorage;

이 두 객체는 공통적으로:

  • key-value 구조를 가지고
  • 문자열 기반으로 저장되며
  • 브라우저에서 쉽게 읽고 쓸 수 있습니다

가장 기본적인 사용은 이렇게 생겼습니다.

localStorage.setItem('theme', 'dark');
 
const theme = localStorage.getItem('theme');
console.log(theme); // dark

즉, Web Storage API브라우저 안에 간단한 문자열 상태를 저장하는 기본 API라고 보면 됩니다.

Web Storage를 쓸까?

가장 큰 이유는 간단합니다. 새로고침 이후에도 일부 상태를 유지하기 쉽기 때문입니다.

예를 들어:

  • 다크모드 설정
  • 사용자가 마지막으로 본 탭
  • 검색 필터 일부
  • 온보딩 닫힘 여부

같은 값은 서버에 저장할 만큼 무겁지는 않지만, 메모리 state에만 두면 새로고침 시 사라질 수 있습니다.

이때 Web Storage는 꽤 편합니다.

즉, 이 API는 "서버 DB"와 "컴포넌트 메모리 state" 사이 어딘가에 있는 가벼운 브라우저 저장소에 가깝습니다.

localStoragesessionStorage는 무엇이 다를까?

둘 다 비슷해 보여도 사용성이 다릅니다.

localStorage

  • 브라우저에 비교적 오래 남습니다
  • 새로고침해도 유지됩니다
  • 브라우저를 닫았다가 다시 열어도 남는 경우가 일반적입니다

즉, 사용자가 다음 방문 때도 유지되면 좋은 값에 더 잘 맞습니다.

sessionStorage

  • 탭 세션에 더 가깝습니다
  • 새로고침해도 유지될 수 있지만
  • 탭을 닫으면 사라지는 방향에 가깝습니다

즉, 현재 탭 안에서만 잠깐 유지되면 좋은 값에 더 잘 맞습니다.

짧게 비교하면 아래처럼 볼 수 있습니다.

항목 localStorage sessionStorage
새로고침 후 유지 유지됨 유지됨
탭 닫은 뒤 유지 대체로 유지됨 보통 사라짐
탭 간 공유 감각 더 있음 탭 단위에 가까움
잘 맞는 용도 설정, 선호값, 가벼운 캐시 임시 입력 상태, 탭 전용 흐름

즉, 두 저장소의 본질적 차이는 보안보다 수명과 범위에 더 가깝습니다.

기본 메서드는 어떻게 생겼을까?

공통적으로 자주 쓰는 메서드는 아래와 같습니다.

1. setItem

localStorage.setItem('theme', 'dark');

값을 저장합니다.

2. getItem

const theme = localStorage.getItem('theme');

값을 읽습니다. 없으면 null이 옵니다.

3. removeItem

localStorage.removeItem('theme');

특정 key를 제거합니다.

4. clear

localStorage.clear();

저장소 전체를 비웁니다.

5. key, length

console.log(localStorage.length);
console.log(localStorage.key(0));

저장된 key 개수와 특정 인덱스의 key를 볼 수 있습니다.

즉, API 자체는 매우 단순합니다. 어려운 부분은 저장소 사용법보다 무엇을 저장해야 하는지 판단하는 것에 더 가깝습니다.

객체는 어떻게 저장할까?

여기서 자주 하는 실수가 있습니다. Storage는 문자열 기반이라서 객체를 그대로 넣을 수 없습니다.

예를 들어 이런 코드는 기대와 다르게 동작합니다.

localStorage.setItem('user', { id: 1, name: 'Marco' } as unknown as string);

그래서 보통은 JSON.stringifyJSON.parse를 같이 씁니다.

const user = { id: 1, name: 'Marco' };
 
localStorage.setItem('user', JSON.stringify(user));
 
const rawUser = localStorage.getItem('user');
const parsedUser = rawUser ? JSON.parse(rawUser) : null;

즉, 객체나 배열은 직렬화해서 저장하고, 읽을 때 다시 파싱하는 흐름이 기본입니다.

언제 localStorage가 잘 맞을까?

대표적으로 아래 같은 값이 잘 맞습니다.

1. 사용자 설정

localStorage.setItem('theme', 'dark');
localStorage.setItem('language', 'ko');

이런 값은 다음 방문에도 유지될수록 좋습니다.

2. 온보딩/공지 닫힘 여부

localStorage.setItem('hideOnboarding', 'true');

사용자 경험상 반복 노출을 줄이고 싶을 때 자연스럽습니다.

3. 비민감한 가벼운 캐시

예를 들어:

  • 마지막 검색어
  • 최근 본 카테고리
  • 정렬 기준

같은 값은 localStorage와 잘 맞는 경우가 많습니다.

즉, localStorage오래 기억해도 되는 비민감한 브라우저 개인화 값에 잘 맞습니다.

언제 sessionStorage가 잘 맞을까?

대표적으로 아래 같은 경우입니다.

1. 탭 단위 임시 상태

예를 들어 멀티스텝 폼에서:

sessionStorage.setItem('signupStep', '2');

같이 현재 탭 흐름 안에서만 유지하고 싶은 값입니다.

2. 새로고침 복원용 임시 데이터

탭은 유지하되 브라우저를 완전히 닫으면 굳이 남지 않아도 되는 값에 잘 맞습니다.

3. 탭 간 섞이면 안 되는 상태

예를 들어 같은 서비스를 여러 탭에서 열었을 때, 탭마다 다른 임시 흐름을 가져야 한다면 sessionStorage가 더 자연스러울 수 있습니다.

즉, sessionStorage지속성보다 현재 탭 문맥 유지에 더 어울립니다.

storage 이벤트는 언제 쓸까?

localStorage를 다룰 때 가끔 유용한 기능입니다.

다른 탭에서 저장소 값이 바뀌면 이벤트를 감지할 수 있습니다.

window.addEventListener('storage', (event) => {
  console.log(event.key, event.oldValue, event.newValue);
});

예를 들어:

  • 한 탭에서 로그아웃하면 다른 탭도 반응하게 하거나
  • 테마 변경을 다른 탭에 반영하고 싶을 때

같은 흐름에 활용할 수 있습니다.

즉, Web Storage는 단순 저장소일 뿐 아니라 탭 간 간단한 상태 동기화 힌트로도 쓸 수 있습니다.

무엇을 저장하면 안 될까?

이 부분이 실무에서 중요합니다.

1. 민감한 인증 정보

예를 들어:

  • 장기 refresh token
  • 민감한 세션 식별자
  • 개인정보가 많이 담긴 값

같은 것은 더 신중히 봐야 합니다.

이유는 단순합니다.

  • localStorage, sessionStorage는 자바스크립트에서 읽을 수 있고
  • XSS가 발생하면 탈취 표면이 커질 수 있기 때문입니다

즉, 브라우저 저장소는 편하지만 민감한 장기 토큰 저장소로는 보수적으로 판단해야 합니다.

2. 너무 큰 데이터

브라우저 저장소는 무한하지 않습니다.

또한 너무 큰 데이터를 넣으면:

  • 직렬화 비용이 커지고
  • 읽기/쓰기 비용이 늘고
  • 유지보수가 어려워질 수 있습니다

즉, 큰 캐시나 대용량 데이터는 다른 저장 전략이 더 맞을 수 있습니다.

3. 진실의 원천이 되어야 하는 비즈니스 데이터

예를 들어 서버와 정합성이 중요한 데이터를 브라우저 저장소만 믿고 운영하면 쉽게 어긋날 수 있습니다.

Web Storage는 어디까지나 보조 저장소에 더 가깝습니다.

React나 Next.js에서는 무엇을 조심해야 할까?

여기가 실무에서 꽤 중요합니다.

1. 서버 환경에서는 바로 접근할 수 없다

localStoragesessionStorage는 브라우저에서만 존재합니다.

즉, SSR이나 서버 컴포넌트에서는 바로 접근하면 안 됩니다.

console.log(localStorage.getItem('theme'));

이런 코드는 클라이언트 실행 시점에만 안전합니다.

그래서 보통:

  • useEffect 안에서 읽거나
  • 이벤트 핸들러 안에서 쓰거나
  • 클라이언트 전용 컴포넌트에서만 사용하는 식으로

경계를 나눕니다.

2. 초기 렌더와 hydration 차이

예를 들어 서버에서는 테마 정보를 모르고, 클라이언트에서만 localStorage를 읽는다면 초기 UI 깜빡임이 생길 수 있습니다.

즉, 저장소 사용은 단순 API 호출 문제가 아니라 초기 렌더 전략과도 연결됩니다.

3. 상태 동기화 기준

React state와 저장소를 양방향으로 무작정 동기화하면 오히려 복잡해질 수 있습니다.

그래서 보통은:

  • 초기값만 저장소에서 읽거나
  • 특정 이벤트 시점에만 저장하거나
  • 커스텀 훅으로 읽기/쓰기 규칙을 묶는 편이 더 안정적입니다

자주 하는 실수

정리하면 아래 실수가 정말 자주 나옵니다.

  • 객체를 그대로 저장하려고 한다
  • 없는 값이 null로 올 수 있다는 점을 놓친다
  • 민감한 토큰을 쉽게 저장해도 된다고 생각한다
  • 서버 환경에서 바로 localStorage를 읽는다
  • 브라우저 저장소를 서버 데이터의 진실 원천처럼 쓴다
  • clear()를 너무 넓게 써서 다른 값까지 같이 지운다

즉, Web Storage는 쉬워 보여도 수명, 직렬화, 브라우저 전용 환경, 보안 특성을 같이 봐야 안정적으로 쓸 수 있습니다.

실무 체크리스트

실제로 적용할 때는 아래 질문으로 빠르게 점검하면 도움이 됩니다.

  1. 이 값은 새로고침 뒤에도 유지되어야 하는가?
  2. 다음 방문 때도 남아야 하는가, 현재 탭에만 있으면 되는가?
  3. 자바스크립트에서 읽혀도 괜찮은 비민감한 값인가?
  4. 객체라면 직렬화/역직렬화 규칙이 명확한가?
  5. 서버 렌더링 환경과 충돌하지 않는가?

이 질문에 답하면 localStoragesessionStorage 중 무엇이 더 맞는지도 훨씬 선명해집니다.

정리하면

Web Storage API를 한 줄로 줄이면, 브라우저 안에 문자열 기반 상태를 저장하고 복원할 수 있게 해주는 기본 저장소 API입니다.

실무 기준으로 기억할 핵심은 이렇습니다.

  • localStorage는 더 오래 남는 설정/선호값에 잘 맞고
  • sessionStorage는 현재 탭 문맥의 임시 상태에 더 잘 맞으며
  • 둘 다 문자열 기반이라 직렬화가 필요하고
  • 둘 다 자바스크립트 접근이 가능하므로 민감 정보 저장은 더 신중해야 합니다

브라우저 저장소는 프론트엔드에서 정말 자주 쓰이는 기본기입니다. 그래서 이 API를 잘 이해하면 단순한 설정 저장뿐 아니라, 상태 복원, 탭 간 동기화, 렌더링 전략까지 더 안정적으로 설계할 수 있게 됩니다.

같이 보면 좋은 글