CSR, SSR, SSG… 이젠 진짜 확실히 구분해보자!
“이 페이지는 CSR로 구현돼서 SEO가 약해요.”
“SSR로 바꾸면 첫 페이지 로딩이 더 빨라질 수도 있어요.”
“SSG로 미리 렌더링해서 정적 페이지로 배포해요.”
CSR, SSR, SSG가 어떻게 다르고, 언제 어떤 걸 써야 하는지 헷갈리는 경우 많은데
오늘 이 글로 한 방에 정리해보자
1. CSR (Client Side Rendering)
브라우저(클라이언트)에서 JS로 화면을 만드는 방식
어떻게 작동하나요?:
- 사용자가 페이지에 접속하면
- 브라우저는 HTML 틀(div#root)만 받고
- JS 번들을 다운받은 후 그때서야 화면이 보이기 시작함
대표 예시:
- React 앱을 Vite/CRA로 만들었을 때
- SPA(Single Page Application) 대부분
장점
- 빠른 페이지 전환 (앱처럼 동작)
- 백엔드 없이도 작동 가능
단점
- JS가 로딩되기 전까진 빈 화면
- 검색엔진이 콘텐츠 못 읽는 경우 있음 (SEO 약함)
2. SSR (Server Side Rendering)
서버에서 HTML을 만들어서 브라우저에 보내주는 방식
어떻게 작동하나요?:
- 사용자가 페이지에 접속하면 서버가 즉시 필요한 데이터를 가져옴
- 서버가 HTML을 만들어서 브라우저에 보내줌
- 사용자는 즉시 화면을 볼 수 있음
대표 예시:
- Next.js의 getServerSideProps
- 쇼핑몰, 뉴스 사이트 (초기 로딩과 SEO 중요할 때)
장점
- SEO에 좋음 (검색엔진이 HTML 읽을 수 있음)
- 첫 화면 로딩 빠름
단점
- 서버 부하가 큼
- 매 요청마다 서버가 렌더링해야 함 → 느려질 수 있음
3. SSG (Static Site Generation)
빌드 타임에 미리 HTML을 만들어두고, 서버 없이 서빙
어떻게 작동하나요?:
- 배포 전에 빌드하면 HTML 파일이 미리 만들어짐
- 요청 시 서버는 그냥 그 정적 파일을 보내줌
대표 예시:
- 블로그, 문서 사이트, 마케팅 랜딩페이지
- Next.js의 getStaticProps
장점
- 속도 빠름 (CDN으로 정적 파일 그대로 전달)
- 트래픽 많아도 서버 부담 거의 없음
단점
- 동적 데이터 처리 어려움 (빌드 후 데이터가 바뀌면 반영이 안 됨)
- 페이지 수가 많으면 빌드 오래 걸림
번외편: ISR (Incremental Static Regeneration)
SSG인데, 필요할 때만 서버에서 다시 만들어주는 방식
Next.js가 만든 하이브리드 전략
- HTML을 미리 만들어두고 일정 시간이 지나면 서버가 백그라운드에서 재생성
완전한 정적 + 최신 데이터 반영을 동시에 잡고 싶을 때 사용
언제 어떤 걸 써야 할까?
SEO 중요, 첫 로딩 빠르게 | SSR |
콘텐츠가 자주 안 바뀜 | SSG or ISR |
로그인/인증 기반 앱 | CSR or SSR |
모든 게 실시간 | CSR or SSR |
블로그, 문서, 마케팅 | SSG |
Next.js 기준 렌더링 방식 요약
CSR | useEffect, 클라이언트 컴포넌트 | 완전 클라이언트에서 처리 |
SSR | getServerSideProps | 요청마다 HTML 생성 |
SSG | getStaticProps | 빌드 시 HTML 생성 |
ISR | getStaticProps + revalidate | 정적 + 일정 시간마다 재생성 |
마무리
렌더링 방식은 성능 + SEO + 사용자 경험 + 서버 비용까지 다 엮인 문제
정답은 없고 상황에 따라 최적의 전략을 조합해서 쓰는 게 중요
+ 번외
nextjs 12 버전 (페이지 라우터) 에서 SSG가(정적 사이트 생성) 기본적으로 설정돼있음.
근데 왜 getServerSideProps 를 쓰냐 ?
동적 seo가 적용 안되기 때문임. (pages/[id].tsx와 같은 동적 페이지)
getServerSideProps 쓸 땐 페이지 요청시마다 서버에서 페이지를 리렌더링
(getServerSideProps 안써도 리렌더링 되지만 그건 서버에서 주는게 아니라 그냥 리액트의 상태 업데이트 때문에 리렌더링 되는 것임)
그래서.. 동적 페이지까지 seo 하고싶다면 getServerSideProps 사용하면 될 것 같다 흠
(근데그럼 getStaticProps 쓰는 이유는 뭐지 ha..)
next 13 ~ 버전 (App Router) 에서는 기본적으로 SSR이 적용
하지만 서버 컴포넌트와 클라이언트 컴포넌트를 명확히 구분할 수 있기 때문에,
서버에서 렌더링하는 방식(SSR)과 클라이언트에서 렌더링하는 방식(CSR)을 상황에 맞게 선택가능
1. 기본 SSR 동작
앱 라우터에서 서버 컴포넌트를 사용하면, 페이지나 컴포넌트가 서버에서 렌더링됨
이 방식은 SSR과 동일한 방식으로 동작. 서버에서 데이터를 가져와서 페이지를 렌더링한 후, 클라이언트에 전달됨
// app/clientside.tsx
'use client';
import { useState } from 'react';
export default function ClientSide() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Click me</button>
<p>Count: {count}</p>
</div>
);
}
이 방식은 매 요청 시마다 서버에서 데이터를 가져오고 렌더링하기 때문에 SSR로 간주됨
2. 클라이언트 컴포넌트 사용 시
Next.js 13에서는 클라이언트 컴포넌트를 별도로 지정해야함
use client라는 지시어를 사용하면, 해당 컴포넌트는 클라이언트에서만 실행됨.
이렇게 지정된 컴포넌트는 CSR 방식으로 동작함
// app/clientside.tsx
'use client';
import { useState } from 'react';
export default function ClientSide() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Click me</button>
<p>Count: {count}</p>
</div>
);
}
use client' 지시어를 사용하면 이 컴포넌트는 클라이언트에서만 렌더링됨. 이 경우, CSR 방식으로 페이지가 렌더링
결론
- 앱 라우터에서는 기본적으로 SSR이 적용됨. 즉, 서버 컴포넌트를 사용하면 서버에서 렌더링되고, 이 방식이 기본
- 하지만 클라이언트 컴포넌트를 사용하면 CSR 방식으로 동작하게 되며, 이때는 서버 렌더링이 이루어지지 않음
- SSR을 적용한 페이지는 매 요청 시마다 서버에서 새롭게 렌더링됨