최근 나는 로그인 기능을 구현하면서, 서버로부터 받는 유저 정보를 관리해야 했다. 이때 전역 상태에 저장을 해도 페이지가 전환되면 상태가 없어질 텐데, 어떻게 관리를 해야할지 고민이었다. 브라우저 저장소에 관리하자니 개인정보 문제도 있고, 유실될 가능성도 있다고 판단했다. 매 페이지 전환마다 api를 붙이자니 공수가 너무 많이 들기도 하고 네트워크 비용도 꽤나 클 것 같았다.
이에 대해 주변 훌륭한 개발자들에게 물어보니,
전역 상태에 저장을 해도 페이지가 전환되면 상태가 없어질 텐데
이 부분에 대해 반문을 하셨다. 그리고 아주 놀라운 사실을 습득하였다. 이번 포스팅을 통해 자세히 설명하겠지만, 결론을 한문장으로 말하자면 라우팅은 새로고침하지 않고 페이지 전환이 되므로, 전역 상태가 그대로 유지 된다!
라우팅이란 사용자가 요청한 URL에 따라 알맞은 페이지를 보여주는 것을 의미한다.
React는 Single Page Application이기 때문에, 별도의 라우팅이 구현되어 있지 않다. 이를 위해 react-router-dom
라이브러리를 사용하여 라우팅을 할 수 있다.
SPA(Single Page Application)에 대해 자세히 알아보자.
SPA를 설명하기 전에, 기존의 방식이었던 MPA부터 시작해보자.
MPA(Multiple Page Application)은 서버가 각각의 페이지를 갖고 있다가 사용자가 다른 페이지로 이동할 때마다 새로운 html을 주는 방식이다. 따라서 SSR(서버 사이드 렌더링)으로 동작한다.
👍🏻 장점
- 초기 로딩 속도가 빠름 (열린 화면에 해당하는 페이지만 서버에서 보내주기 때문)
- SEO 유리 (모든 정보가 html에 다 적혀있기 때문)
🚨 단점
- 페이지를 이동할 때마다 화면이 깜빡인다 (새로 서버에서 페이지를 가져와야 하기 때문)
SPA(Single Page Application)은 하나의 페이지(html)에 변경된 정보, 또는 영역을 동적으로 변경하는 것을 말한다. 풀어 말하자면, 뷰 렌더링은 사용자의 브라우저가 담당하도록 하고, 우선 웹 애플리케이션을 브라우저에 불러와서 실행시킨 후에 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 사용해서 업데이트하는 방식이다. 즉, html은 한번만 받아와서 웹 애플리케이션을 실행시킨 후, 이후에는 필요한 데이터만 받아와서 화면에 업데이트 하는 것이다.
리액트를 보면 많은 파일을 작성하는데, 하나의 html이라는 것이 이해가 안될 수도 있다. 리액트를 처음 설치할 때 함께 설정되는 webpack이나 vite와 같은 번들러가 build시 우리가 작성한 많은 파일을 하나의 index.html 파일로 합쳐준다. (js 파일들도 bundle.js로 합쳐준다.)
CSR(클라이언트 사이드 렌더링)으로 동작한다.
👍🏻 장점
- 페이지 이동 속도가 빠르다
- 서버 부담이 감소한다
🚨 단점
- 초기 로딩 속도가 느리다 (모든 자바스크립트 파일을 받아와야 하기 때문)
- SEO 불리 (자바스크립트가 화면의 거의 모든 동작을 책임지고 있는데, 크롤러는 자바스크립트를 크롤링할 수 없기 때문)
<a>
태그 사용시 브라우저 새로고침으로 인해 상태값이 초기화된다.
대신 react-router-dom의 Link
컴포넌트를 사용하면 새로고침 없이 페이지 전환이 된다. 그리고 useNavigate 훅 또한 당연히 새로고침 없이 라우팅된다.
따라서 라우팅시에는 새로고침이 없이 페이지 전환이 되어 다시 bundle.js를 다운받지 않고, 돔을 다시 그리지 않기 때문에 전역 상태가 그대로 유지된다. (돔이 무엇인지 모르겠다면, 포스팅 참고!)
하지만 사용자는 라우팅만을 하지 않고, 새로고침을 하기도 하고, 직접 주소창에 url을 입력하여 자연스럽게 새로고침이 발생하기도 한다. 이 경우에는 어떻게 데이터를 유지할 수 있을까?
가장 기본적인 방법이다.
localStorage는 해당 웹페이지에 대한 데이터를 영구적으로 저장한다. 도메인마다 별도로 로컬 스토로지가 생성된다.
sessionStorage는 현재 페이지가 브라우징되고 있는 브라우저 컨텍스트 내에서만 데이터가 유지된다.
vs Cookie
- 쿠키는 매번 서버로 전송된다
- session cookie는 브라우저가 종료되면 제거된다
- persistant cookie는 만료일이 있어서, 만료일이 되면 제거된다
- 쿠키는 용량 제한이 있다
- 웹 스토리지는 파싱 기능
redux나 zustand 전역 상태 라이브러리의 persist기능을 사용하여, 새로고침 시에도 데이터가 유지되도록 할 수 있다.
하지만 이 방법도, localStorage/sessionStorage에 저장하고 값을 가져오는 것이 기본 작동 원리이다. 기존 전역 상태 관리 구현 방법을 그대로 작성하면서, 간편하게 브라우저 storage를 함께 사용하도록 도와주는 것이다.
아래는 zustand의 persist 예시이다.
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
export const useBearStore = create(
persist(
(set, get) => ({
bears: 0,
addABear: () => set({ bears: get().bears + 1 }),
}),
{
name: 'food-storage', // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
},
),
)
https://velog.io/@yijaee/what-is-spa
https://velog.io/@nasikun/react-router-dom-v6-동작-원리의-모든-것
https://jforj.tistory.com/341
https://docs.pmnd.rs/zustand/integrations/persisting-store-data
https://velog.io/@ejchaid/localstorage-sessionstorage-cookie의-차이점
[도서] 리액트를 다루는 기술