loading.tsx·error.tsx — 로딩과 에러 UI 분리
useState·try-catch 없이 파일 두 개로 UX 80% 끝.
옛 React 에선 모든 페이지마다 같은 패턴을 반복해야 했다 — useState({loading: false, error: null, data: null}) 만들고, fetch 호출 전후로 setState, JSX 안에서 {loading ? <Spinner /> : error ? <Error /> : <Real />} 분기. 10번 페이지 만들면 같은 코드 10번.
App Router 의 답이 특수 파일로 분리. loading.tsx 와 error.tsx 를 한 폴더에 두면 — 그 폴더 안 모든 페이지가 자동으로 그 UI 를 쓴다. 따로 import 도 분기도 없음. React 의 <Suspense> 와 ErrorBoundary 를 Next 가 자동으로 감싸준다.
1. loading.tsx — 자동 로딩 UI
가장 단순한 예. app/dashboard/loading.tsx 만 만들면 끝.
사용자가 /dashboard 로 이동하면 — Next 가 자동으로 먼저 loading 컴포넌트를 즉시 렌더, 백그라운드에서 fetchSlowStats 가 끝나면 진짜 페이지로 교체. useState·useEffect 한 줄 없이.
<Suspense fallback={<Loading />}><Page /></Suspense> 로 자동 감싼다. React Suspense 의 표준 패턴을 파일 시스템 컨벤션으로 단순화한 것. 익숙해지면 옛 useState 분기 코드가 끔찍해 보인다.
2. error.tsx — 자동 에러 폴백
같은 패턴. app/dashboard/error.tsx 만 만들면 그 폴더의 모든 페이지가 에러 발생 시 이 UI 로 자동 대체.
두 가지를 받는다 — error(에러 객체) 와 reset(재시도 함수). reset() 을 호출하면 Next 가 그 에러 바운더리 안쪽을 다시 렌더. 사이드 메뉴는 그대로, 깨진 영역만 다시 시도.
꼭 'use client' 를 적어야 한다. 재시도 버튼이 onClick 으로 인터랙티브하니까. 서버 컴포넌트에선 동작 안 함.
3. not-found.tsx — 404 전용
비슷한 결의 셋째. 동적 라우트에서 데이터가 없을 때.
페이지 안에서 notFound() 호출 → Next 가 즉시 가장 가까운 not-found.tsx 로 점프. throw new Error('Not Found') 같은 거 안 해도 됨.
4. 4가지 특수 UI 한 표로
| 파일 | 언제 | 'use client' |
|---|---|---|
loading.tsx | page 가 async 라 데이터 기다리는 중 | 선택 (보통 서버) |
error.tsx | 렌더 도중 예외 throw | 필수 (reset 버튼) |
not-found.tsx | notFound() 호출 또는 404 URL | 선택 |
global-error.tsx | 루트 layout 자체 깨짐 | 필수 + <html> 포함 |
global-error.tsx 는 거의 마지막 안전망. 루트 app/layout.tsx 가 깨졌을 때 (드물지만 존재). 자체 <html>·<body> 를 포함해야 한다.
5. 부분 스트리밍 — 한 페이지 안에서 영역별 로딩
page 전체가 아니라 일부 컴포넌트만 로딩 처리하고 싶을 때. React <Suspense> 를 직접 사용.
차트와 테이블이 독립적으로 로드되어 각자 끝나는 대로 화면에 채워진다. Streaming SSR 의 진수. 사용자는 빠르게 헤더부터 보고, 차트는 1초 뒤, 테이블은 2초 뒤 자동 채움.
loading.tsx 는 그 폴더 전체를 감싼다. 부분만 로딩하고 싶으면 <Suspense> 를 페이지 안에서 직접 써야. 또 error.tsx 는 같은 레벨의 layout 은 못 잡는다 — 부모 폴더로 한 단계 올려야 잡힘.
요약 — 11편 좌표
여기까지 정리. 네 가지 특수 파일 — loading.tsx(자동 Suspense), error.tsx('use client' + reset, 자동 ErrorBoundary), not-found.tsx(notFound() + 404), global-error.tsx(최후 안전망). 부분 영역만 로딩 처리하려면 <Suspense> 를 페이지 안에서 직접. useState·try/catch 분기 코드가 거의 사라진다. 다음 편에서 Suspense 의 진짜 잠재력 — Streaming.
다음 편 예고 — Suspense와 스트리밍
큰 페이지를 부분 부분 빠르게 보여주는 기법. 12편.