[Next.js] 페이지 렌더링 방법

문지은·2023년 10월 14일
1

Next.js - Page Router

목록 보기
3/11
post-thumbnail

Next.js 13, Pages Router 을 기준으로 작성되었습니다.

페이지와 렌더링 방법

  • pages 디렉터리 아래에 배치된 TSX 등의 파일은 파일 1개가 페이지 1개에 대응한다.
    • 예를 들어, pages/index.tsx 라는 파일은 /에 접속했을 때 반환하는 페이지를 구현한 것이다.
  • 이 파일들은 리액트 컴포넌트를 반환하는 함수를 정의하고, 그 함수를 익스포트 한다.
    • 관습적으로 익스포트한 함수와 파일명은 동일하게 작성한다.
  • 다음과 같이 pages/sample.tsx를 작성하고 npm run dev로 개발 서버를 실행하면 localhost:3000/sample 이라는 URL로 접근할 수 있다.
function sample() {
  return (
    <div>sample 페이지 입니다.</div>
  )
}

export default sample

  • 파일 내부에서 컴포넌트 위에 구현하는 함수나 해당 함수의 반환 값에 따라 렌더링 방법이 달라지는데, 각 페이지들이 어떤 타입으로 설정돼 있는지 빌드시 결과로 확인할 수 있다.
  • 빌드 결과에서 Server 가 SSR을, Static과 SSG가 SSG를, ISR이 ISR를 나타낸다.
  • CSR은 모든 페이지 타입과 함께 사용할 수 있으며 기본적으로는 이 타입들에 포함돼 있으므로 빌드 결과에는 표시되지 않는다.
npm run build

Next.js의 페이지와 페이지 취득

  • Next.js 에서는 구현하는 함수나 해당 함수의 반환 값에 따라, pages의 렌더링 방법이 달라짐.
  • 렌더링 방법을 결정하는 주요한 요소는 데이터 취득 함수
종류데이터 취득에 사용하는 주요 함수데이트 취득 시점
SSGgetStaticProps빌드 시(SSG)데이터 취득을 전혀 수행하지 않는 경우도 SSG에 해당
SSRgetServerSideProps사용자 요청 시(서버 사이드)getInitialProps를 사용해도 SSR
ISRrevalidate를 반환하는 getStaticProps빌드 시(ISR)ISR은 배포 후에도 백그라운드 빌드가 실행된다.
CSR그 밖의 임의의 함수(useSWR 등)사용자 요청 시(브라우저)CSR은 SSG/SSR/ISR 과 동시에 사용 가능
  • pages는 그 종류에 따라 데이터 취득에 사용할 수 있는 함수가 다름
  • 페이지 컴포넌트에서 모든 표시 부분을 구현할 필요는 없음.
    • 페이지 사이에서 공통으로 사용하는 코드나 UI 부분은 pages 디렉터리 밖에서 정의하고 임포트 해서 사용할 수 있음.

SSG를 통한 페이지 구현

  • pages 아래에 새롭게 ssg.tsx 라는 이름의 파일을 추가하고, 페이지 컴포넌트를 구현해보자.
  • NextPage 는 pages를 위한 타입
    • 받을 props를 결정하고, NextPage<Props> 와 같이 지정
import { NextPage } from 'next'  // 타입을 위해 도입
import Head from 'next/head'  // Next.js의 내장 컴포넌트

// 페이지 컴포넌트의 props 타입 정의
type SSGProps = {}

// SSG용 페이지 구현
// NextPage는 Next.js의 Pages용 타입
// NextPage<props>에서 props가 들어가는 Page임을 명시

const SSG: NextPage<SSGProps> = () => {
    return (
        <div>
            {/* Head 컴포넌트로 감싸면, 그 요소는 <head> 태그에 배치됨 */}
            <title>Static Site Generation</title>
            <link rel="icon" href="/favicon.io" />
            <main>
                <p>
                    이 페이지는 정적 사이트 생성을 통해 빌드 시 생성된 페이지입니다.
                </p>
            </main>
        </div>
    )
}

export default SSG
  • npm run build 로 빌드를 실행하고, npm run dev 실행시 브라우저에서 http://localhost:3000/ssg에 접속하면 구현한 페이지가 표시됨을 확인할 수 있음.

getStaticProps를 사용한 SSG를 통한 페이지 구현

  • 파일 안에 getStaticProps라는 함수를 정의하고 익스포트하면, 해당 함수는 빌드 시 실행된다.
  • getStaticProps는 반환값으로 props를 반환할 수 있으며, 그 값이 페이지 컴포넌트에 전달되어 그려진다.
  • pages/ssg.tsxgetStaticProps를 추가하고, 다시 빌드해보자.
import { GetStaticProps, NextPage, NextPageContext } from 'next'  // 타입을 위해 도입
import Head from 'next/head'  // Next.js의 내장 컴포넌트

// 페이지 컴포넌트의 props 타입 정의
type SSGProps = {
    message: string;
}

// SSG는 getStaticProps가 반환한 props를 받을 수 있다.
// NextPage<SSGProps>는 Message: string만을 받아 생성된 페이지 타입
// Next.js의 페이지 컴포넌트나 함수 타입은 공식 문서 참고

const SSG: NextPage<SSGProps> = (props) => {
    const { message } = props;
    return (
        <div>
            {/* Head 컴포넌트로 감싸면, 그 요소는 <head> 태그에 배치됨 */}
            <title>Static Site Generation</title>
            <link rel="icon" href="/favicon.io" />
            <main>
                <p>이 페이지는 정적 사이트 생성을 통해 빌드 시 생성된 페이지입니다.</p>
								<p>{message}</p>
            </main>
        </div>
    );
};

// getStaticProps는 빌드 시 실행된다.
// GetStaticProps<SSGProps>는 SSGProps 인수로 받는 getStaticProps 타입

export const getStaticProps: GetStaticProps<SSGProps> = async (context) => {
    const timestamp = new Date().toLocaleString();
    const message = `${timestamp}에 getStaticProps가 실행됐습니다.`;
    console.log(message);
    return {
        // 여기에서 반환된 props를 기반으로 페이지 컴포넌트를 그린다.
        props: {
            message,
        },
    };
};

export default SSG
  • npm run build를 실행하면 getStaticProps 안에 있는 console.log가 빌드 도중 실행되는 것을 확인할 수 있다.

  • npm run start 실행해서, 페이지를 표시하면 getStaticProps에서 반환한 props를 사용해 페이지를 표시하고 있는 것을 확인할 수 있다.

  • npm run dev 를 사용해 개발 서버를 실행하는 경우에는 최신 코드를 사용해 페이지를 표시하기 때문에, 요청이 있을 때마다 getStaticProps가 실행되고 서버에서 페이지를 생성

  • getStaticProps는 익스포트(export)해야 하며, 비동기 함수로서 async와 함께 정의해야함
    • getStaticProps의 인수에는 context가 부여된다.
    • context에는 빌드 시에 함께 사용할 수 있는 데이터가 포함되어 있다.
export async function getStaticProps(context) {
	return {
		props: {}
	}
}
  • context는 실행 관련 정보가 모인 객체로, context.locale과 같은 형태로 접근가능하다.
파라미터내용
params경로 파라미터, SSG의 경우에는 getStaticPaths 함수를 별도로 정의했을 때 참조 가능
locale현재 로케일 정보(가능한 경우)
locales지원하는 로케일 배열(가능하는 경우)
previewPreview Mode 여부
previewDataPreview Mode에서 setPreviewData에 따라 설정된 데이터

getStaticPaths를 사용한 여러 페이지의 SSG

  • 이번에는 사용자 프로필이나 게시글 페이지 등 표시하는 데이터만 다른 페이지를 만들어보자.
  • Next.js 의 동적 라우팅(Dynamic Routing) 기능을 사용하면 된다.
  • 동적 라우팅은 다음 2가지 요소로 구성된다.
    • [파라미터].tsx 와 같이 [] 로 감싼 특별한 파일명
    • getStaticProps에 맞춰 getStaticPaths를 사용한다.

getStaticPaths

  • getStaticProps 실행 전에 호출되는 함수로, 생성할 페이지의 경로 파라미터 조합(paths)과 폴백(fallback)을 반환한다.
  • paths는 경로 파라미터의 조합을 나타내며, 배열의 각 요소가 1개의 페이지에 대응한다.
  • fallbackgetStaticPaths가 생성하는 페이지가 존재하지 않는 경우의 처리를 기술한다.
export async function getStaticPaths() {
    return {
        paths: [
            { params: { ... } }
        ],
        fallback: false  // true 또는 'blocking' 지정 가능
    }
}
  • pages/posts 디렉터리를 새롭게 만들고, [id].tsx(pages/posts/[id].tsx) 라는 파일을 만든다.
    • 대괄호로 감싼 부분이 경로 파라미터를 나타낸다.
import { GetStaticPaths, GetStaticProps, NextPage } from "next";
import Head from 'next/head';
import { useRouter } from "next/router";
import { ParsedUrlQuery } from "querystring";

type PostProps = {
    id: string;
}

const Post: NextPage<PostProps> = (props) => {
    const {id} = props
    const router = useRouter()

    if (router.isFallback) {
        // 폴백 페이지용 표시를 반환한다.
        return <div>Loading...</div>
    }

    return (
        <div>
            <Head>
                <title>Create Next app</title>
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main>
                <p>이 페이지는 정적 사이트 생성을 통해 빌드 시 생성된 페이지입니다.</p>
                <p>{`/post/${id}에 대응하는 페이지입니다.`}</p>
            </main>
        </div>
    )
}

// getStaticPaths는 생성한 페이지의 경로 파라미터 조합을 반환한다.
// 이 파일은 pages/posts/[id].tsx 이므로, 경로 파라미터로서 id의 값을 반환해야 한다.
export const getStaticPaths: GetStaticPaths = async () => {
    const paths = [
        {
            params : {
                id: '1',
            },
        },
        {
            params : {
                id: '2',
            },
        },
        {
            params : {
                id: '3',
            },
        },
    ]

    // fallback을 false로 설정하면, paths에 정의된 페이지 외에는 404 페이지를 반환한다.
    return {paths, fallback: false}
}

// 파라미터 타입을 정의
interface PostParams extends ParsedUrlQuery {
    id: string
}

// getStaticPaths 실행 후에 각 경로에 대해 getStaticProps가 실행된다.
export const getStaticProps: GetStaticProps<PostProps, PostParams> = async (context) => {
    return {
        props: {
            id: context.params!['id'],
        }
    }
}

export default Post
  • getStaticPaths 에서는 id가 각각 1, 2, 3 인 경로 파라미터를 반환하고 /posts/1, /posts/2, /posts/3의 3개 경로의 페이지를 생성한다.
  • 여로 경로 파라미터를 사용하는 경우, paths의 각 요소에 추가 파라미터를 더할 수 있다.
  • paths의 각 요소에 대해 getStaticProps가 호출되고, 페이지가 생성된다.
  • getStaticProps에는 contextparams로부터 경로 파라미터를 참조할 수 있다.
  • getStaticPathsfallbackfalse로 반환하면 paths에 주어지지 않은 경로에 대해서는 404 페이지를 표시한다.
    • fallbacktrue를 지정하면, 최초 요청과 그 뒤의 요청에서 작동이 달라진다.
    • 우선, 가장 첫 번째 방문한 사용자에 대해서는 폴백 페이지를 처음에 표시한다.
    • 이것은 페이지 컴포넌트의 props가 빈 상태로 화면이 그려진 페이지이다.
    • 서버 사이드에서는 요청의 경로에 대해 getStaticProps를 실행한다. getStaticProps가 반환한 props는 페이지를 표시하고 있는 클라이언트에 전송되고 화면을 그린다.
    • 또한 서버 사이드에서 props를 기반으로 페이지를 그리고 그 결과를 저장한다.
    • 그 뒤, 동일한 경로에 대한 요청이 오면 저장했던 페이지를 반환한다.

  • fallback: false 인 경우

  • fallback: true 인 경우

SSR을 통한 페이지 구현

  • SSR에서는 접근할 때마다 서버에서 페이지를 그리고, 그 결과를 클라이언트에서 표시한다.
  • SSG의 getStaticProps에 대해, SSR에서는 getServerSideProps를 정의한다.
  • SSR에서는 페이지를 그리기 전에 getServerSideProps 를 호출하며, 이 함수가 반환한 props 를 기반으로 페이지를 그린다.
  • pages/ssr.tsx를 새롭게 작성하고, 다음 코드를 추가해보자.
import { GetServerSideProps, NextPage } from "next";
import Head from 'next/head';

type SSRProps = {
    message: string
}

const SSR: NextPage<SSRProps> = (props) => {
    const {message} = props
    
    return (
        <div>
            <Head>
                <title>Create Next App</title>
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main>
                <p>이 페이지는 서버 사이드 렌더링을 통해 접근 시에 서버에서 그려진 페이지 입니다.</p>
                <p>{message}</p>
            </main>
        </div>
    )
}

// getServerSideProps는 페이지로의 요청이 있을 때마다 실행된다.
export const getServerSideProps: GetServerSideProps<SSRProps> = async (
    context
) => {
    const timestamp = new Date().toLocaleDateString()
    const message = `${timestamp}에 이 페이지의 getServerSideProps가 실행됐습니다.`
    console.log(message)

    return {
        props: {
            message,
        }
    }
}

export default SSR
  • 접근할 때마다 표시되는 내용이 변하기 때문에, 매번 서버에서 getServerSideProps가 호출되고 페이지가 그려지는 것을 알 수 있다.

  • getServerSideProps의 인수인 context 에는 getStaticPropscontext에서 참조할 수 있는 데이터와 함께 요청 정보들을 참조할 수 있다.
파라미터내용
reqhttp.IncomingMessage의 인스턴스에서 요청 정보나 쿠키(Cookie)를 참조할 수 있다.
reshttp.ServerResponse의 인스턴스에서 쿠키를 설정하거나, 응답 헤더를 치환할 때 사용할 수 있다.
resolveUrl실제로 접근이 있던 경로
query해당 쿼리를 객체로 만든 것

ISR을 통한 페이지 구현

  • 점진적 재생성(ISR)은 SSG의 응용이라 말할 수 있는 렌더링 방법
  • 특징으로 페이지 수명을 정할 수 있으며, 수명을 지난 페이지에 대해서는 최신 정보로 재생성을 시도하고, 정적 페이지를 전송하면서 정보를 업데이트할 수 있다.
  • ISR에는 revalidate를 반환하는 getStaticProps를 사용한다. getStaticProps에서 revalidate를 반환하면 그 값이 유효 기간이 되며, 유효 기간이 지난 페이지는 재생성된다.
  • 다음 코드는 ISR을 사용한 예이다. (pages/isr.tsx)
    • getStaticProps를 정의하고, getStaticProps에서는 props와 함께 revalidate를 반환한다.
    • revalidate는 페이지의 유효 기간을 초로 나타낸 것을 반환한다.
import { GetStaticPaths, NextPage, GetStaticProps } from "next";
import Head from 'next/head'
import {useRouter} from 'next/router'

type ISRProps = {
    message: string
}

// ISRProps를 받는 NextPage(페이지) 타입
const ISR: NextPage<ISRProps> = (props) => {
    const {message} = props

    const router = useRouter()

    if (router.isFallback) {
        return <div>Loading...</div>
    }

    return (
        <div>
            <Head>
                <title>Create Next App</title>
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main>
                <p>이 페이지는 ISR을 통해 빌드 시 생성된 페이지입니다.</p>
                <p>{message}</p>
            </main>
        </div>
    )
}

export const getStaticProps: GetStaticProps<ISRProps> = async (context) => {
    const timestamp = new Date
    const message = `${timestamp}에 이 페이지의 getStaticProps가 실행됐습니다.`
    
    return {
        props : {
            message,
        },
        // 페이지의 유효 기간을 초 단위로 지정
        revalidate: 60
    }
}

export default ISR
  • 가장 처음으로 페이지에 접근한 경우에는 SSG와 마찬가지로 폴백 페이지가 표시되고, 서버 측에서 실행한 getStaticProps를 기반으로 클라이언트에서 다시 화면을 그린다.
  • 그 이후의 요청에 대해서는 revalidate에서 지정한 시간 내에서는 서버 사이드에서 그려서 저장하고 있던 페이지(같은 페이지)를 반환한다.
  • 유효 기간이 지난 뒤 요청이 있는 경우에는, 해당 요청에 대해서는 현재 저자오대 있는 페이지를 반환한다.
  • 그리고 getStaticProps를 실행하고 페이지를 그려 새로운 캐시로 저장한다.

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글