Dog foot print

Next.js@v13 Pages Directory 단점 본문

Next.js/고찰

Next.js@v13 Pages Directory 단점

개 발자국 2023. 11. 22. 14:30

Next.js@v13의 등장

Next.js@v13은 너무나도 많은 변화로 인하여, 많은 프론트엔드 개발자들이 기존 프로젝트 마이그레이션과 변화에 적응하는데, 큰 고통을 받고 있다. 물론 편리하고 좋은 기능들이 많이 추가 되었지만 여전히 일부 신규기능들은 “프로덕트 레벨에서 사용하기에 불안하다”라는 것이 중론인 것 같다.

 

가장 큰 변화로는 pages 폴더를 이용한 라우팅 방식에서 app 폴더를 이용한 라우팅인데 기존 pages 디렉토리에서 사용되던 레이아웃, API, 미들웨어 등 거의 모든 것들이 변화했다. 특히 기존 getServerSidePropsgetStaticSideProps를 이용하여, 초기 페이지 화면에 필요한 모든 값들을 최상단에서 호출 하는 것과는 달리, server-component를 활용하여 데이터가 필요한 컴포넌트에서 패칭하는 형태로 변경된 점이 가장 큰 변화이다.

 

react@18next.js@13이 배포로부터 아직 1년도 채 되지않았고 기존에 사용하던 client-componentpages가 멀쩡히 잘 굴러가고 있는데 pages를 대체할 app을 만든 것인가 ? 라는 궁금증이 생겨났다. 그래서 pages 가 가졌던 단점을 파악해보려고 한다.

Pages Router의 단점

Pages라우터는 getServerSidePropsgetStaticSideProps를 이용해서, 초기 페이지 구성에 필요한 값들을 최상단에서 구성하여, 아래의 그림과 같이 하위 컴포넌트에게 데이터를 전달하는 방법을 취한다.

 

위와 같이 매우 간단한 방법을 통하여, 초기 페이지 요청에서 필요한 데이터를 구성하여 화면을 보여줄 수 있지만 다음과 같은 문제점을 야기한다.

  1. 초기에 받아올 데이터의 수가 많아지고 이를 다시 재구성하려면 getServerSidePropsgetStaticSideProps가 매우 비대해진다.
  2. getStaticSideProps를 사용한 페이지의 수가 많아질수록 빌드시간이 비약적으로 증가한다.
  3. 클라이언트 사이드에서 데이터 패칭이 발생할 때 warterFall 형태로 패칭되며 highLatency가 발생된다.
  4. 캐싱되었거나 이미 만들어진 페이지의 정보가 변경되면 새로 만들기 어렵다.

 

getServerSideProps와 getStaticSideProps로 전달 받은 초기 Props

초기 화면에 필요한 모든 데이터 패칭은 getServerSidePropsgetStaticSideProps에서 발생해야 한다. 이러한 이유로 초기 화면에 필요한 데이터들이 많아지고 이 데이터를 올바른 형태로 가공하기 위해서는 필연적으로 getServerSidePropsgetStaticSideProps에 집중되고 전달해야하는 Props의 크기가 커진다.

 

 

 

이렇게 생성된 Props는 클라이언트가 페이지를 요청할때, html에 JSON 형태로 제공되게 된다. 이때 만약 사용자의 권한이나 조건에 따라 화면에 필요한 데이터가 달라질 때, getServerSidePropsgetStaticSideProps에서 데이터를 가공하지 않으면 크롤러나 유저에게 그대로 노출된다. 그렇기에 이런점을 고려한다면 초기 데이터를 구성하는 함수에서 수행하는 일은 더욱 많아지게 된다.

getStaticPaths와 getStaticProps로 발생한 빌드시간 증가

next.js는 빌드중 미리 페이지를 구성하여, 사용자가 페이지를 요구시 미리 만들어 놓은 페이지를 전달하는 SSG를 지원한다. 이는 권한이나 조건에 따라 다른 화면을 보여주지 않고, 동일한 화면을 보여주어야하는 페이지에서 주로 사용된다. getServerSideProps와 달리 사용자가 요청할때마다 페이지에 필요한 데이터를 패칭하지 않아도 되며, 미리 만들어놓은 파일을 전달하는 형태이기 때문에 getServerSideProps에 비해 성능 상 이점이 존재한다. 그러나 미리 준비해야하는 페이지가 많아질수록 데이터 요청 레이턴시 * (페이지의 수 / 최대 병렬적 페이지 생성의 수) + (페이지 수 * 렌더링 시간) 에 따라 빌드시간이 비약적으로 증가되게 된다.

클라이언트 컴포넌트의 waterfall

Note : 이는 page route의 문제라기보다는 client-component가 가지는 특성으로 봐야한다.

페이지 로딩에 걸리는 시간을 최소화하기 위해서 초기 페이지를 구성하는 데이터는 본문을 제외하고는 거의 대부분 client-side에서 데이터를 패칭한다. 이 경우, 클라이언트 사이드에서 데이터를 패칭하는 동안 사용자는 스켈레톤 UI나 로딩 스피너와 같은 fallback ui를 보게된다.

async function getDatas() {
  return new Promise((res) => setTimeout(res, 1000));
}

function Component({ apiKey }: { apiKey: number }) {
  const [data, setData] = useState<number>();
  const [isLoading, setIsLoading] = useState(false);
  useEffect(() => {
    setIsLoading(true);
    getDatas()
      .then(() => {
        setData(apiKey + 1);
      })
      .finally(() => setIsLoading(false));
  }, []);

  if (isLoading) return <div>Loading</div>;

  return (
    <div>
      {apiKey} + 1 = {data}
    </div>
  );
}

export default function Page() {
  const [datas, setDatas] = useState<number[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    setIsLoading(true);
    getDatas()
      .then(() => {
        setDatas([1, 2, 3, 4]);
      })
      .finally(() => setIsLoading(false));
  }, []);

  if (isLoading) return <div>hello</div>;

  return (
    <div>
      {datas.map((apiKey) => (
        <Component key={apiKey} apiKey={apiKey} />
      ))}
    </div>
  );
}

 

만약 UI를 구성하는데 있어 데이터 패칭이 트리 형태처럼 단계적으로 호출하게 되면, 사용자는 계단처럼 UI가 완성되는 것을 보게된다. 또한 사용자의 데이터 환경에 따라 상이하게 UI가 완성되기 때문에 이는 사용자 경험에도 좋지 못하다.

캐싱되었거나 이미 만들어진 페이지의 정보가 변경되면 즉시 업데이트하기 어렵다.

 

getStaticProps로 만들어진 페이지는 revalidate 의 값에 따라 사용자 방문 후 새롭게 페이지를 생성 할 수 있는 옵션이 있다. 이러한 페이지는 콘텐츠의 특성에 따라 변경되는 시점을 예상해야하기 때문에 매우 부정확하게 컨텐츠가 새롭게 리로드 될 수 있다. 이로인하여, 모든 getStaticPropsrevalidate를 0으로 하여, 방문마다 새롭게 페이지를 만드는 방법이 유행하기도 하였다.

반응형
Comments