본문 바로가기

개발/Next.js

Next.js Page Router

이 글은, 제가 학습한 내용들을 정리함과 동시에 Next.js를 공부하시는 분들께 조금이나마 도움이 되었으면 좋겠습니다~

 

먼저 Next.js 버전은 메이저 버전으로 15버전까지 나와 있는 상태다. 그래서 13버전 이후엔 App Router 기반으로 동작되지만, Page Router는 Next 초창기부터 제공되어 오던 구 버전 라우터다.

나 또한, Page Router 기반의 Next.js 만 사용해봐서 이번 기회에 Page Router를 다시 한 번 정리하고 15버전의 App Router를 공부할 계획!

학습은 공식문서와 인프런의 이정환 튜터님의 Next.js v15 강의를 토대로 정리하였다.

 

Next.js 특징들은 대표적으로 어떤게 있을까?

  • Page Routing
  • Optimizations
  • Server Pre Rendering

 

Page Router에 대해 먼저 살펴보자.

 

그럼 Next.js 12 version 까지는 Page Router로 동작되었는데 이 Page Router라는 녀석 도대체 어떻게 동작 하는걸까?

Page Router는 파일 시스템 기반 라우팅으로 동작된다.

 

즉, pages/ 디렉토리 내에 파일을 생성하면 해당 파일의 이름이 곧 URL 경로가 된다.

예를 들어, pages/search.tsx 파일을 만들면 /search 경로에서 이 페이지에 접근이 가능하게 되는 것.

아마, Next.js Page Router로 한 번 이상 사용했다면 아는 내용일 거다.

 

다음으로, 사전 렌더링에 관련하여 개념을 살펴보자면..

사전 렌더링 (Pre Rendering)? 브라우저 요청에 사전에 렌더링이 완료된 HTML을 응답하는 렌더링 방식이다.

 

그럼! React와 Next.js 간의 렌더링 방식 차이를 간략하게나마 살펴보자.

React에서의 렌더링 과정은?

1. 유저가 클라이언트를 통해 요청 → 서버로 전달

2. 서버가 클라이언트에 index.html 빈껍데기를 보내준다.

3. 클라이언트는 유저에게 빈 화면 렌더링을 제공

4. 서버는 클라이언트에 자바스크립트 파일을 번들링해서 후속으로 보내준다.

5. 리액트앱이 자바스크립트 파일이 실행이되서 컨텐츠 렌더링이 된다. (이때 유저가 요청한 화면이 보여짐)

 

리액트의 가장 큰 장점은 초기접속 요청 이후에 페이지 이동이 매우 빠르다!

(서버에게 새로운 페이지를 요청할 필요가 없다. 브라우저가 리액트 앱을 가지고 있고 리액트 앱에는 모든 페이지에 필요한 컴포넌트가 포함되어 있기 때문.)

하지만..? 위 방식을 따르기 때문에 초기접속요청이 매우 느리다.

FCP : 요청 시작 시점으로부터 컨텐츠가 화면에 처음 나타내는데 걸리는 시간.

(3초 이상일 경우 이탈률 32%증가, 5초 이상일 경우 90% 증가)

즉, 리액트는 FCP가 꽤나 늦는다는걸 알 수 있다.

 

Next.js에서의 렌더링 과정은?

  1. 유저가 클라이언트를 통해 요청 → 서버로 전달
  2. 서버측에서 미리 직접 JS 실행. (사전 렌더링) 렌더링 된 HTML을 브라우저에게 전달
  3. 브라우저는 그대로 화면에 렌더링하여 유저는 바로 화면을 볼 수 있음.

Next.js 에서 렌더링 과정은 리액트에비해 굉장히 심플하다고 볼 수 있지만 Next.js 에서 사전렌더링 방식이 여러가지가 존재한다.

 

서버사이드 렌더링(SSR)

가장 기본적인 사전 렌더링 방식이다. 요청이 들어올 때 마다 사전렌더링 진행하는 방식인데 코드를 살펴보면 다음과 같다.

 

처음 메인화면을 로드했을때 SSR렌더링으로 모든책과 추천책을 동시에 받아오도록 하는 코드다. 여기서 Next.js 내부에서 지원하는 Type인 InferGetServerSidePropsType 을 활용하면 사전렌더링에서 받게 되는 데이터의 타입을 자동으로 유추할 수 있도록 지정할 수 있다.

 

정적 사이드 생성(SSG)

SSG 방식은 설명이 좀 있어야 하는데, 특징들은 다음과 같다.

- 빌드 타임에 미리 페이지 사전 렌더링 (SSG는 요청이 한 번만 진행되서 빌드타임 이후엔 페이지를 새롭게 생성하지 않기 때문에 매 번 똑같은 페이지만 응답하기 때문에 최신 데이터 반영은 어렵다.)

- SSR이 항상 최신의 데이터로 유지할수 있지만, 데이터 요청이 늦어질 경우 모든게 늦어지는 단점이 존재하여 이런 단점을 보완하기 위해 SSG 렌더링 방식을 사용할 수 있다.

- SSR을 사용하다가 코드를 수정했을시 개발모드에선 SSG방식을 사용하더라도 SSR처럼 동작된다. 따라서 빌드 후에 다시 개발모드로 코드를 실행시켰을때 빌드된 시점으로 코드가 동작된다.

- url에 쿼리 스트링이 담긴 데이터를 SSG로 패칭할 수 없다. 따라서, SSR로 데이터를 받든, 아니면 기존 컴포넌트에서 useEffect + state로 관리해줘야 한다.

- 동적경로에 SSG를 적용하기 위해선 사전 렌더링 이전에 경로들을 설정해줘야 한다.

위와 같이 getStaticPaths와 getStaticProps 함수를 export하여 사전렌더링 SSG 방식을 사용할 수 있는데, 처음에 서버가 요청을 받으면 JS bundle을 만들때 위 paths에 담겨진 녀석들만 미리 만들어준다. 이후엔 fallback 값에 따라 동작방식을 약간씩 변경이 가능하다

 

증분 정적 재생성(ISR)

SSG방식으로 생성된 정적페이지를 일정 시간 주기로 다시 생성하는 기술 → 최신데이터를 반영하기 어려운 SSG방식의 단점을 해결해준 방식. (SSG + SSR 방식의 장점들을 어느정도 결합한 방식)

즉, 특정 시기 주기로 데이터를 바꿔야 하는 페이지에 적용해주면 좋은 방식이다.

 

export const getStaticProps = async () => {
  const [allBooks, recoBooks] = await Promise.all([
    fetchBooks(),
    fetchRandomBooks(),
  ]);

  return {
    props: {
      allBooks,
      recoBooks,
    },
    revalidate: 3,
  };
};

기존에 SSG 방식을 위한 getStateProps 함수내에서 리턴문 2번째인자로 revalidate에 시간(s)를 지정해주면 해당 시간마다 getStateProps 함수가 실행이 된다.

하지만 위와 같은 방식은 시간과는 관계없이 사용자 행동에따라 즉각적으로 데이터를 업데이트 해야하는 게시글 작성, 수정 같은 기능들에 적용할 수 없는 방식인데… 그래서 Next.js는 이런 단점도 보완하고자 On-Demand ISR 방식이 존재한다!

 

 

import { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    await res.revalidate("/");
    return res.json({ revalidate: true });
  } catch (err) {
    res.status(500).send("Revalidation Failed");
  }
}

On-Demand ISR : 요청을 받을때마다 ISR 페이지를 다시 생성

→ 오늘날 대부분의 구축된 Next App에서 많이 사용되는 방법

특정 페이지에서 revlidate하기 위해 api 폴더 내에 위와 같이 만들어주었다. 위 api를 실행시키면 내가 어떤 특정 행동을 한 이후에 특정 데이터만 변경된다.

 

 

이로써, Page Router가 무엇이고 여러 사전렌더링 방식을 살펴보았는데 PageRouter의 단점과 장점은 무엇이 있을까?

장점: 파일시스템 기반 간편한 페이지 라우팅 제공, 다양한 방식의 사전렌더링 제공 (SSR, SSG, ISR)

단점 ? 

  1. 페이지별 레이아웃 설정이 번거로움
  2. 데이터 페칭이 페이지 컴포넌트에 집중된다 → 컴포넌트가 많아질시, 페이지 컴포넌트 데이터 페칭이 되면 그 자식의 컴포넌트들에 데이터를 내려보내줘야 하므로 데이터 관리가 어려워지며 성능적인 부분에서도 문제가 생길 수 있다.
  3. 불 필요한 컴포넌트들도 JS Bundle에 포함된다. → 가장 큰 단점이지 않을까 싶다. 파일의 크기가 커지면 당연히 초기 JS 로드 시간에 영향을 주기 때문.

 

뭔가 정리하다보니.. 단점이 더 많아서 Page Router가 굉장히 안좋아 보이는것 처럼 보여지는데, 나는 Page Router만 사용해왔던 사람으로써 크게 불편함을 겪지 못했고 오히려 React보다 더 많은 기능들을 쓸 수 있었어서 만족하면서 써왔다. 

하지만, Next.js 13 version 이후에 서버컴포넌트라는 개념과 현재 Next.js 15 version에서의 캐싱 방식의 개선, 부분 프리렌더링 기능 등을 통해 더 편리하게 개발을 할 수 있을 거라고 느꼈다.

 

다음엔, App Router에 대해 정리해보려고 한다.