이번 편의 결과물: 코드는 바꾸지 않습니다. bookshelf-next가 따를 렌더링 모델과 최종 라우트 구성을 확정합니다. · 다루는 개념: SPA 렌더링 모델의 한계, 파일 기반 라우팅, App Router 파일 컨벤션(page/layout/loading/error), bookshelf-next 청사진
이 편에서 만드는 파일
개념 편입니다. 새로 만들거나 고치는 파일이 없습니다. 03편부터 실제로 bookshelf-next 프로젝트를 만들며 이 편에서 정리한 구조를 그대로 적용합니다.
개념 정리
react_3 bookshelf가 브라우저에서 하는 일
react_3에서 완성한 bookshelf는 SPA(Single Page Application)입니다. 브라우저가 처음 받는 HTML은 빈 div 하나뿐이고, 화면은 자바스크립트 번들이 실행된 뒤에야 그려집니다. 책 목록도 컴포넌트가 마운트된 다음 useQuery가 mock API를 호출해야 나타납니다.
이 방식에는 세 가지 한계가 있습니다.
| 한계 | 내용 |
|---|---|
| 초기 화면 지연 | 자바스크립트 번들을 내려받고 실행할 때까지 화면이 비어 있거나 로딩 표시만 보인다 |
| 클라이언트에 노출되는 코드 | API 호출 로직과 토큰 처리 로직이 모두 브라우저에서 실행되는 번들 안에 들어간다 |
| 검색엔진과 공유 미리보기 | 브라우저가 자바스크립트를 실행해야 내용이 채워지므로, 크롤러나 링크 미리보기는 빈 화면만 본다 |
Next.js는 이 세 가지를 서버에서 먼저 렌더링하는 방식으로 다르게 풉니다. 서버가 데이터를 읽고 HTML을 만들어 브라우저에 보내므로, 화면은 자바스크립트 실행을 기다리지 않고도 먼저 보입니다.
파일 기반 라우팅
react_3는 react-router로 라우트를 코드로 선언했습니다. 라우터 컴포넌트에 path와 element를 나열하는 방식입니다. Next.js App Router는 라우트를 코드가 아니라 폴더 구조로 선언합니다. app 폴더 안 폴더 하나가 URL 세그먼트 하나에 대응합니다.
| Next.js 폴더 | URL |
|---|---|
app/page.js | / |
app/books/page.js | /books |
app/books/[bookId]/page.js | /books/1, /books/2 등 |
대괄호로 감싼 폴더명([bookId])은 동적 세그먼트입니다. 05편에서 실제로 만듭니다.
App Router 파일 컨벤션
폴더 안에 정해진 이름의 파일을 두면 Next.js가 그 역할을 자동으로 인식합니다.
| 파일명 | 역할 |
|---|---|
page.js | 그 세그먼트를 실제 URL로 공개한다. 없으면 폴더가 있어도 라우트가 열리지 않는다 |
layout.js | 그 세그먼트와 하위 세그먼트가 공유하는 UI. 페이지 이동 시 다시 그려지지 않는다 |
loading.js | 데이터를 불러오는 동안 자동으로 보여줄 화면 |
error.js | 렌더링 중 에러가 나면 자동으로 보여줄 화면 |
not-found.js | notFound()를 호출했을 때 보여줄 화면 |
page.js가 없는 폴더는 URL로 접근할 수 없습니다. 이 규칙 덕분에 컴포넌트나 유틸 파일을 라우트 폴더 안에 함께 두어도 실수로 라우트가 되는 일이 없습니다.
bookshelf-next 청사진
react_1~3의 bookshelf 코드를 그대로 옮기지 않고, 같은 도메인(책 관리)을 App Router 구조로 새로 짭니다. 이후 편에서 하나씩 채울 라우트는 다음과 같습니다.
app/
├── page.js (홈 — 04편)
├── books/
│ ├── page.js (책 목록 — 04편)
│ └── [bookId]/
│ └── page.js (책 상세 — 05편)
└── api/
└── books/
└── route.js (REST 엔드포인트 — 07편)이 트리는 목표 지점을 미리 보여주기 위한 스케치이고, 실제로는 03편부터 한 단계씩 만듭니다.
왜 서버 컴포넌트가 기본값인가
App Router의 모든 컴포넌트는 별다른 표시가 없으면 서버 컴포넌트입니다. 서버 컴포넌트는 서버에서 실행되고 결과만 HTML과 최소한의 데이터로 브라우저에 전달됩니다. 상태나 이벤트 핸들러처럼 브라우저에서만 의미 있는 기능이 필요한 부분만 예외적으로 클라이언트 컴포넌트로 선언합니다. 이 경계를 어디에 그을지가 02편의 주제입니다.
직접 해보기
react_3의 BookListPage가 화면에 나타나기까지 어떤 순서로 일이 벌어지는지 단계별로 적어보세요.
- 브라우저가 빈
div#root만 담긴 HTML을 받는다. - 자바스크립트 번들을 내려받고 실행한다.
- React가
BookListPage컴포넌트를 마운트한다. useQuery가 mock API에 요청을 보낸다.- 응답이 오면 그제서야 책 목록이 화면에 그려진다.
이 다섯 단계가 끝나기 전까지 사용자는 로딩 화면만 봅니다. Next.js는 3~5단계를 서버에서 먼저 처리해, 완성된 HTML을 1단계에서 바로 보냅니다.
자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
| Next.js도 react-router처럼 라우트 설정 파일을 찾는다 | SPA 라우팅 습관이 남음 | App Router는 폴더 구조 자체가 라우트다. 별도 설정 파일이 없다 |
모든 페이지에 page.js만 있으면 된다고 생각한다 | 파일 컨벤션을 다 외우지 않음 | 공통 UI는 layout.js, 로딩·에러는 각각 loading.js와 error.js로 분리한다 |
bookshelf(react_3) 코드를 그대로 복사하려 한다 | 두 프로젝트가 같은 도메인이라 착각 | 렌더링 모델 자체가 다르므로 03편부터 새로 만든다 |