[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: web-history-api-routing
|
||||
title: Browser History API — push / replace / popstate
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [web, history, routing, browser, vibe-coding]
|
||||
tech_stack: { language: "JavaScript / browser", applicable_to: ["Web"] }
|
||||
applied_in: []
|
||||
aliases: [pushState, replaceState, popstate, back/forward]
|
||||
---
|
||||
|
||||
# Browser History API
|
||||
|
||||
> SPA 라우팅의 기반. **pushState / replaceState** 로 URL 변경 + 뒤로가기 지원. 라우터 (RR, Next) 가 추상화하지만 알아야 모달 / 검색 필터 / scroll restoration 등 처리 가능.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- pushState: 새 history entry. 뒤로가기 가능.
|
||||
- replaceState: 현재 entry 교체. 뒤로가기 안 변함.
|
||||
- popstate: 사용자 뒤로/앞으로. listener.
|
||||
- scroll restoration: 자동 복원.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### URL 변경
|
||||
```ts
|
||||
// 새 entry
|
||||
history.pushState({ page: 1 }, '', '/users/1');
|
||||
|
||||
// 현재 교체 (검색 필터 typing 시 디폴트)
|
||||
history.replaceState({ filter: 'name' }, '', '?filter=name');
|
||||
|
||||
// 뒤로가기 감지
|
||||
window.addEventListener('popstate', e => {
|
||||
const state = e.state; // pushState 의 첫 인자
|
||||
render(state);
|
||||
});
|
||||
```
|
||||
|
||||
### 모달 — URL 동기
|
||||
```ts
|
||||
function openModal(id: string) {
|
||||
history.pushState({ modal: id }, '', `?modal=${id}`);
|
||||
setOpenModal(id);
|
||||
}
|
||||
|
||||
// popstate (back) 시 모달 닫기
|
||||
window.addEventListener('popstate', () => {
|
||||
setOpenModal(null);
|
||||
});
|
||||
|
||||
// 모달 닫기 버튼 → 뒤로
|
||||
function closeModal() {
|
||||
history.back(); // popstate 트리거 → 위 핸들러
|
||||
}
|
||||
```
|
||||
|
||||
### 검색 필터 — replaceState (back 무한 폭주 방지)
|
||||
```ts
|
||||
function setSearch(q: string) {
|
||||
const url = new URL(location.href);
|
||||
url.searchParams.set('q', q);
|
||||
history.replaceState(null, '', url.toString());
|
||||
// 매 keystroke push 면 history 폭증 → replace
|
||||
}
|
||||
```
|
||||
|
||||
### Scroll restoration
|
||||
```ts
|
||||
// 디폴트: 'auto' — 브라우저가 위치 복원.
|
||||
// SPA 에서 비동기 데이터 로드 후 위치 안 맞으면 'manual' 후 직접
|
||||
history.scrollRestoration = 'manual';
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
// 데이터 로드 후
|
||||
window.scrollTo(0, savedScrollPositions[location.pathname] ?? 0);
|
||||
});
|
||||
```
|
||||
|
||||
### 라우터 안에서 (React Router)
|
||||
```tsx
|
||||
const navigate = useNavigate();
|
||||
navigate('/users/1'); // pushState
|
||||
navigate('/users/1', { replace: true }); // replaceState
|
||||
navigate(-1); // back
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 액션 | 메서드 |
|
||||
|---|---|
|
||||
| 새 화면 (back 가능) | pushState |
|
||||
| URL 만 갱신 (필터, 정렬) | replaceState |
|
||||
| 모달 / drawer | pushState — back 으로 닫힘 (UX) |
|
||||
| 인증 후 redirect | replaceState — back 으로 안 돌아감 |
|
||||
| 외부 링크 | location.href = ... 또는 anchor |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **검색 typing 매번 pushState**: 100번 typing → 100 history. replaceState.
|
||||
- **state 없이 pushState**: popstate 시 state null. 정보 손실. 항상 state object.
|
||||
- **state 에 큰 객체**: 직렬화 비싸고 5MB 한계. ID 만, 본문은 별도 store.
|
||||
- **popstate 에서 fetch 호출 + race**: 빠른 back/forward 시 stale. AbortController.
|
||||
- **scroll 위치 안 저장**: 뒤로 시 top 으로. 직접 saveScrollPosition.
|
||||
- **history.back() 호출 후 해당 entry 가 없음**: 사이트 떠남. canGoBack 체크.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- "새 화면 = push, URL 만 갱신 = replace" 명확.
|
||||
- 모달 = pushState + popstate close.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[React_Router_Patterns]]
|
||||
- [[React_Refs_Patterns]]
|
||||
Reference in New Issue
Block a user