Next.js - force-dynamics 실패 사례

#Next.js
• • •

문제 1. 서버 컴포넌트에서 원하는 함수가 매번 실행되지 않음

현상

  • 노트 페이지에 진입할 때 shuffle()로 리스트를 섞으려고 했음
  • 하지만 매번 같은 순서의 리스트를 보여줌
@/app/note/page.tsx
const Notes = async () => {
  const data = getPostData("note");

  return (
    <div className="flex flex-col">
      <StaggerGridList count={data.length}>
        {shuffle(data).map(async ({ slug, metadata: { title }, content }) => {
          const exceprt = await highlightMarkdown(getExcerpt(content));
          return (
            <SquareLink
              key={slug}
              title={title}
              href={`/note/${slug}`}
              excerpt={exceprt}
            />
          );
        })}
      </StaggerGridList>
    </div>
  );
};
export default Notes;

원인

  • Notes 컴포넌트는 파일 시스템을 통한 정적 데이터만 사용하고 있음
  • Next.js는 정적 데이터를 다루는 컴포넌트는 정적 사이트로 생성함
  • SSG 페이지는 동적으로 렌더링되지 않음

조치

@/app/note/page.tsx
export const dynamic = "force-dynamic";

// .. 이하 코드 동일
강제로 동적 렌더링

문제 2. 배포된 서버에서 파일 시스템에 접근하지 못하는 이슈

  • 현상: 배포 이후 노트 페이지에서 500 에러 발생
  • 원인: 파일 시스템에 접근하지 못함
  • 조치: process.cwd()를 사용하여 안정적인 경로 접근이 가능하도록 수정

문제 3. 동적 렌더링으로 인한 지연

현상

  • force-dynamic 옵션을 적용하여 동적 렌더링 강제하도록 하니까 노트 페이지 진입할 때 약간의 지연 발생

원인

  • export const dynamic = "force-dynamic"; 사용하여 페이지를 항상 동적 렌더링(SSR)하도록 강제
  • 페이지 요청 시마다 서버에서 getPostData("note") 실행하여 파일 시스템에서 게시글 데이터 읽음
  • 읽어온 데이터에 대해 shuffle, getExcerpt, highlightMarkdown 연산을 서버 렌더링 과정 중 수행
  • 단순 순서 섞기를 위해 매 요청마다 파일 시스템 접근 및 전체 데이터 처리 로직을 서버에서 처리

조치

  • export const dynamic = "force-dynamic"; 제거하여 컴포넌트를 다시 SSG로 생성
  • 서버 컴포넌트 (page.tsx)와 클라이언트 컴포넌트 (NoteListClient.tsx)로 분리
  • getPostData 실행 및 데이터 전처리(getExcerpt, highlightMarkdown)를 서버에서 수행
  • 처리된 데이터 배열 (processedNotes)을 클라이언트 컴포넌트에 props로 전달
  • shuffle은 클라이언트 컴포넌트에서 수행
const Notes = async () => {
  const data = getPostData("note");

  const processedNotes = await Promise.all(
    data.map(async ({ slug, metadata: { title }, content }) => {
      const excerpt = await highlightMarkdown(getExcerpt(content));
      return { slug, title, excerpt };
    }),
  );

  return (
    <div className="flex flex-col">
      <NoteList notes={processedNotes} />
    </div>
  );
};

export default Notes;

후기

  • 빌드 과정에서 많은 일을 처리해보자.
  • 가급적 정적 사이트를 만들어보자.
  • 무분별한 force-dynamics 사용은 조심하자.
  • 런타임에서 파일 시스템 접근이 필요할 때 파일 경로 잘 설정하자.
  • 서버에서 할 일과 클라이언트에서 할 일을 잘 구분하고 설계하자.
published 10 months ago · last updated 10 months ago