이번 편의 결과물: 코드 변경 없음 — 개념만 정리하고 03편부터 적용합니다. · 다루는 개념: URL과 화면 상태의 관계, History API, 라우터가 없을 때의 한계, React Router의 역할
개념 정리
URL은 화면 상태의 일부다
지금 bookshelf는 페이지가 하나라서 “지금 무엇을 보고 있는지”가 오직 React state(filter, 선택한 책 등)에만 있습니다. 문제는 이 상태가 주소창의 URL에는 반영되지 않는다는 점입니다.
| 상황 | URL만으로 상태를 복원 | 새로고침 후 상태 |
|---|---|---|
| 라우터 없이 state로만 화면 전환 | 불가능(URL이 항상 동일) | 초기 화면으로 초기화됨 |
URL에 라우트를 반영(/books/3) | 가능(URL만 보고 3번 책 상세를 알 수 있음) | 같은 상세 화면이 다시 렌더됨 |
URL이 화면 상태를 담으면 새로고침·뒤로 가기·북마크·링크 공유가 모두 같은 화면을 가리킵니다. 이것이 라우팅의 핵심 목적입니다.
History API — 브라우저가 이미 제공하는 것
브라우저는 페이지를 다시 불러오지 않고 주소창 URL과 방문 기록을 조작하는 History API(history.pushState, history.replaceState, popstate 이벤트)를 내장하고 있습니다.
| 메서드/이벤트 | 역할 |
|---|---|
history.pushState(state, '', url) | 새로고침 없이 URL을 바꾸고 방문 기록에 새 항목을 추가 |
history.replaceState(state, '', url) | 현재 방문 기록 항목을 바꿈(새 항목 추가 안 함) |
popstate 이벤트 | 사용자가 뒤로/앞으로 가기를 눌렀을 때 발생 |
아래 예제로 pushState가 URL만 바꾸고 페이지는 다시 불러오지 않는다는 점을 확인합니다.
버튼을 누르면 URL은 /books/3으로 바뀌지만 화면은 render()를 직접 호출해야만 바뀝니다. pushState 자체는 렌더링과 무관합니다.
라우터 없이 직접 구현할 때의 한계
위 예제처럼 pushState와 popstate만으로 라우팅을 만들 수는 있습니다. 하지만 페이지가 늘어나면 다음을 전부 직접 관리해야 합니다.
- URL 패턴(
/books/:bookId)에서 파라미터를 뽑아내는 로직 - 중첩된 화면(목록 안의 상세, 상세 안의 편집)을 URL 세그먼트와 맞추는 로직
- 페이지 진입 전에 필요한 데이터를 미리 준비하는 로직
- 뒤로 가기·앞으로 가기·직접 URL 입력까지 전부 같은 화면을 그리는 일관성
React Router의 역할
React Router는 History API 위에 이 로직들을 표준화해 올립니다. 라우트를 객체 배열로 선언하면 URL 매칭, 파라미터 추출, 중첩 렌더링, 진입 전 데이터 준비(loader)까지 라이브러리가 맡습니다. 03편부터 createBrowserRouter로 이 라우트 객체를 정의합니다.
자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
pushState 호출 후 화면이 안 바뀜 | pushState는 렌더링을 유발하지 않음 | 위 예제처럼 URL 변경 뒤 직접 다시 렌더하거나, React Router처럼 URL 변화를 감지해 렌더하는 도구를 쓴다 |
<a href="/books/3">를 그냥 씀 | 브라우저 기본 동작은 페이지를 새로 요청함 | SPA 안에서는 라우터가 제공하는 Link 컴포넌트를 써서 새로고침 없이 이동한다(03편) |
| 뒤로 가기를 눌러도 화면이 그대로임 | popstate 이벤트를 처리하지 않음 | 라이브러리를 쓰면 이 처리가 내장되어 있다 |