👥Club/☁️9oormthon univ☁️

⚛️ React - Next.js App Router에서의 렌더링 전략 이해

비엔 Vien 2025. 6. 4. 22:42

Component란?
: 화면에 보여줄 조각

function Hello() {
  return <h1>안녕!</h1>;
}

 



Server Component 

: 서버에서 실행됨

ㄴ 브라우저가 아닌 서버에서 렌더링됨

- 민감한 API 키나 DB 접근이 필요한 경우 등에서 서버 컴포넌트로 만듦 (보안성)

// 이건 서버에서 실행됨 (브라우저가 아닌)
// 서버 컴포넌트
export default async function PostList() {
  const res = await fetch('https://api.example.com/posts');
  const data = await res.json();

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
  • 서버가 API에 요청해서 글 목록을 받아오고
  • 받아온 글을 화면에 보여줘
  • 브라우저에서는 ‘화면만 보여줘’, 직접 API에 접근 안 해!

Client Component

: 브라우저에서 실행됨

- 사용자 인터랙션, 애니메이션 등에서 사용

'use client'; // 이건 꼭 써줘야 함!

import { useState } from 'react';

export default function LikeButton() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      좋아요 {count}개
    </button>
  );
}

- 사용자가 클릭할 수 있고

- 버튼을 누를 때마다 숫자가 올라감

- 이건 브라우저에서만 작동하니까 'use client'를 꼭 써야 해!

'use client';

이 선언이 없는 컴포넌트는 기본적으로 서버 컴포넌트로 취급

use client를 명시하면 브라우저에서 실행되는 클라이언트 컴포넌트가 됨

 

https://ko.react.dev/reference/rsc/server-components

 


 

fetch() 

:  브라우저나 서버에서 HTTP 요청을 보내서 데이터(API 등)를 가져오는 함수

 

 

어디서 쓰느냐: 위치에 따른 차이

Server Component 페이지나 서버 전용 파일 SSR, SSG, ISR 가능
Client Component 'use client' 있는 컴포넌트 CSR만 가능

 

// 서버에서 쓰는 fetch (가능: SSR/SSG/ISR)
export default async function Page() {
  const res = await fetch("https://api.example.com");
  const data = await res.json();
  return <div>{data.title}</div>;
}
// 클라이언트에서 쓰는 fetch (가능: CSR만)
"use client";

import { useEffect, useState } from "react";

export default function Page() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://api.example.com")
      .then(res => res.json())
      .then(setData);
  }, []);

  return <div>{data?.title}</div>;
}

 

 

 

어떻게 쓰느냐: fetch 옵션에 따른 전략

1. 

fetch("https://api주소.com", { cache: "no-store" });

➡️ SSR (항상 fresh)

 

2.

fetch("https://api주소.com", { cache: "force-cache" });

➡️ SSG (빌드 타이밍 캐시)

 

3. 

fetch("https://api.com", { next: { revalidate: 10 } });

➡️ ISR (10초마다 재생성)

 


 

CSR

: 클라이언트 사이드 렌더링

- useEffect 같은 React hook에서 fetch하여 데이터 받아와 렌더링

- 브라우저에서 JS로 HTML 생성 

- JS가 필요하며, 초기 로딩이 느림 (즉 첫 화면이 늦게 뜨지만 이후 페이지 전환은 빠름)

- 대시보드나 SPA 등 사용자 상호작용이 많은 경우에 적합

"use client";
import { useEffect, useState } from "react";

export default function CSR() {
  const [data, setData] = useState();

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts/4")
      .then(res => res.json())
      .then(setData);
  }, []);

  return <p>{data?.title}</p>;
}

- setData는 이 상태를 변경할 때 사용하는 함수

- API 요청으로 받아올 데이터를 이 변수에 저장


SSR

: 서버 사이드 렌더링

: 웹 페이지를 서버측에서 렌더링하는 방식

- 유저의 매 페이지 요청마다 서버에서 HTML 생성됨

- 매번 새로운 데이터 받아옴!

- 항상 최신 상태를 유지해야하는 웹 페이지나, 분석 차트 등에서 적합  (로그인이나 프로필)

 

CSR과의 차이로는 CSR은 useEffect 사용,

SSR에서는  getServerSideProps를 활용하여 데이터를 불러옴

 

getServerSideProps 
- Next.js에서 pre-rendering 중  컴포넌트 함수 호출 전에 getServerSideProps를 먼저 호출

- API 통신을 통해 데이터를 받아온 후 컴포넌트에 props로 데이터를 전달
- 매 요청마다 호출되며 서버에서 실행됨

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`);
  const data = await res.json(); //서버가 보내준 실제 데이터

  return {
    props: {
      listData: data,
    },
  };
}

const Main = ({ listData }) => {
  <ul>
    {listData.map((item) => (
      <li key={item.id}>{item.title}</li>
    ))}
  </ul>;
};

 

- 외부 API(https://.../data)에 요청해서 데이터를 받아오고 (fetch)

- 받아온 데이터를 listData라는 이름으로 페이지 컴포넌트에 props로 넘김

 


 

SSG

: Static Site Generation

:  빌드를 진행할 때 pages 폴더에서 작성한 각 페이지들에 대해 각각의 문서를 생성해서 static한 파일로 생성

- 정적이고 빠르다!

- 빌드시점에 미리 페이지를 렌더링➡️ 정적인 사이트를 구축할때 좋은 효율

-  정적으로 생성된 정보를 요청마다 동일한 정보로 반환하는 경우에 적합 (소개페이지나 블로글 글 등)

- SSR보다 SSG가 높은 성능?!

(SSG는 빌드 시에 HTML이 생성되고 매 요청마다 HTML을 재사용
하지만 그에 비해 SSR은 매 요청마다 HTML을 생성하기 때문에 응답 속도가 느리고 서버에 더 많은 부담이 감)

 

getStaticProps와 getStaticPath 사용 (SSG, ISR)
- getStaticProps: 페이지를 미리 생성할 때 필요한 데이터를 가져오는 함수
- getStaticPaths: 동적 라우팅에서 어떤 경로들(path)을 빌드할지 미리 지정

 

getStatixProps

- SSG를 사용하여 데이터를 받아오려면 getStaticProps를 사용

서버 측에서만 실행되는 함수로 클라이언트에서 실행되지 않음

ㄴ API와 같은 외부 데이터를 받아서 Static Generation 하기 위한 용도

ㄴ 빌드 시에 딱 한 번만 호출

ㄴ static file로 빌드

export async function getStaticProps() {
  const res = await fetch(`https://.../data`);
  const data = await res.json();

  return {
    props: {
      listData: data,
    },
  };
}

const Main = ({ listData }) => {
  <ul>
    {listData.map((item) => (
      <li key={item.id}>{item.title}</li>
    ))}
  </ul>;
};

Main 페이지가 호출되면 getStaticProps가 먼저 실행되며 axios 통신을 통해 게시물 리스트를 가져오고
props에 리턴 값을 담아서 Main 컴포넌트에 전달)

 

+ 동적 경로를 위한 페이지 경로 목록 생성시 getStaticPaths 사용

export const getStaticPaths = async () => {
  return {
    paths: [
      { params: { id: '1' } },
      { params: { id: '2' } },
      { params: { id: '3' } },
    ],
    fallback: true,
  };
};

export const getStaticProps = async ({ params }) => {
  const id = params.id;
  const res = await axios.get(`https://url/${id}`);

  return {
    props: {
      listData: res.data,
    },
  };
};

const Detail = ({ listData }) => {
  <ul>
    {listData.map((item) => (
      <li key={item.id}>{item.title}</li>
    ))}
  </ul>;
};

- 호출 순서는 getStaticPaths -> getStaticProps -> Detail 페이지
- getStaticPaths에서 리턴값으로 path에 1,2,3 페이지 번호를 지정

- 그 후 getStaticProps에서 params.id를 읽어서 해당 게시글에 대한 데이터를 가져와서 페이지를 생성
- getStaticPaths에서 정적으로 지정했기 때문에 1,2,3 페이지는 static file로 생성됨




 

ISR

: SSG에 포함되는 개념 (SSG와의 차이는 설정한 시간마다 페이지를 새로 렌더링)

- SSG는 빌드 시에 페이지를 생성하기 때문에 데이터가 변경되면 다시 빌드를 해야하지만, ISR은 일정 시간마다 특정 페이지만 다시 빌드하여 페이지를 업데이트

- 일정 주기로 갱신

- 블로그와 같이 컨텐츠가 동적이지만 자주 변경되지 않는 사이트에 적합

 

next: {revalidate: N}

: " 이 페이지를 N초마다 자동으로 새로 고치게 해줘" 라는 뜻

- 정적으로 만든 페이지를 일정 시간 후에 다시 생성하게 만드는 옵션

ㄴ 항상 최신 데이터를 보여줄 수 있음

- getStaticProps()나 fetch()를 사용한 페이지는 기본적으로 SSG로 처리되어 한 번 만들어지면 변하지 않으나, 이걸 일정시간마다 다시 새로 만들고 싶을 때 revalidate 사용

// 빌드 시 데이터를 미리 받아서 페이지에 props로 넘김
export const getStaticProps = async ({ params }) => {
  const id = params.id;
  const res = await axios.get(`https://url/${id}`);

  return {
    props: {
      list: res.data,
    },
    revalidate: 20, //페이지를 20초마다 백그라운드에서 새로 생성
  };
};

const Detail = ({ list }) => {
  <ul>
    {list.map((item) => (
      <li key={item.id}>{item.title}</li>
    ))}
  </ul>;
};

 

(해당 코드에서는 20초마다 최신 내용으로 바뀜) 

 

 

https://velog.io/@kyeun95/CSR-SSR-SSG-ISR-이란

 

[React] CSR, SSR, SSG, ISR 이란?

클라이언트 사이드 렌더링은 HTML 파일을 받아와서 Client(웹 브라우져)에서 렌더링이 일어나는 방식이다.브라우저의 요청을 서버로 보내면, 서버는 HTML 파일과 함께 JavaScript 파일을 보낸다. 클라

velog.io

 

코드 예시

https://enjoydev.life/blog/nextjs/1-ssr-ssg-isr

 

Next.js의 렌더링 방식 이해하기 - SSR, SSG, ISR

Next.js의 렌더링 방식의 차이점에 대해서 알아보며 필요에 따라 적절한 렌더링 기법을 적용하는 방법에 대해 배워보겠습니다.

enjoydev.life