솔미는 성장중

[모던 리액트 딥다이브] 서버사이드 렌더링 본문

카테고리 없음

[모던 리액트 딥다이브] 서버사이드 렌더링

solming 2024. 2. 4. 17:32
728x90

Next.js란 ?

react-page에서 영감을 받아 만들어진 프레임워크. (서버사이드 렌더링)

 

nextJS는 기본적으로 pre-rendering을 한다!

  •  Pre-rendering : HTML을 미리 렌더링  ▶︎ hydration (js를 불러오며 인터랙션 가능해짐)
                                  브라우저처럼 동작하지 않는(JS를 실행하지 않는) 검색엔진에서도 데이터를 조회할 수 있기에 SEO 향상된다!
                                  (SSG(빌드타임에 pre-render), SSR(요청타임에 pre-render) 데이터 패칭 방식을 이용해야 함)
  • No Pre-rendering: JS가 load되어야지만 화면이 보임 (대부분의 SPA)

 

 

Next 시작하기

npx create-next-app@latest --ts

Next 기본 파일 (예약어로 관리되는 페이지) 뜯어보기

next.config.js

Next.js 프로젝트의 환경 설정을 담당

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true
};

export default nextConfig;
  • reactStrictMode
    : 리액트 애플리케이션 내부에서 잠재적인 문제를 개발자에게 알리기 위한 도구
  • swcMinify
    : 번들링과 컴파일을 더욱 빠르게 수행하기 위한 목적. Rust로 작성되었고, 병렬로 작업을 처리하기에 바벨보다 빠르다.

 

pages/_app.tsx

  • Next.js를 초기화 하는 파일로, Next.js 설정과 관련된 코드를 모아두는 곳이며, 경우에 따라 서버와 클라이언트 모두에서 렌더링 될 수 있다.
  • 주로 하는 일
    • 에러 바운더리 사용해 전역에서 발생하는 에러 처리
    • reset.css 선언
    • 모든 페이지에 공통으로 제공해야 하는 데이터 제공 (ex. 공통적인 제목)

예시) 공통 레이아웃 적용하는 방법

import Layout from "../components/Layout";

export default function App({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

 

pages/_document.tsx

  • Next.js로 만드는 웹사이트의 뼈대가 되는 HTML 설정과 관련된 코드를 추가하는 곳
  • 무조건 서버에서 실행
  • 기본 생성 X (필수 아님)

 

pages/_error.tsx

  • 기본 생성 X
  • 개발 모드에서 확인할 수 없는 페이지기 때문에 프로덕션으로 빌드해서 확인해봐야한다.
  • 프로젝트 전역에서 발생하는 에러를 적절하게 처리하고 싶다면 이 페이지를 활용

 

pages/404.tsx

  • 404 페이지 정의

 

pages/500.tsx

  • 500 에러 페이지 정의
  • _error.tsx와 500.tsx가 모두 있다면 500.tsx가 우선

Next 기본 특징 알아보기

1. Next.js는 라우팅이 파일명으로 이어지는 구조를 갖고 있다. 

  • /pages/index.tsx : 웹사이트 루트 = localhost:3000
    /pages/hello.tsx : hello.tsx나 hello/index.tsx와 같은 곳을 가리킴 = localhost:3000/hello
    /pages/hello/[greeting].tsx: [ ]는 어떠한 문자도 올 수 있다는 뜻. greeting이란 변수에 사용자가 접속한 주소명이 옴
                                                  localhost:3000/hello/1, localhost:3000/hello/hi 모두 유효 (greeting에 1, hi가 담김)
    /pages/hello/[...props].tsx : /hello를 제외한 /hello의 모든 하위 주소가 여기로 온다. 
                                                  ex) localhost:3000/hello/1, localhost:3000/hello/hi/world/foo
🌸 [ ]의 변수로 지정된 값을 사용하는 방법

// 클라이언트에서 값 가져오는 법
export default function Test({props: serverProps}: {props: string[]}) {
  const {query: {props}} = useRouter()
  
  return(
    <ul>
      {serverProps.map((item)=>(
        <li key={item}> {item} </li>
      ))}
    </ul>
  )
}

// 서버에서 값 가져오는 법
export default function Test(context: NextPageContext) => {
  const {query: {props}} = context
  
  return(
   props: {props}
  )
}​


props에 담기는 값 예시
/hi/1/2 : ['1','2']
/hi/my/name/is : ['my', 'name', 'is']

 

 

2. Next.js는 서버 사이드 렌더링 + 클라이언트 라우팅 모두 수행 가능하다!

서버 사이드 렌더링 프레임워크이기 때문에 최초 페이지 렌더링은 서버에서 수행된다.

 

왜 이렇게 작동할까?

ex) <a> 태그와 <Link> 태그 비교

<a> 태그는 모든 리소스를 처음부터 다 가져온다. 

서버에서 렌더링을 수행하고, 클라이언트에서 hydrate하는 과정에서 한 번 더 실행

 

<Link>를 이용한 경우 서버 사이드 렌더링이 아닌, 클라이언트에서 필요한 자바스크립트만 불러온 뒤 라우팅하는 클라이언트 라우팅/렌더링 방식으로 작동. 

 

❇️ 사용자가 빠르게 볼 수 있는 최초 페이지를 제공한다는 서버 사이드 렌더링의 장점 + 자연스러운 라우팅이라는 싱글 페이지 애플리케이션의 장점을 모두 살리기 위한 방향

  • 페이지 이동시에는 2가지 규칙을 지키자!
    - <a> 대신 <Link>사용하기
    -  window.location.push 대신 router.push 사용하기

 

 

3. getServerSidePRops를 제거하면 서버 사이드 렌더링이 필요없는 정적인 페이지로 분류된다

getServerSideProps가 없는 경우

  • 서버 사이드 렌더링이 필요없는, 빌드 시점에 미리 만들어도 되는 페이지로 간주
  • 빌드 시점에 미리 트리쉐이킹을 한다.

❇️ Next.js는 서버 사이드 렌더링 프레임워크이지만 모든 작업이 서버에서 일어나는 것은 아니다!

 

 

4. api라고 작성된 디렉토리는 서버의 API를 정의하는 폴더

ex) /pages/api/hello.ts

HTML 요청을 하는 게 아니라 단순히 서버 요청을 주고 받는다.

여기 있는 코드는 오직 서버에서만 실행됨

 


Data Fetching (데이터 불러오기 전략)

- pages/ 폴더에 있는 라우팅이 되는 파일에서만 사용가능

- 예약어로 지정되어 반드시 정해진 함수명으로 export 해야 한다.

 

❇️  서버에서 미리 필요한 페이지를 만들어서 제공하거나 해당 페이지의 요청이 있을 때마다 서버에서 데이터를 조회해서 미리 페이지를 만들어서 제공 가능

 

대표적인 3가지 함수

  • getStaticProps (Static Generation) : 프로젝트가 빌드될 때 데이터를 가져온다.
  • getStaticPaths (Static Generation) :  데이터를 기반으로 동적 라우트를 지정한다.
  • getServerSideProps (Server-Side Rendering) : 각 요청이 있을 때마다 데이터를 가져온다.

 

getStaticPaths & getStaticProps - 정적페이지

  • 사용자와 관계없이 정적으로 결정된 페이지를 보여주고자 할 때 사용
  • getStaticPaths를 사용할 땐 무조건 getStaticProps를 사용해야 함!!!!!!

언제 사용하나요? (When should I use getStaticProps?)

  • 페이지에 필요한 데이터가 빌드 시에 사용 가능할 때
  • 데이터를 headless CMS에서 가져올 때
    : headless CMS란? (headless Content Management System)
      보통 데이터를 전송 받을때 head에는 데이터를 보여줄 방법,수단을 명시하고 body에 컨텐츠를 담는다.
      즉 headless는 순수 데이터만 오는 컨텐츠 관리 시스템을 말한다.
  • 모든 사용자에게 같은 데이터를 보여줄 때
  • SEO를 위해서 속도 빠른 페이지가 필요할 때
    : 사용자가 접근할 수 있는 페이지를 모두 빌드해두고 배포하면 사용자는 굳이 페이지가 렌더링될 것을 기다릴 필요없이 완성된 페이지를 받기만 하면 된다!
    => 매우 빠름 (getStaticProps로 생성된 HTML, JSON파일은 효율성을 위해 CDN에 캐시됨)
  • Node api(path, fs 등)을 사용해야 할 때

// /pages/post/[id]가 접근 가능한 주소를 정의하는 함수
// /post/1과 /post/2만 접근 가능하고 나머지는 404
export const getStaticPaths: GetStaticPaths = async()=>{
  return{
    paths: [{params: {id:'1'},{params: {id:'2'}],
    fallback: false // true, "blocking"이라는 값을 설정할 수도 있음. 아래 글 참고
  }
}

// 앞서 정의한 페이지로 요청이 왔을 때 제공할 props반환하는 함수
// fetchPost(1), fetchPost(2)를 기준으로 각 함수의 응답 결과를 변수로 가져와 {post}로 반환
export const getStaticProps: GetStaticProps = async({params})=>{
  const {id} = params
  const post = await fetchPost(id)
  return{
    props: {post}
  }
}

// getStaticProps가 반환한 post를 렌더링하는 역할
export default function Post({post}: {post:Post}) {
  // post로 페이지 렌더링
}

 

결국 빌드 타임에 필요한 모든 경로에 대해 페이지를 미리 프리렌더링해둔다는 뜻인거같은데.. 새로운 data가 추가되면 어떻게 되는거지?

 

그때 사용하는게 바로 fallback !

fallback 옵션
- false : 빌드 타임에 모든 경로를 만들고 찾을 수 없는 경로면 404
- true : 미리 빌드하지 않은 페이지에 접근할 시 fallback 컴포넌트 보여주고, 빌드 완료 후 해당 페이지 보여줌
- "blocking" : 아무 처리하지 않고, 빌드 완료될 때까지 기다리게 하다가 렌더링 완료 후 해당 페이지 보여줌
                     동적 라우팅 페이지를 static 페이지로 제공해야 할 때 사용.

true를 설정했을 때 이점
1.
미리 빌드해야 할 페이지가 너무 많은 경우 미리 빌드할 것을 path에 지정하고 나머지 페이지가 어떻게 작동할 것인지 지정하는 게 좋다 !
2.
build 후에 새롭게 생성된 페이지는 동시에 Next.js는 이 경로를 미리 렌더링된 페이지 목록에 추가한다. 동일한 경로에 대한 후속 요청은 빌드 시점에 미리 렌더링된 다른 페이지와 마찬가지로 생성된 페이지를 제공한다.
(공식문서피셜: https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-true)

 

 

유용하게 사용될 수 있는 페이지

  • 마케팅 및 랜딩 홍보 페이지
  • 블로그 페이지 및 Docs 페이지와 같은 문서를 담은 페이지
  • 이커머스 마켓 상품 페이지들

 

getServerSideProps - 서버 사이드 렌더링

export default function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`);
  const data = await res.json();

  // Pass data to the page via props
  return { props: { data } };
}
  • 서버에서 실행되는 함수
  • 이 함수가 있다면 무조건 페이지 진입 전에 이 함수 실행되어 응답값에 따라 어떤 액션을 할지 결정함
  • 이 함수가 있다면 Next.js는 꼭 서버에서 실행해야 하는 페이지로 분류해 빌드 시에도 서버용 JS파일을 별도로 만든다.
    ➡️  window, document와 같이 브러우저에서만 접근할 수 있는 객체 접근 불가
    ➡️  API 호출 시 protocal과 domain 없이 fetch 요청 불가. (반드시 완전한 주소를 제공해야 함)
    ➡️  에러 발생시 미리 정의해 둔 에러 페이지로 리다이렉트 (ex. 500.tsx)
  • 사용자가 매 페이지를 호출할 때마다 실행되고, 이 실행이 끝나기 전까진 어떠한 HTML도 보여줄 수 X !
    ➡️  꼭 최초에 보여줘야 하는 데이터가 아니라면 getServerSideProps보단 클라이언트에서 호출하는게 유리
  • 어떤 조건에 따라 다른 페이지로 보내고 싶다면 redirect 사용가능
    ➡️ JS 로딩 이전에 원하는 페이지로 보내버릴 수 있어 클라이언트에서 리다이렉트하는 것보다 자연스럽다

 

유용하게 사용될 수 있는 페이지

  • 데이터 업데이트가 자주 발생하는 페이지

 

getInitialProps

  • getStaticProps & getServerSideProps 나오기 이전에 데이터 불러오기 수단이었는데, 지금은 비추
  • _app.tsx 같은 일부페이지에선 얘를 사용할 수 밖에 없음
  • 가장 큰 차이점
    ➡️ 페이지의 루트 함수에 정적 메서드로 추가한다
    ➡️ props 객체가 아닌 바로 객체를 반환

 

 

출처
https://velog.io/@go286/getStaticPath%EA%B0%80-%EB%AD%90%EC%95%BC

https://hong-devbox.tistory.com/13

https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props#when-should-i-use-getstaticprops

 

728x90