DEVELOP
article thumbnail

인프런 '박용주 지식 공유자님' 의 [Next.js 시작하기] 강의를 수강하고 작성한 게시글입니다.

 

Next.js 시작하기(feat. 지도 서비스 개발) - 인프런 | 강의

Next.js의 기본을 다루는 강의입니다. Next.js로 지도 서비스를 처음부터 끝까지 개발해봅니다., - 강의 소개 | 인프런...

www.inflearn.com


getStaticProps

  • gatStaticProps 함수에서 리턴된 props를 받아서 pre-rendering할 수 있다

 

▼ src/pages/section1/getStaticProps.tsx

  • ▽ 2초 대기 후 랜덤한 값을 data로 리턴하고 그 값을 받아 화면에 출력하는 기능을 한다.
import type { NextPage } from 'next';

interface Props {
    data: number;
}

const Example: NextPage<Props> = ({ data }) => {
    return (
        <main>
            <h1>getStaticProps Page</h1>
            <p>값 : {data}</p>
        </main>
    );
};

export default Example;

export const getStaticProps = async () => {
    const delayInSeconds = 2;
    const data = await new Promise((resolve) => {
        setTimeout(() => resolve(Math.random()), delayInSeconds * 1000);
    });
    return {
        props: { data },
    };
};
  • http://localhost:3000/section1/getStaticProps 접속 했을 때, 2초 뒤에 HTML이 생성된다.
  • 새로고침하면 data 값이 바뀌고 HTML이 재생성되어 새로고침마다 2초가 걸림

  • 2초마다 계속해서 바뀌는 방식은 원하는 SSG 방식이 아님
    -> 계속 값이 바뀌는 이유는 현재 Next를 실행하고 있는 환경이 개발환경이기 때문 
  • 개발환경에서는 getStaticProps가 매 요청마다 실행된다고 함
    ( 공식 문서 : https://nextjs.org/docs/basic-features/data-fetching/get-static-props )

공식문서

  • 개발환경 모드가 아닌, production 모드로 실행하기 위해 빌드하고, 서비스 시작해 줌 
$ npm run build
$ npm run start
  • 새로고침해도 처음의 값이 유지되며 HTML이 재생성되지 않고, 314ms로 적은 시간이 걸림 
    HTML의 헤더를 확인해보면 변하지 않는 정적인 파일이므로 항상 캐시에 hit하는 것을 알 수 있음

💡빌드 타임에 API를 한 번만 불러오고, 배포된 이후에는 그 값이 변하지 않는다면 SSG 방식으로 렌더링하면 된다!  

 

  • 하지만 상황에 따라 가끔 API의 결과가 바뀌는 경우가 있을 수도 있음 
  • 하지만 값이 바뀐다고 매번 사이트 전체를 새로 배포하는 것도 문제가 있고, SSR방식으로 대체하기에는 SSG의 장점을 포기하기 힘듦
    => NextJS에서는 revalidate라는 속성을 지원함 

revalidate

  • ISR(Incremental Static Regeneration, 증분 정적 재생성) 방식을 지원하기 위해 만든 속성 
  • 이미 빌드가 완료된 사이트에서 주기적으로 정적인 페이지를 업데이트 할 수 있음
  • 해당 페이지만 업데이트하는 것이기 때문에 전체 사이트를 다시 빌드할 필요가 없음

    ▽ revalidate 값을 5초로 설정 

export const getStaticProps = async () => {
    const delayInSeconds = 2;
    const data = await new Promise((resolve) => {
        setTimeout(() => resolve(Math.random()), delayInSeconds * 1000);
    });
    return {
        props: { data },
        revalidate: 5,
    };
};
  • 약 7초를 주기로 값이 바뀌는 것을 확인할 수 있다. 
  • 5초동안 캐시가 hit로 유지되다가 2초동안 stale로 유지하고 또 다시 5초동안 hit가 보이고... 를 반복한다.

  • 즉, revalidate : 5 라는 것은 
    5초마다 (서버가 request를 받은 지 5초가 지난 후, 다시 request가 왔을 때마다) 이 함수를 다시 실행해서 만약 데이터가 바뀌었으면 새로운 값으로 다시 pre-rendeing하라는 뜻이다. 
  • 만약 데이터가 랜덤으로 생성한 값이 아닌 고정된 값이라면 ? setTimeout(() => resolve(100), delayInSeconds * 1000)
    => props의 값이 변하지 않았으므로, 프리렌더링을 다시 수행하지 않는다

next/Link : SSG+CSR

▼ src/pages/links.tsx

   ▽ /getStaticProps를 클릭하면 /section1/getStaticProps로 이동

import Link from 'next/link';
const Links = () => {
    return (
        <main>
            <h1>Links</h1>
            <Link href="/section1/getStaticProps">/getStaticProps</Link>
        </main>
    );
};

export default Links;
  • Link 페이지는 HTML 파일을 받아온다.
  • 페이지를 이동할 때는 더이상 추가적인 HTML 을 받아오지 않고 json 파일만 가져온다.
  • 최초 실행은 SSG로 실행되지만 페이지를 라우팅할 때는 CSR 방식으로 빠르게 이동 

json 파일 추가됨

  • SSG + CSR 결합한 라우팅을 할 수 있는 것은 next / link의 장점이다

  • 해당 페이지에 대한 정보를 JS파일로 이미 가지고 있어서 HTML을 불러오지 않는다.
  • 즉 ,JS파일과 json이 결합하여 바로 DOM을 업데이트 할 수 있고, CSR과 같은 역할을 한다.
  • SEO 보장함과 동시에 CSR 방식으로 라우팅 함으로써 빠른 라우팅과 적은 네트워크 요청 가능하다.
  • 만약 a태그를 사용한다면 CSR이 아니기 때문에 매번 HTML을 받아와야 한다.

  • 링크 클릭 전 빈 div를 생성해서 아래로 내려가야 링크가 보이도록 했을 때, 링크가 보이지 않을 때는 파일을 불러오지 않다가  파일을 누를 수 있을 때 lazy한 방식으로 파일을 가져온다.

링크가 보이기 시작할 때 json과 js 파일이 생성됨

  • next12에서는 Link가 a태그를 완전히 대체하지 않았기 때문에 Link에 자식으로 a태그 추가해야했다.
  • Link태그에 직접 style이나 className등을 지정할 수 없고, 자식 태그인 a태그에 지정해야했는데, 그러려면 Link 태그에 legacybehavior 키워드를 추가해야한다.

  • next13부터는 Link에 style이나 className등을 바로 사용 가능하다.

useRouter

▼ src/pages/links.tsx

   ▽ /getStaticProps 버튼을 클릭하면 /section1/getStaticProps로 이동

import Link from 'next/link';
import { useRouter } from 'next/router';

const Links = () => {
    const router = useRouter();
        <main>
            <h1>Links</h1>
            <button
                onClick={() => {
                    router.push('/section1/getStaticProps');
                }}
            >/getStaticProps
            </button>
        </main>
    );
};

export default Links;
  • next/link 처럼 CSR 방식으로 json파일이 생성됨

  • next/link 대체가 가능하지만 다른 점은 prefetch가 안된다는 것 ( 버튼이 보인다고해서 미리 json 파일을 만들지 않음)
  • prefetch위해서는 개발자가 useEffect 사용 등으로 코드를 직접 구현해야함

  • 특별한 경우가 아니라면 next/link 사용을 권장함 


.next 폴더

  • build 하면 .next 폴더가 생성된다.
  • .next/static/chunks/section1 폴더에는 위에서 만들었던 getStaticProps-ad029998614cd2df.js 파일과 links-db3b4ecdac939204.js 파일이 있고,
  • 이 파일들은 브라우저에서 다운로드 하는 파일들이고, getStaticProps에 관한 정보가 담겨있다 

.next/static/chunks/section1

  • .next/server/pages/section1 에는 HTML과 json 파일이 존재한다.
  • 값이 변하면 해당 파일의 값도 변함
  • 새로고침 하기 전에 pre-rendering하기 때문에 화면에 보여지는 값보다 먼저 해당  html 파일의 값이 바뀐 것을 확인할 수 있다.

.next/server/pages/section1

 


getServerSideProps

▼ src/pages/section1/getServerSideProps.tsx

  • ▽ 2초 대기 후 랜덤한 값을 data로 리턴하고 그 값을 받아 화면에 출력하는 기능을 한다.
import type { GetServerSideProps, NextPage } from 'next';

interface Props {
    data: number;
}

const Example: NextPage<Props> = ({ data }) => {
    return (
        <main>
            <h1>getServerSideProps Page</h1>
            <p>값: {data}</p>
        </main>
    );
};

export default Example;

export const getServerSideProps: GetServerSideProps = async ({ res }) => {
    const delayInSeconds = 2;
    const data = await new Promise((resolve) =>
        setTimeout(() => resolve(Math.random()), delayInSeconds * 1000)
    );

    return {
        props: { data },
    };
};
  • 새로고침하면 2초동안 pending상태이다가, 2초가 지나야 결과가 렌더링된다.
  • SSR은 build time에 pre-rendering되는 것이 아니라 , request time,즉 페이지에 들어올 때마다 pre-rendering이 된다.
  • SSG에 비해 UX가 좋지 않다.
  • 💡매 request마다 SSR 해야하는 페이지에만  getServerSIdeProp를 사용해야 한다 
    • ex) 사용자의 인증정보에 따라 변하는 페이지,
      페이지가 동적으로 변해야 하지만 보안은 중요한 페이지 등

getServerSideProps

    res.setHeader(
        'Cache-Control',
        'public, s-maxage=5, stale-while-revalidate=10'
    );
  • 위의 코드를 추가해주면 revalidate 기능 가능하다.
    • 5초이내 새로고침하면 계속 hit가되면서 캐시된 HTML이 보여지게 된다.
    • 5~10초가 지나면 stale상태로돌입하면서 새롭게 뒤에서 프리렌더링된다.
    • 15초가 지난 후 새로고침하면 처음 진입시와 비슷하게 2초가 pending 상태가 된다.
  • 하지만 SSR에서 revalidate기능은 거의 사용할 일이 없다.

CSR

useEffect로 CSR하기

▼ src/pages/section1/clientSideRendering.tsx

  • ▽ 2초 후 랜덤한 값을 result로 받아 state인 data로 setData한다.
import type { NextPage } from 'next';
import { useEffect, useState } from 'react';

const Example : NextPage = () => {
    const [data, setData] = useState(0);

    useEffect(() => {
        const delayInSeconds = 2;
        new Promise<number>((resolve) => {
            setTimeout(() => resolve(Math.random()), delayInSeconds * 1000);
        }).then((result) => setData(result));
    }, []);

    return (
        <main>
            <h1> Client-side data fetching</h1>
            <p>값 : {data}</p>
        </main>
    );
};

export default Example;
  • 처음에는 초기상태인 0으로 HTML 을 pre-rendering한다.
  • 2초 뒤 자바스크립트 코드로 상태가 업데이트 되면서 DOM도 바뀌게 됨 

client side rendering

dynamic으로 CSR하기

▼ components/NoSSR.tsx

  • ▽ window화면의 너비를 화면에 출력한다.
const NoSSR = () => {
    return <p>width:{window.innerWidth}</p>;
};

export default NoSSR;

▼ src/pages/section1/clientSideRendering.tsx

import NoSSR from 'components/NoSSR';
/ * * /
            <p>값 : {data}</p>
            <h1>No SSR</h1>
/ * * /

  • window객체를 사용할 수 없다는 에러가 뜬다.
  • 기본적으로 페이지에 포함되는 컴포넌트는 서버에서 렌더링 되어야 하고, 서버에서는 window라는 객체를 알 수 없음
  • 브라우저에서만 객체에게 접근할 수 있기 때문이다.
  • 이처럼 SSR로 렌더링 하고 싶지 않은 컴포넌트가 있을 경우, 일반적인 import 문이 아니라next/dynamic을 이용해서 import 한다.

▼ src/pages/section1/clientSideRendering.tsx

import dynamic from 'next/dynamic';
/* */
const NoSSR = dynamic(() => import('../../../components/NoSSR'), {
    ssr: false,
});
  • window 객체에 접근할 수 있기 때문에 너비가 제대로 출력되었다.
  • NoSSR 컴포넌트는 서버에서 렌더링하지 않았기 때문에 (CSR) HTML에서 완전히 제거된 것을 확인할 수 있다.

 

profile

DEVELOP

@JUNGY00N