1. 동작 흐름 한눈에 보기
구분클릭 순간 브라우저가 하는 일React 트리가 어떻게 되나Zustand 스토어는?
Link (Next.js) | history.pushState()만 호출 → 네트워크 요청 없음 | 기존 React 트리 그대로 유지. router.pathname만 바뀌고 필요한 컴포넌트만 리렌더 | 메모리 그대로. 테마·로그인 등 전역 상태 유지 |
순수 <a> | 새 HTML 요청 → <html> 전체 다시 파싱 | 기존 React 앱 언마운트 → 새 HTML에 의해 다시 마운트 | 브라우저 메모리 클리어 → 새로운 store 인스턴스 생성 (초기값으로 리셋) |
2. 왜 이런 차이가 생길까? - 3단 구조로 뜯어보기
2-1. 브라우저 레이어 – “어디까지 다시 그릴까?”
- Link
- 내부적으로 window.history.pushState()(혹은 replaceState) 호출.
- URL만 바꾼 뒤 fetch()도, <script src> 재실행도 없다.
- 브라우저는 **“같은 문서 내 뷰 전환”**으로 취급 → DOM·JS Context 그대로.
- <a>
- 기본 동작은 HTTP GET & 새 문서 수신.
- 받은 HTML 헤더부의 <script> 태그를 다시 평가 → React·Zustand 코드를 **“새 인스턴스”**로 로딩.
- 즉, 기존 Memory Context 자체가 소멸.
2-2. React 레이어 – “컴포넌트를 살릴까 죽일까?”
- Link 라우팅은 App 컴포넌트 루트 유지.
- Next.js가 pages/**에 해당하는 컴포넌트만 교체 렌더.
- React DOM root는 “온전히 살아있다” → Context Provider(Store)도 살아남음.
- <a> 이동은 React root부터 해체.
- 새 HTML이 오면 document가 완전히 바뀌므로,
- 예전 React tree & 모든 state provider 언마운트 → 메모리에서 GC.
2-3. Zustand 레이어 – “Store 인스턴스 수명주기”
상황인스턴스 생성 시점파괴 시점
Single-Page Routing(=Link) | 브라우저 탭에서 최초 import 때 한 번 | 브라우저 탭 닫힐 때 |
Full Reload(=<a>) | 새 페이지에 스크립트가 로드될 때마다 | 다음 Reload 직전 |
👉 즉, 같은 탭이라도 <a>를 클릭하면 “새 탭 열었다 닫은 것”과 거의 같은 효과
3. 간단 비유 🌊
- Link : 수영장 안에서 레인만 바꾼다. 물도 그대로고 사람도 그대로.
- <a> : 아예 다른 수영장으로 이동. 기존 풀장은 불 끄고 문 잠그는 수준이라, 물놀이용 튜브(스토어)도 새로 사야 함.
4. 상태가 필요한 SPA에서 지켜야 할 2가지 수칙
- 내부 이동 = 반드시 Link(or router.push) 사용
- 로고, GNB, 사이드바 전부.
- 불가피하게 <a>를 써야 할 땐
- 외부 도메인이거나, "download" 속성이 필요한 파일 링크 정도로만 한정.
❓ “그럼 새로고침 하면?”
- 메모리 스토어는 당연히 사라진다. zustand/persist나 localStorage 직접 저장으로 해결 가능(단, 개인정보 암호화 주의).
'리액트' 카테고리의 다른 글
리액트의 key 속성이 중요한 이유 (0) | 2025.05.08 |
---|---|
SSR이 검색엔진에 더 잘 보이는 이유 (0) | 2025.04.23 |
onBlur 정리 (0) | 2025.04.21 |
부모 와 자식에서 같은 데이터를 쓸 때 (0) | 2025.01.17 |
[네이버지도] 새로고침 해야만 지도가 뜨는 이슈,, (0) | 2024.12.16 |