Routing - Loading UI: 로딩 UI
Next.js 13에는 새로운 파일 규칙인 loading.js가 도입되어 React Suspense로 의미 있는 로딩 UI를 만들 수 있습니다. 이 규칙을 사용하면 경로 세그먼트의 콘텐츠가 로드되는 동안 서버에서 즉시 로딩 상태를 표시할 수 있으며, 렌더링이 완료되면 새 콘텐츠가 자동으로 교체됩니다.
즉시 로딩 상태
즉시 로딩 상태는 탐색 시 즉시 표시되는 fallback(대체) UI입니다. 스켈레톤, 스피너와 같은 로딩 표시기를 미리 렌더링하거나 표지 사진, 제목 등과 같이 향후 화면에서 작지만 의미 있는 부분을 미리 렌더링할 수 있습니다. 이를 통해 사용자가 앱이 응답하고 있음을 이해하고 더 나은 사용자 경험을 제공할 수 있습니다.
폴더 내에 loading.js 파일을 추가하여 로딩 상태를 생성합니다.
// app/dashboard/loading.tsx
export default function Loading() {
// 스켈레톤을 포함한 모든 UI를 로딩 내에 추가할 수 있습니다.
return <LoadingSkeleton />
}
같은 폴더에서 loading.js는 layout.js 안에 중첩됩니다. page.js 파일과 그 아래의 모든 하위 파일을 <Suspense> 경계로 감싸게 됩니다.
알아두면 좋습니다:
- 서버 중심 라우팅을 사용하더라도 내비게이션이 즉시 실행됩니다.
- 내비게이션은 중단 없이 진행되므로 경로를 변경할 때 다른 경로로 이동하기 전에 경로의 콘텐츠가 완전히 로드될 때까지 기다릴 필요가 없습니다.
- 새 경로 세그먼트가 로드되는 동안 공유 레이아웃은 대화형 상태로 유지됩니다.
수동으로 서스펜스 경계 정의하기
loading.js 외에도 자체 UI 컴포넌트에 대한 서스펜스 바운더리를 수동으로 생성할 수도 있습니다.
권장 사항: Next.js가 이 기능을 최적화하므로 route 세그먼트(레이아웃 및 페이지)에는 loading.js 규칙을 사용하세요.
서스펜스 바운더리 사용법을 배우려면 React 문서를 참조하세요.
Routing - Error Handling : 에러 핸들링
error.js 파일 규칙을 사용하면 중첩된 라우트에서 런타임 오류를 우아하게 처리할 수 있습니다.
- route 세그먼트와 그 중첩된 자식들을 React 에러 바운더리에서 자동으로 래핑합니다.
- 파일 시스템 계층 구조를 사용하여, 특정 세그먼트에 맞춘 오류 UI를 생성해 세분하게 조정할 수 있습니다.
- 앱의 나머지 기능은 유지하면서 영향을 받는 세그먼트에 대한 오류를 격리합니다.
- 전체 페이지를 다시 로드하지 않고 오류에서 복구를 시도하는 기능을 추가합니다.
route 세그먼트 내에 error.js 파일을 추가하고 React 컴포넌트를 내보내서 오류 UI를 생성합니다:
'use client'; // Error 컴포넌트는 클라이언트 컴포넌트여야 합니다.
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
);
}
error.js 작동 방식
- error.js는 중첩된 자식 세그먼트 또는 page.js 컴포넌트를 감싸는 React Error Boundary를 자동으로 생성합니다.
- error.js 파일에서 내보낸 React 컴포넌트가 폴백 컴포넌트로 사용됩니다.
- Error Boundary 내에서 에러가 발생하면 에러가 포함되고 fallback 컴포넌트가 렌더링됩니다.
- fallback error 컴포넌트가 활성화되면 Error Boundary 위의 레이아웃은 해당 상태를 유지하고 대화형 상태를 유지하며 Error 컴포넌트는 오류를 복구하는 기능을 표시할 수 있습니다.
오류 복구
오류의 원인은 일시적인 것일 수 있습니다. 이러한 경우 다시 시도하면 문제가 해결될 수 있습니다.
오류 컴포넌트는 reset() 함수를 사용하여 사용자에게 오류에서 복구를 시도하라는 메시지를 표시할 수 있습니다. 이 함수가 실행되면 오류 경계의 내용을 다시 렌더링하려고 시도합니다. 성공하면 fallback Error 컴포넌트가 다시 렌더링한 결과로 대체됩니다.
// app/dashboard/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
중첩된 경로
특수 파일을 통해 생성된 React 컴포넌트는 특정 중첩 계층 구조에서 렌더링됩니다.
예를 들어 layout.js 파일과 error.js 파일이 모두 포함된 두 개의 세그먼트가 있는 중첩된 경로는 다음과 같은 단순화된 컴포넌트 계층 구조로 렌더링됩니다:
중첩된 컴포넌트 계층 구조는 중첩된 경로에서 error.js 파일의 동작에 영향을 미칩니다:
- 에러는 가장 가까운 상위 Error Boundary까지 퍼집니다. 즉, error.js 파일은 중첩된 모든 하위 세그먼트에 대한 오류를 처리합니다. route의 중첩된 폴더에서 error.js 파일을 서로 다른 레벨에 배치하면 다소 세분화된 오류 UI를 구현할 수 있습니다.
- Error Boundary가 해당 레이아웃 컴포넌트 내부에 중첩되어 있기 때문에 error.js Boundary는 동일한 세그먼트의 layout.js 컴포넌트에서 발생한 오류를 처리하지 않습니다.
Layout에서 에러 처리하기
error.js Boundary는 같은 세그먼트의 layout.js 또는 template.js 컴포넌트에서 발생한 에러를 포착하지 않습니다. 이 의도적인 계층 구조는 에러가 발생했을 때 형제 route(예: navigation) 간에 공유되는 중요한 UI가 계속 표시되고 작동하도록 합니다.
특정 레이아웃 또는 템플릿 내에서 에러를 처리하려면 레이아웃 상위 세그먼트에 error.js 파일을 배치합니다.
루트 레이아웃 또는 템플릿 내에서 에러를 처리하려면 global-error.js라는 error.js의 변형을 사용합니다.
루트 레이아웃에서 에러 처리하기
루트 app/error.js boundary는 루트 app/layout.js 또는 app/template.js 컴포넌트에서 발생한 오류를 포착하지 못합니다.
이러한 루트 컴포넌트의 에러를 구체적으로 처리하려면 루트 앱 디렉터리에 있는 app/global-error.js라는 error.js의 변형을 사용하세요.
루트 error.js와 달리 global-error.js Error boundary는 전체 애플리케이션을 감싸며, 해당 fallback 컴포넌트가 활성화되면 루트 레이아웃을 대체합니다. 따라서 global-error.js는 자체 <html> 및 <body> 태그를 정의해야 한다는 점에 유의해야 합니다.
global-error.js는 가장 세분화된 에러 UI이며 전체 애플리케이션에 대한 "포괄적인" 에러처리로 간주할 수 있습니다. 루트 컴포넌트는 일반적으로 덜 동적이며 다른 error.js boundary가 대부분의 에러를 포착하므로 자주 트리거되지 않을 가능성이 높습니다.
global-error.js가 정의되어 있더라도 전역적으로 공유되는 UI 및 브랜딩을 포함하는 루트 레이아웃 내에서 렌더링될 fallback 컴포넌트가 있는 루트 error.js를 정의하는 것이 좋습니다.
// app/global-error.tsx
'use client';
export default function GlobalError({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
);
}
서버 에러 처리
데이터를 불러오는 중일때나 서버 컴포넌트 내부에서 에러가 발생하면 Next.js는 결과인 Error 객체를 error prop으로 가장 가까운 error.js 파일로 전달합니다.
다음 개발을 실행할 때 에러는 직렬화되어 서버 컴포넌트에서 클라이언트 error.js로 전달됩니다. 프로덕션에서 next start을 실행할 때 보안을 보장하기 위해 일반 에러 메시지는 에러 메시지의 해시가 포함된 .digest와 함께 error로 전달됩니다. 이 해시는 서버 로그에 대응하는 데 사용할 수 있습니다.