loading.js 라는 특별한 키워드로 파일을 생성하면
React Suspense 에 의해 page 컴포넌트가 로딩되는 동안
즉각적인 로딩 상태를 보여줄 수 있다.
스켈레톤 UI 등을 설정할 때 유용한 기능.
해당 기능을 사용하면 사용자 경험을 크게 향상시킬 수 있다.
사용방법은 굉장히 간단한데,
// app/about/loading.tsx
export default function Loading() {
return <LoadingSkeleton />
}
로딩 컴포넌트를 사용한 폴더 바운더리에서 loading.tsx 파일을 만들고
return 으로 보여주고싶은 컴포넌트를 반환하면 된다.
특정 컴포넌트가 비동기적으로 로딩되는 데이터를 기다리도록 하는 매커니즘.
로딩이 완료될 때 까지 보여줄 fallback 컴포넌트를 지정할 수 있다.
요컨데, 비동기적으로 컴포넌트 처리를 해줄 수 있다는 것.
React 18 버전에서 정식 기능으로 릴리즈 되었다.
Next js 에서는 loading.js 파일을 이용한 로딩 UI 설정 이외에도
Suspense 바운더리를 수동으로 설정하여 적절하게 활용할 수도 있다.
이를 이해하기 위해서는 서버 사이드 렌더링(SSR)에 대한 이해가 필수적인데
SSR 은 다음과 같은 일련의 과정을 거쳐 진행된다.
이 단계는 서로 순차적으로 진행되어야만 하며 동시진행될 수 없다.
서버에서 데이터를 받아와야만 페이지에 대한 HTML 을 렌더링할 수 있고,
클라이언트는 모든 컴포넌트의 코드를 받아와야만 Hydration 을 진행할 수 있다.
모든 데이터를 서버에서 가져오는 데 완료되기 까지의 시간이 길다면
사용자에게 상호작용 가능한 페이지를 제공하는 데 오랜 시간이 걸릴 수 있고,
이는 곧 사용자 경험에 부정적인 영향을 준다.
Next js 개발팀에서는 이러한 문제를 해결하기 위해서 Streaming 이라는 매커니즘을 도입했다.
Streaming 이란 HTML 을 보다 작은 요소로 나누어
"전체 페이지를 한번에 가져오는 것" 이 아닌,
"부분적으로 나누어 서버에서 클라이언트로 점진적으로 전송" 하는 것이다.
이렇게 되면 모든 데이터를 로드해야만 상호작용할 수 있는 것이 아니라,
UI의 일부분을 우선적으로 렌더링 할 수 있기 때문에 어느정도 위의 문제점을 해결할 수 있다.
뿐만 아니라, "우선적으로 렌더링 할 요소" 를 선택할 수 있기 때문에
먼저 상호작용 되어야 하는 컴포넌트에 적용함으로써,
보다 나은 사용자 경험을 제공할 수 있다.
컴포넌트 내에서 발생하는 런타임 오류를 처리하기 위해
React 에서는 Error boundaries 라는 매커니즘을 제공한다.
error boundary 는 컴포넌트 트리에서 발생한 자바스크립트 오류를 포착하여,
오류를 발생시킨 컴포넌트 트리 대신 fallback UI 를 보여주도록 설계되어 있다.
Next js 에서는 error.js 라는 특별한 키워드로 파일을 생성하면
Error Boundary 를 자동으로 생성한다.
에러가 발생하면 해당 바운더리에서 자체적으로 대신 보여줄 UI를 설정하거나,
reset() 함수를 이용해 사용자가 오류로부터 복구할 수 있는 기능을 제공할 수 있다.
실 사용 예시는 다음과 같다.
'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>
)
}
주의해야 할 점은,
error boundary 매커니즘 자체가 하위 트리 구조의 에러를 포착하는 구조이므로
동일한 바운더리에 있는 layout 컴포넌트에 대한 오류를 처리할 수 없다.
layout 컴포넌트에서 발생한 오류를 처리하기 위해서는 error.js 파일을
해당 바운더리의 부모 바운더리에 설정해야 한다.
이러한 문제 때문에, root layout 컴포넌트의 경우
global-error.js 라는 파일을 만들어 루트 앱 디랙토리에 위치시켜야만
error boundary 를 적용할 수 있다.
또한 이 컴포넌트는 자체적으로 html, body 태그를 정의해야만 한다.
예시는 아래와 같다.
'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>
)
}