Liontown Study - Next.js(2)

SSO·2022년 5월 23일
1

LikeLion10th

목록 보기
15/16

이번 포스팅에서는 next.js의 getInitialProps에 대해서 알아보고자 한다.
쉽게 말해 getInitialProps는 data를 fetching하는 방법 중 하나라고 생각하면 된다.
주로 CSR인 React에서는 axios나 fetch함수를 사용했었는데 next.js에서는 getInitialProps와 다른 기타 메서드들을 사용한다!


서버사이드 렌더링을 하는 next.js에서 컴포넌트는 각 페이지마다 사전에 불러와야할 데이터가 있다.(이하 data fetching)
react, vue같은 클라이언트 라이드 렌더링(CSR)의 경우는 useEffect, created함수를 이용해 data fetching을 한다.

서버사이드에서 실행하는 next에서는 getInitialProps를 이용해 data fetching 작업을 한다!

next v9 이상에서는 getInitialProps대신 getStaticProps, getStaticPaths, getServerSideProps을 사용하도록 가이드 하니 주의 ! -> 밑에서 추가로 설명할 예정입니당


next.js가 SSR을 수행하는 방식

next.js는 SSR을 기반으로 하긴 하지만 페이지가 로드된 이후에는 CSR(Client Side Rendering)을 이용하는 방식을 차용한다.

  • 페이지는 서버가 그리는 것으로 pages/안에 폴더를 만들면 해당 라우팅의 페이지들은 서버측에서 먼저 로드해준다.
  • 페이지가 그려진 이후에 페이지 내부에서 동적인 데이터를 패치하는 과정은 CSR방식을 따른다. 이때의 데이터들은 일단 페이지가 로드된 이후에 클라이언트 측에서 다시 불러와지기 때문에 seo문제에 걸리지 x

ServerSide Cycle

  1. Next Server가 GET 요청을 받는다.
  2. 요청에 맞는 Page를 찾음.
  3. get



getInitialProps

react를 예시로 들면 react는 로직에 따라 컴포넌트가 마운트 되고 난 후 하는 경우가 많다.
이 과정을 서버에서 미리 처리하도록 도와주는 것이 바로 getInitialProps

data fetching을 서버에서 해주면 좋은 점

  • 속도가 빨라진다. 브라우저에서의 연산을 서버와 함께 하면서 미리 데이터를 받아오고 브라우저는 렌더링만 할 수 있기 때문.
  • 코드 상의 처리가 깔끔해지고 로직 파악이 쉽다. 렌더링하는 함수와 data fetching을 하는 함수가 분리되어 로직 파악이 쉽고 initial한 데이터가 들어오는 과정을 전제로 코드를 작성할 수 있다.

사용법

fetching할 mok data

{
  "test": {
    "title": "test post",
    "content": "test content"
  },
  "second": {
    "title": "second post",
    "content": "second content"
  }
}
 

알아보기 전에 동적 url이란?
→ 가변적으로 변하는 url에 대해 동적 url을 지원. []문법으로 동적 페이지를 생성하는 동적 url을 만들 수 있다!

동적 url example

import { useRouter } from "next/router";

export default () => {
  const router = useRouter();

  return (
    <>
      <h1>post</h1>
      <p>postid: {router.query.id}</p>
    </>
  );
};
 

위처럼 작성하고 localhost:3000/123으로 접속하면 postid가 123으로 나온다.
pages/[값].tsx왼쪽 페이지 구조의 router.query.값과 동일!!



import { useEffect } from "react";
import { useRouter } from "next/router";
import posts from "../posts.json";

const Posts = () => {
  const router = useRouter();
  const post = posts[router.query.id];

  return (
    <>
      <h1>{post.title}</h1>
      <h1>{post.content}</h1>
    </>
  );
};

export default Posts;
 

위의 코드를 실행 후 localhost:3000/test로 접속하면 post값이 없기 때문에 title이 없다는 에러가 발생한다.
SSR인 next.js에서는 getInitialProps를 이용해 데이터를 미리 받아오고, 렌더링 할 당시에는 이미 값이 있기 때문에 렌더링이 되는 방식으로 사용한다.

import { useEffect } from "react";
import { useRouter } from "next/router";
import posts from "../posts.json";

const Posts = (props: { post: { title: string; content: string } }) => {
  const router = useRouter();

  return (
    <>
      <h1>{props.post.title}</h1>
      <h1>{props.post.content}</h1>
    </>
  );
};

Posts.getInitialProps = context => {
  // context.query.id = 'test'
  return {
    post: posts[context.query.id]
  };
};

export default Posts;
 

getInitialProps내부에는 context, component 등 여러 객체가 있으며 그 중 query.id에 접근하여 우리의 url인 'test'를 받아오고, 데이터에서 test 객체를 꺼내와 post에 담는다.

context 객체??
context 객체는 아래의 키를 포함한다.

  • params : 동적 경로를 사용하는 페이지에 대한 정보
  • preview : preview 모드 여부 (공식문서 참조)
  • previewData : setPreviewData로 설정된 데이터
  • req : HTTP 요청 정보
  • res : HTTP 응답 객체
  • query : 쿼리 문자열

🔊주의사항

하나의 페이지에서는 하나의 getInitialProps만 실행!!!

이 이슈는 매우매우 중요하다.
페이지가 렌더링 될 때 next내부에서 거치는 순서는 _app -> page component이다.
만약 _app에서 getInitialProps를 정의했다면, 하위 컴포넌트에서는 실행되지 않는다!
하위 컴포넌트에서도 getInitialProps 값을 반영하려면 아래와 같이 _app.tsx에 코드를 추가해야 한다.


app.tsx

import "./globals.css";

function MyApp({ Component, pageProps }) {
  return <Component ponent {...pageProps} />;
}

MyApp.getInitialProps = async ({ Component, ctx }) => {
  let pageProps = {};
  // 하위 컴포넌트에 getInitialProps가 있다면 추가 (각 개별 컴포넌트에서 사용할 값 추가)
  if (Component.getInitialProps) {
    pageProps = await Component.getInitialProps(ctx);
  }

  // _app에서 props 추가 (모든 컴포넌트에서 공통적으로 사용할 값 추가)
  pageProps = { ...pageProps, posttt: { title: 11111, content: 3333 } };

  return { pageProps };
};

export default MyApp;




getStaticProps

위에서 처음에 설명했듯이 next.js v9 이상에서는 getStaticProps, getStaticPaths, getServerSideProps을 사용하도록 가이드한다.

getStaticProps는 빌드 시 데이터를 fetch하여 static페이지를 생성한다. 고정되는 값으로 빌드 이후 값 변경이 불가능하다.

언제 필요한가?

  • 페이지에 필요한 데이터가 빌드 시에 사용 가능할 때
  • 데이터를 headless CMS에서 가져올 때
  • 모든 사용자에게 같은 데이터를 보여줄 때
  • SEO를 위해서 속도 빠른 페이지가 필요할 때
  • Node api(path, fs 등)을 사용해야 할 때
  • 변하지 않는 공개적인 캐시 데이터를 가져올 필요가 있을 때

사용방법

export async function getStaticProps(context){
  return{
  	props : {}, // page component의 props로 전달되는 객체
  };
}

Example

function Blog({ posts }) {
  return (
    <ul>
      {posts.map(post => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}

export async function getStaticProps() {
  // 외부 api endpoint로 호출하면 post 정보를 가져온다
  const res = await fetch("https://.../posts");
  const posts = await res.json();

  // post 데이터가 담긴 props를 빌드 시간에 Blog 컴포넌트에 전달
  return {
    props: {
      posts
    }
  };
}

export default Blog;



getStaticPaths

pages/--/[id].tsx 형태의 동적 라우팅 페이지 중 빌드 시에 static하게 생성할 페이지를 정한다.
이곳에 정의하지 않은 하위 경로는 접근해도 페이지가 뜨지X.
동적라우팅 되는 경우의 수 따져서 하위로 넣을 수도 있음.

언제 쓰는데?

  • 페이지는 사전에 렌더링되어야 하고(SEO) 매우 빨라야 할 경우.
  • 동적라우팅 + getStaticProps 를 원할 경우 사용!

페이지가 동적라우팅을 사용하고 있고 getStaticProps를 쓰는 경우 getStaticPaths 를 통해 빌드 타임 때 정적으로 렌더링할 경로를 설정해야 한다.

//빌드될 때 실행
export async function getStaticPaths() {
  return {
    //빌드 타임 때 아래 정의한  /dyna/1,  /dyna/2, ... /dyna/동적인값 경로만 pre렌더링.
    paths: [
      { params: { dynamic: 1 } },
      { params: { dynmic: 2 } }
      ......
      { params: { dynmic: 동적인값 } }
    ],
    // 만들어지지 않은 것도 추후 요청이 들어오면 만들어 줄 지 여부.
    fallback: true,
  }
}

위에서 path는 빌드타임에 pre-rendering할 경로들이다



getServerSideProps

서버사이드 렌더링(SSR) 시 활용하는 메서드.
해당 메서드는 런타임 환경에서 페이지에 접근 시 서버 측에서 실행된다.

언제 필요한가?

  • 우선 미리 렌더링 해야하는 경우에만 사용한다!
  • 외부로부터 데이터를 요청해 페이지에 렌더링이 필요한 경우 사용
  • 단 이 메서드는 페이지 컴포넌트에 접근할 때마다 서버에서 항상 실행되기 때문에 getStaticProps보다는 속도가 느리고 추가 구성 없이는 결과 데이터를 캐싱할 수 X
  • 데이터를 미리 렌더링 할 필요가 없는 경우 클라이언트 측에서 데이터를 패칭하는 것이 더 효율적!!

사용방법

export async function getServerSideProps(context){
	return{
    	props: {},
    }
}

Example

function Page({ data }) {
  // 데이터 렌더링
}

// This gets called on every request
export const getServerSideProps: GetServerSideProps = async context => {
  // 외부 api로 부터 받은 데이터 패칭
  const res = await fetch(`https://.../data`);
  const data = await res.json();

  // page 컴포넌트에 props로 전달
  return { props: { data } };
};

export default Page;

자세한 내용은 공식문서와 기타 자료들을 참고해주세요🙌🏻

profile
Github_qkrthdus605

0개의 댓글