← 전체 글로 돌아가기

웹 개발

404 페이지가 막다른 길이 되지 않게 하기

404 페이지에 홈 링크 하나 없으면 사용자는 뒤로 가기 버튼을 눌러 이탈한다. 최소한의 탈출구를 만들어두는 것이 맞다.

깨진 링크를 타고 들어온 사용자가 404 페이지에서 볼 것은 에러 메시지와 뒤로 가기 버튼뿐인 경우가 많다. 사이트에서 제공하는 다른 콘텐츠로 이어지는 경로가 없으니 이탈 외에 선택지가 없다.

Next.js에서 404 페이지 커스터마이징

App Router 기준으로 app/not-found.tsx 파일을 만들면 404 응답 시 이 페이지가 렌더링된다.

// app/not-found.tsx
import Link from 'next/link'

export default function NotFound() {
  return (
    <main style={{ padding: '4rem', textAlign: 'center' }}>
      <h2>페이지를 찾을 수 없습니다</h2>
      <p>요청한 주소가 존재하지 않거나 이동되었습니다.</p>
      <Link href="/">홈으로 돌아가기</Link>
    </main>
  )
}

Pages Router라면 pages/404.tsx에 동일하게 작성한다.

링크 하나에서 더 나아가기

홈 링크만 있어도 기본은 된다. 여기에 사이트 검색이나 최근 글 목록을 붙이면 사용자가 원하는 걸 찾을 가능성이 높아진다.

export default function NotFound() {
  return (
    <main>
      <h2>페이지를 찾을 수 없습니다</h2>
      <nav>
        <Link href="/"></Link>
        <Link href="/posts">전체 글 보기</Link>
      </nav>
    </main>
  )
}

블로그라면 최근 포스트 목록을 서버에서 가져와 보여주는 것도 방법이다. not-found.tsx는 서버 컴포넌트이므로 async/await로 데이터를 바로 불러올 수 있다.

HTTP 상태 코드 확인

커스텀 404 페이지를 만들었더라도 실제로 404 상태 코드를 반환하는지 확인해야 한다. 브라우저 개발자 도구 네트워크 탭에서 응답 코드를 보거나 curl로 확인한다.

curl -o /dev/null -s -w "%{http_code}" https://example.com/없는-경로
# 404

Next.js는 기본적으로 not-found.tsx에서 404를 응답한다. 하지만 동적 라우트에서 직접 notFound()를 호출하지 않으면 200을 반환하면서 빈 페이지를 그릴 수도 있으니 주의한다.

// app/posts/[id]/page.tsx
import { notFound } from 'next/navigation'

export default async function PostPage({ params }) {
  const post = await getPost(params.id)
  if (!post) notFound()  // 이 호출이 없으면 404가 아닌 200으로 응답된다
  return <article>{post.content}</article>
}

작은 부분이지만, 이탈률에 영향을 미치고 검색 엔진 크롤러도 404를 제대로 인식해야 인덱스에서 빠진 링크를 처리할 수 있다.