ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Next.js] Pre-Rendering
    Front-End(Web)/React - 프레임워크(React, Next) 2021. 7. 7. 01:45
    반응형

    오늘도 역시, Next.js의 주요기능인 Pre-Rendering에 대해 포스팅하려고 한다.

    이전에, Next.js 입문에서 작성한 Pre-Rendering 부분을 좀 더 구체화하며, 관련된 메서드까지 소개하기 위함이다!

    * 이전 포스팅(Next.js 입문) : https://abangpa1ace.tistory.com/177

     

    [Next.js] Next.js 입문

    🤔 Next.js를 시작하며.. 정말 오랜만에 블로그를 쓰는것 같다. 이제 입사 1개월이 되어 task를 맡기도 하고, 구현에 맘이 앞서다 보니 중간중간 찾아본 주요한 정보들을 정리하는 시간을 가지지 못

    abangpa1ace.tistory.com

     

    React 개발을 하면서, Data Fetch를 위해 componentDidMount() 라이프사이클 혹은 useEffect() 훅을 애용한다.

    Boiler-Plate 마운팅이 끝나면 비동기 요청을 하는 보편적인 방법이나, 화면이 두 번 렌더링된다는 단점이 존재한다.

     

    이러한 과정을 SSR시에 미리해주면서, 렌더링 효율과 SEO 최적화를 향상시키는 Pre-Rendering 기법에 대해 알아보도록 하겠다!


    🤍 Pre-Rendering

    Pre-Rendering은 Next.js에서 아주 중요한 개념이다.

     

    Next.js에서 모든 페이지가 사용자에게 전해지기 전에 HTML을 미리 생성해 Pre-Rendering을 수행하기 때문이다.

    이는, Next.js가 Client-Side Javascript로 모든 작업을 수행하는 대신, 미리 각 페이지에 대한 HTML을 구성하는 것이다.

     

     

    - Pre-Rendering 과정

    1. Initial Load : JS동작이 없는 HTML을 먼저 화면에 보여준다. JS파일 로드전으로 <Link> 등은 동작하지 않음

    2. Hydration : Initial Load 후 JS파일을 HTML에 연결하는 과정이다. React 컴포넌트가 초기화되는 과정이다.

     

    * 만약 Pre-Rendering에 없다면?

    Pre-Rendering이 없다면 모든 JS파일이 로드되고 초기화될 때까지 빈 페이지가 노출되거나 로딩중임을 명시해야 할 것이다.

    이는, 모든 파일이 로드되기 전까지 페이지를 볼 수 없다는 뜻이며, 결국 UX 측면에서 낮은 평가를 받게 될 것이다.

     

    - Pre-Rendering의 2가지 모드

    이전에도 언급한, Pre-Rendering의 2가지 모드이다.

    Static GenerationServer-side Rendering 이라 칭하며, 둘의 차이는 언제 페이지를 위한 HTML을 구성하는지다.

     

    1) Static Generation(SSG)

    HTML이 build 시 생성되며, pre-render 된 페이지는 매 request에 따라 활용/재활용된다.

    Next.js의 default Pre-Rendering 방법이자, 공식문서에서 추천하는 방법이다.

     

    SSG는 동일한 HTML을 매 요청마다 생성하는 SSR의 단점을 보완하기 위해 탄생한 기법이다.

    Next.js 내부에 존재하는 Pre-Render 메서드가 최초에 HTML을 Build 할 때 동작하고, 이는 CDN에 캐싱되어 다음 요청시부터 재사용된다.

     

    퍼포먼스가 중요한 마케팅 페이지, 블로그 게시물, 제품 리스트 등을 정적 생성하여 각 요청에 동일한 문서를 반환한다.

     

    * 개발모드(dev)에서 Static Generation(SSG) 을 사용하는 페이지라도, 모든 페이지가 각 Request에 Pre-Render 된다.

     

    2) Server-side Rendering(SSR)

    HTML이 매 request마다 생성되는 기본적인 SSR 동작이다.

    SSG보다 재이동 측면에서 느리나, 항상 최신 데이터를 유지한다는 특징이 있다.

     

    항상 최신상태를 유지해야하거나 요청마다 다른 내용을 보여줘야하는 제품 상세페이지, 분석 차트 등 시기적절한 HTML을 반환해야 할 때 사용된다.

     

    3) CSR

    그렇다면 Next.js에서는 Client-Side Rendering을 사용하지 않는가? 그렇지 않다!

    기본적으로 SPA는 CSR 방식을 전재하며, Next는 SSG(or SSR)을 연계한 CSR 전략으로 운용한다고 생각하면 된다.

     

    가령, 데이터 변동이 빈번하게 일어난다면, 기존 React처럼 클라이언트 사이드에서 Data-Fetching 하는 것을 권고한다.

    유저보드가 대표적인데, 이는 개인적인 영역이어서 외부 노출이나 SEO 필요성이 적고, 유저의 수정을 즉각적으로 화면에 표출해야 하므로 CSR을 권고하는 것이다.


    🤍 Pre-Rendering Methods

    Next.js 에서는 Pre-Rendering을 위한 메서드들을 지원한다. (v9.3 이전까지는 getInitialProps() 로 일괄적으로 처리함)

    이를 통해, 서버에서 미리 필요한 데이터들을 패칭한 뒤 HTML 파일을 렌더링하는 것이다.

     

    Next.js v9.3 부터는 SSR, SSG를 지원하는 새로운 메서드가 등장한다. 

    이는 바로, getStaticProps(), getServerSideProps(), getStaticPaths() 로 세분화되며, 공식문서도 이를 추천한다.

     

    - Methods 개념 및 사용법

     

    1) getStaticProps()

     

    Static Generation(SSG) 시 활용하는 메서드이다. build 시 한번 호출되고, static하게 빌드되므로 이후 수정되지 않는다.

    최초에만 패치를 진행하므로 성능적으로 우수하나, 최신상태를 유지할 수 없다는 단점이 있다.

    const About = ({ list }) => {
      // const [list, setList] = useState([]);
    
      // useEffect(() => {
      //   const getList = async () => {
      //     const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`);
      //     const data = res.data;
      //     setList(data);
      //   };
      //   getList();
      // }, []);
    
      return (
        <div className="About">
          <h1>여기는 About 페이지요!</h1>
          {list.length &&
            list.slice(0, 10).map((item) => <li key={item.id}>{item.title}</li>)}
        </div>
      );
    };
    
    export default About;
    
    export const getStaticProps = async (context) => {
      const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`);
      const data = res.data;
    
      console.log(data[1]); // 해당 콘솔은 어디에서 출력이 되나요?
    
      return {
        props: {
          list: data,
        },
      };
    };

    getStaticProps() 라는 비동기 함수를 선언하고, 그 안에서 axios 요청을 통해 props를 반환해주면 된다.

    이 메서드가 해당 컴포넌트에 props 형태로 패치된 데이터를 내려주기 때문에, 컴포넌트의 매개변수로 받아오면 된다.

    이렇게 내보내진 getStaticProps() 는 build 시 작동하고, 함수가 외부 데이터를 받아와 props로 내려주는 것이다.

     

    또한, 이 getStaticProps() 는 기본적으로 context를 매개변수로 받으며, 여기의 다양한 필드값들을 참조할 수 있다.

    • params : 동적 라우팅의 쿼리값을 받는다. [id] 등의 path 값을, { id: ... } 의 객체 형태로 받는다.
    • preview : 페이지가 preview 모드인지 여부
    • previewData : preview 모드에서의 데이터
    • locale : 활성화된 locale(지역/언어)
    • locales : 부가적인 locale(지역/언어)
    • defaultLocale : default locale(지역/언어) 

     

    그리고, 반환값으로 다음과 같은 종류들이 있다. (모든 반환값은 필수항목은 아니다.)

    • props : 해당 페이지 컴포넌트에 하달할 props
    • revalidate : SSG가 다시 발생하는(데이터를 최신화하는) 시간간격을 의미한다. 기본값은 false
    • notFound : 404 status를 페이지로 반환할지 여부를 의미
    • redirect : 패치 데이터가 없는 경우, redirect 하는 경로를 설정한다. 값은 Object로, { destination, permanent }

     

    2) getServerSideProps()

     

    Server-Side Rendering(SSR) 시 활용하는 메서드이다. 매 요청시마다, 내부의 데이터 패치 로직이 발생한다.

    Link 태그로 이동하는 상세 페이지처럼, 각 path나 환경에 따라 새로운 페이지가 로드되어야 하는 상황에 적합하다.

    import React from "react";
    import axios from "axios";
    
    const Detail = ({ item }) => {
      return (
        <div className="Detail">
          <h1>{item.title}</h1>
          <p>{item.body}</p>
          <p>{item.id}번째 게시글</p>
        </div>
      );
    };
    
    export default Detail;
    
    export const getServerSideProps = async (ctx) => {
      const id = ctx.params.id;
      const res = await axios.get(
        `https://jsonplaceholder.typicode.com/posts/${id}`
      );
      const data = res.data;
    
      return {
        props: {
          item: data,
        },
      };
    };

    getServerSideProps() 도 마찬가지로 비동기 함수를 선언하고, 그 안에서 axios 요청을 통해 props를 반환해주면 된다.

    아무래도, 상세 페이지와 같은 경우에서 인자로 받는 context의 params와 같은 필드를 참조하는 경우가 많을 것이다.

     

    또한, 이 getServerSideProps() 의 context는 좀 더 많은 필드값들을 참조할 수 있다.

    • params, preview, previewData, locale, locales, defaultLocale
    • req, res : HTTP 요청, 응답
    • query : 쿼리 스트링
    • resolvedUrl : 인자로 받은 URL 정보가 들어오며, _next/data 와 연관성이 있다고 한다.

     

    3) getStaticPaths()

     

    위에서 언급했듯 동적 라우팅에는 getServerSideProps() 가 주로 사용되나, 상세 페이지와 같은 경우에도 미리 정적 생성하고자 하는 경우가 있다.

    이 때, 특정 path 값을 지정하여 SSG로 생성하기 위한 매서드가 getStaticPaths() 다.

    import React from "react";
    import axios from "axios";
    
    const DetailStatic = ({ item }) => {
      return (
        <div>
          {item && (
            <div className="Detail">
              <h1 style={{ color: "#fff" }}>with Static Generation</h1>
              <h1>{item.title}</h1>
              <p>{item.body}</p>
              <p>{item.id}번째 게시글</p>
            </div>
          )}
        </div>
      );
    };
    
    export default DetailStatic;
    
    export const getStaticPaths = async () => {
      return {
        paths: [
          { params: { id: "1" } },
          { params: { id: "2" } },
          { params: { id: "3" } },
        ],
        fallback: true,
      };
    };
    
    export const getStaticProps = async (ctx) => {
      const id = ctx.params.id;
      const res = await axios.get(
        `https://jsonplaceholder.typicode.com/posts/${id}`
      );
      const data = res.data;
    
      return {
        props: {
          item: data,
        },
      };
    };

    getStaticPaths() 에서 설정한 경로들에 대해 getStaticProps() 를 통한 SSG가 적용되는 모습이다.

    이를 build 하면, server/pages 폴더에 각각의 id에 대한 HTML 파일이 생성된다.

     


    최근에 회사선배와 SSR에 대해 얘끼하면서, Vue의 Nuxt.js는 SSG를 할 때 node 서버가 추가적으로 필요하다고 들었다.

    Next.js의 경우엔 별도의 소요는 없지만, 내부적으로 Node.js 서버를 가지고 있는 프레임워크라고 한다.

    1. 굳이 SEO 적용 또는 데이터 pre-rendering이 필요 없다면 CSR 방식
    2. 정적 문서로 충분한 화면이면서 빠른 HTML 문서 반환이 필요하다면 SSG 방식
    3. 매 요청마다 달라지는 화면이면서 서버 사이드로 이를 렌더링 하고자 한다면 SSR 방식

     

    이 외에도 NEXT가 가지고 있는 기능은 다양하다. React 에서는 별도의 npm library인 react-helmet을 설치해서 SPA의 meta 태그들을 수정하는 반면, NEXT는 _document.js 에서 기본설정 후 각 컴포넌트에서 필요한 메타 태그들을 직접 수정할 수도 있고, pages 폴더 내부의 파일들은 기본적으로 코드 스플리팅이 적용되어 렌더링된다. 

     

    아마, 사이드 방향성이 바뀌면서 Next.js 학습을 잠시 중단하게 되겠지만, 이후 공부한다면 코드 스플리팅과 SEO 등의 기능에 대해 추후 작성하게 될 것 같다.

     

    [출처]

    - Next.js 공식문서 : https://nextjs.org/learn/basics/data-fetching/setup  

    - LongRoadHome 님의 블로그(SSR vs SSG) : https://velog.io/@longroadhome/FE-SSRServer-Side-Rendering-%EA%B7%B8%EB%A6%AC%EA%B3%A0-SSGStatic-Site-Generation-feat.-NEXT%EB%A5%BC-%EC%A4%91%EC%8B%AC%EC%9C%BC%EB%A1%9C  

    - hyounglee 님의 블로그(Pre-Rendering) : https://velog.io/@hyounglee/TIL-93  

    - beside-lab 블로그(Pre-Render Methods) : https://beside-lab.tistory.com/entry/Nextjs-getStaticProps-vs-getServerSideProps-%EC%B0%A8%EC%9D%B4%EC%99%80-%ED%99%9C%EC%9A%A9

    반응형
Designed by Tistory.