Suspense는 비동기 데이터 로딩 중에 대체 UI를 제공하는 React의 기능이다. Next.js에서는 Suspense를 사용해 컴포넌트가 준비될 때까지 로딩 스피너나 플레이스홀더를 표시할 수 있다.
예를 들어, 아래 코드는 Suspense를 사용하여 데이터가 로딩되는 동안 LoadingSpinner 컴포넌트를 보여준다.
import React, { Suspense } from 'react';
import DataComponent from './DataComponent';
function LoadingSpinner() {
return <div>Loading...</div>;
}
function Page() {
return (
<Suspense fallback={<LoadingSpinner />}>
<DataComponent />
</Suspense>
);
}
export default Page;
또한, Streaming SSR은 서버에서 HTML을 스트리밍 방식으로 클라이언트에 전송한다. 이를 통해 브라우저는 페이지의 중요한 부분부터 렌더링하여 초기 TTV(Time To View)를 단축시킨다. Next.js 13에서는 Streaming SSR을 기본적으로 지원하며, 이를 활용하면 사용자가 느끼는 로딩 시간을 크게 줄일 수 있다.
예시로, 서버에서 점진적으로 데이터를 보내는 경우 아래와 같은 코드 구조를 사용할 수 있다.
// Next.js 13의 App Router 환경에서 streaming을 활용하는 예시
export default async function Page() {
// fetchData가 점진적으로 데이터를 반환한다고 가정
const dataStream = fetchDataStream();
return (
<div>
<h1>Streaming SSR 예시</h1>
{dataStream}
</div>
);
Error UI와 Error Handling은 애플리케이션에서 에러가 발생할 때 사용자에게 친절한 안내를 제공하고, 개발자가 문제를 파악할 수 있도록 돕는다.
Next.js에서는 에러 경계(Error Boundary)를 사용해 특정 컴포넌트에서 발생한 예외를 캡처하고 대체 UI를 렌더링할 수 있다.
예를 들어, 다음 코드는 간단한 에러 경계 컴포넌트를 구현한다.
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 에러 로깅 등을 수행한다.
console.error("Error occurred:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>문제가 발생했습니다. 잠시 후 다시 시도해 주세요.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
또한, Next.js 13의 App Router에서는 error.js 파일을 만들어 페이지 단위로 에러 UI를 설정할 수 있다. 이를 통해 사용자에게 일관된 에러 화면을 제공한다.
Next.js는 이미지, 폰트, 스크립트 등의 정적 자산을 최적화하는 다양한 기능을 제공한다. 이를 통해 페이지 로딩 속도와 SEO 성능을 향상시킨다.
가장 대표적인 예는 next/image 컴포넌트이다. 이 컴포넌트는 이미지를 자동으로 최적화하며, 필요한 경우 Lazy Loading을 지원한다.
예시 코드는 다음과 같다.
import Image from 'next/image';
function OptimizedImage() {
return (
<Image
src="/images/sample.jpg"
alt="최적화된 이미지 예시"
width={800}
height={600}
priority // 첫 화면에 필수적인 이미지라면 우선 로딩
/>
);
}
export default OptimizedImage;
또한, Next.js는 정적 자산을 빌드 시에 압축하고, CDN 캐싱을 적용하여 전 세계 사용자에게 빠른 응답을 제공한다.
Route Handler는 Next.js에서 특정 경로에 대한 요청을 처리하는 서버 측 로직을 의미한다. 이 기능을 통해 API 라우트를 구현하거나, 동적 요청에 따른 맞춤 응답을 제공할 수 있다.
예를 들어, app/api/hello/route.js 파일을 생성하면 다음과 같이 API 요청을 처리할 수 있다.
// app/api/hello/route.js
export async function GET(request) {
return new Response(JSON.stringify({ message: "Hello, World!" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
Server Action은 서버 측에서 직접 함수를 실행해 데이터를 처리하거나 업데이트하는 기능이다. 이를 통해 클라이언트와 서버 간의 역할을 명확히 분리하면서도 간단한 서버 로직을 구현할 수 있다.
예를 들어, 아래 코드는 서버 측 액션을 사용하여 폼 데이터를 처리하는 예시이다.
// app/actions/submitForm.js
"use server"; // 이 파일이 서버 전용임을 선언
export async function submitForm(formData) {
// 서버 측에서 데이터 처리 로직을 구현한다.
const response = await fetch("https://api.example.com/submit", {
method: "POST",
body: formData,
});
return response.json();
}
이와 같이 Route Handler와 Server Action을 활용하면, 클라이언트 코드에서 복잡한 서버 로직을 분리해 관리할 수 있으며, 코드의 재사용성과 유지보수성이 크게 향상된다.