세 파일이 서로 다른 실패 시간을 맡아
| 파일 | 역할 | 실행 위치 |
|---|---|---|
loading.tsx | 페이지를 Suspense로 감싸 데이터가 준비되는 동안 보여 줘. | 서버 또는 클라이언트 |
error.tsx | 해당 구간과 하위 트리에서 난 오류를 잡아. | 클라이언트 필수 |
not-found.tsx | 해당 구간에서 notFound()를 실행했을 때 보여 줘. | 서버 또는 클라이언트 |
한 화면에 몰아서 조건문으로 처리하지 않아도 파일 위치가 경계의 범위를 정해 줘. 더 안쪽에 파일을 둘수록 실패가 영향을 주는 범위도 좁아져.
루트 레이아웃에는 별도 안전망이 필요해
일반 error.tsx는 자신과 같은 구간의 레이아웃에서 난 오류를 잡지 못해. 루트 레이아웃까지 깨질 수 있는 경우에는 app/global-error.tsx를 둬. 이 파일은 망가진 루트 문서 전체를 대신하므로 자기 <html>과 <body>를 직접 반환해야 해.
존재하지 않는 데이터는 명시적으로 끝내
서버 컴포넌트에서 next/navigation의 notFound()를 실행하면 렌더링을 중단하고 가장 가까운 not-found.tsx로 넘어가. HTTP 404 상태 코드는 프레임워크가 처리해. 빈 화면을 정상 응답처럼 돌려주는 것보다 의미와 상태가 함께 맞아.
각 실패를 데이터 대기, 예상 가능한 부재, 예상 밖 오류로 분류해. 대기는 loading.tsx, 없는 자원은 notFound(), 예외는 error.tsx로 보내면 상태 코드와 사용자 행동이 맞아. 재시도 버튼이 실제로 안전한지, 오류 경계가 어느 레이아웃까지 보존하는지도 시험해. 각 경계에 일부러 지연과 예외를 넣고 어느 레이아웃까지 살아 남는지 기록해. 오류를 고친 뒤 다시 시도할 때 같은 입력과 이동 맥락이 유지되는지도 봐. 모든 오류를 친절한 200 화면으로 바꾸면 모니터링과 검색 엔진은 실패를 알아채지 못해. 반대로 사용자가 고칠 수 있는 검증 오류를 예외로 던지면 전체 경계가 무너져. 실패의 기술적 종류보다 다음 행동이 같은 사건끼리 묶는 게 좋아. 느린 Promise, 존재하지 않는 ID, 렌더 예외를 각각 만들어 세 파일이 다른 상태와 화면을 내는지 확인해. 같은 구간의 레이아웃에서 오류를 던져 일반 error.tsx가 잡지 못하는 것도 보고, 루트 실패에서는 global-error.tsx가 독립 문서를 만드는지 시험해.