기술블로그를 제작하면서 Next.js 13.4버전을 사용하고 있었다.
각 포스트 페이지마다 SEO를 위해 post 데이터를 받아오고 metadata를 만들어서 넘겨주고 있었다.
next.js의 GET API를 작성해서 게시글을 찾아오고 있었고 만약 게시글이 존재하지 않는경우에는 404 error를 응답으로 보내주고 있었다.
//post/[slug]
export async function GET(request: NextRequest) {
if (!_post) {
return getErrorResponse(404, 'Post not found');
}
근데 ....... 404에러 이전에 500 error가 먼저 발생하는것이 아닌가..?
분명히 404에러를 보내주는데 왜 500에러가 발생하지?
하면서 코드를 확인했다.
post페이지에서 SEO를 위해 데이터를 서버에서 미리 받아와 metadata 생성해주고 있었다.
만약 존재하지 않는 포스트를 불러오게 할 경우 API에서 404응답을 보내주고있엇고
post를 받아오는 과정에서 에러가 발생했을 경우에 대한 핸들링 처리를 해놓지 않았기 때문에
서버사이드에서 실행 된 generateMetadata 함수에서 이뤄진 데이터패칭이 에러를 뱉으면서 500에러가 발생했던 것이였다..........
새벽이여서 그랬는지.. 이걸 약 세시간동안 잡고있었다....
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.content,
alternates: {
canonical: `/post/${post.id}`,
},
};
서버에서 에러가 발생해서 스크립트가 멈춰버리게 되니 렌더 될 데이터를 클라이언트에서 받을 수가 없었고,
클라이언트에서 error.tsx를 이용해 에러를 잡으려고 해도 잡히지 않았던 것이였다...
아래와 같이 try/catch를 이용해서 에러가 발생했을 때 알맞은 metadata 값을 리턴해주었다.
export async function generateMetadata({ params }: Props): Promise<Metadata> {
try {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.content,
alternates: {
canonical: `/post/${post.id}`,
},
};
} catch (error) {
return {
title: 'Not Found',
description: 'The post is not found',
};
}
}
페이지를 렌더링하면서 에러가 발생했던 부분은 error.tsx를 통해 렌더링 하는 부분만 따로 처리했다.
서버에서 발생한 에러로 인해 클라이언트에서 렌더링을 못하게된 경우에는 클라이언트에서 에러 핸들링을 해야했다.
error.tsx 파일은 런타임 오류를 잡아준다.
그리고 React Error Boundary 설정을 따로 하지 않아도 자동으로 생성한다.
reset 함수를 받아 에러가 발생한 부분을 다시 받아오려는 시도도 할 수 있다.

에러가 발생했을 때 화면에 error.tsx 내용만 렌더링 되는것이 아니고,
에러가 발생한 부분만 렌더링 된다.
나같은 경우에는 헤더와 사이드바는 유지되면서 Post가 렌더링 되는 부분에만 표기되도록 해놓았다.

아래와 같이 렌더링 할 코드를 작성해놓으면 적용시킨 부분만 렌더링된다
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>찾으시는 게시글이 존재하지 않습니다.</h2>
<a href='/posts'>목록으로 돌아가기</a>
</div>
);
}
이 문제를 해결하면서 App router와 page router의 에러핸들링 방식이 변경 된 사실도 알게되었고, root layout에서 global-error 로 핸들링 할 수있다는 것도 알게됐다.
이번 기회를 통해 에러핸들링에 대한 중요성을 한번 더 깨달은거 같다.
유저에게 더 좋은 사용성을 주려면 정말 중요한 부분이였는데.. 조금.. 민망했다.
앞으로 블로그 레이아웃도 수정해야되고 추가로 붙이고 싶은 기능도 많고,
제작하면서 이것저것 해보고 싶은게 많았는데 그 중에 하나가 errorboundary 적용이였다.
지금 앱 라우터를 이용하고 있기때문에 error.tsx 파일을 통해 손쉽게 에러바운더리를 정의할 수 있었지만 모든 프로젝트가 최신 버전을 사용하는 것은 아니다.
이전에 page router로 작업하고 있던 프로젝트에서 suspense와 errorboundary를 적용해보면서 추가적으로 공부를 더 해봐야겠다!