- next.js가 만들어진 배경
- Pre-Rendering 이란?
- CSR SSR SSG ISR 을 각각 알아보자
Next.js 사용하는 이유 중 가장 큰 이유라고 생각이 드는 렌더링 방식. 더 이상 미루지 말고 자세히 알아보자.
Next.js는 왜 탄생했을까?
웹 개발 생태계는 끊임없이 변화해왔다. 오늘날 많은 개발자들이 사용하고 있는 Next.js 역시 이러한 변화의 산물이다. Next.js가 어떤 문제를 해결하기 위해 등장했는지, 그 배경을 웹 개발의 역사와 함께 살펴보자.
과거: SSR과 MPA의 시대
초기 웹사이트들은 SSR(Server Side Rendering)과 MPA(Multi Page Application) 방식으로 동작했다. 서버에서 완전한 HTML을 생성해 클라이언트에 전달하는 방식이었다.
하지만 이 방식에는 명확한 한계가 있었다
- 페이지 이동 시 깜빡거리는 현상이 발생
- 매번 전체 페이지를 새로 로드해야 하는 불편함
- 스마트폰 시대의 도래로 앱과 같은 부드러운 사용자 경험에 대한 요구가 증가
전환: AJAX와 jQuery의 등장
이런 문제들을 해결하기 위해 AJAX와 jQuery가 등장했다. 이를 통해 새로운 패러다임이 시작되었다
- 클라이언트에서 비동기적으로 JSON 형태의 데이터를 서버에 요청
- 전체 페이지를 새로고침하지 않고도 필요한 부분만 업데이트
- 더 나은 사용자 경험을 제공
혁신: SPA의 등장
여기서 한 걸음 더 나아가, "클라이언트에서 데이터를 받아오는 것뿐만 아니라 웹페이지도 렌더링해버리자"는 아이디어가 등장했다.
이때 탄생한 것이 바로 프론트엔드 3대장 react, angular, vue 이었다
이들 덕분에 CSR(Client Side Rendering)이 가능한 SPA(Single Page Application)**의 전성기가 시작되었다.
문제: SPA 방식의 한계
하지만 CSR + SPA 방식에도 여러 문제점들이 드러나기 시작했다
- 페이지 로딩 시간이 길어졌다: 텅 빈 HTML을 다운받고, JS를 다운받아 실행한 후에야 화면이 렌더링되었다
- 자바스크립트 활성화가 필수가 되었다: JS가 비활성화되면 아무것도 보이지 않았다
- SEO 최적화가 어려워졌다: 검색 엔진이 크롤링할 때 빈 페이지만 보게 되었다
- 보안에 취약했다: 클라이언트 측 렌더링의 보안 이슈가 발생했다
- CDN에 캐시가 되지 않았다: 동적 콘텐츠로 인한 캐싱이 어려워졌다
특히 React의 경우, 다음과 같은 과정을 거치면서 첫 페이지 로딩이 느려졌다:
- 텅 빈 HTML을 다운로드했다
- 빈 화면을 표시했다
- JavaScript를 다운로드 및 실행했다
- React가 HTML을 생성 및 렌더링했다
Next.js의 등장
이 모든 문제를 한 번에 해결하고자 Vercel의 Next.js가 등장했다.
Next.js가 제공하는 핵심 기능들
- 자동 프리렌더링: 복잡한 설정 없이 SSR과 SSG를 지원했다
- 하이브리드 렌더링: CSR과 SSR의 장점을 모두 활용했다
- 최적화된 호스팅: Vercel을 통한 쉽고 최적화된 배포를 제공했다
- 파일 시스템 기반 라우팅: 폴더 구조만으로 라우팅을 설정했다
- 성능 최적화: 자동 코드 분할, 이미지 최적화 등을 제공했다
Pre-rendering 사용
이러한 문제들을 해결하기 위해 Pre-rendering(프리렌더링) 개념이 등장했다
- 미리 렌더링해서 보여줄 HTML을 저장했다
- 서버가 실시간으로 HTML을 생성해서 전달했다
- HTML 다운로드 즉시 렌더링된 화면을 표시할 수 있게 되었다
React에서도 프리렌더링이 가능했지만, 설정과 관리가 복잡했다.
Next.js에서의 Pre-Rendering
Pre-Rendering이란?
Pre-Rendering은 서버에서 페이지의 HTML을 미리 생성하여 사용자에게 전달하는 렌더링 방식이다. 클라이언트에서 JavaScript로 모든 것을 렌더링하는 CSR과 달리, 완성된 HTML을 먼저 보여주고 이후 JavaScript가 로드되어 상호작용이 가능해진다.
일반적인 React 웹 애플리케이션은 CSR(Client-Side Rendering) 방식을 사용한다.
이 방식에서는 브라우저가 처음에 빈 HTML 파일을 받아와 빈 화면을 보여주다가, JavaScript가 다운로드되고 클라이언트에서 렌더링이 완료되면 그제서야 완성된 화면을 표시한다.
반면 Next.js는 Pre-Rendering 방식을 채택하여 모든 페이지를 사용자에게 전달하기 전에 미리 렌더링한다. 즉, 클라이언트에서 모든 작업을 처리하는 것이 아니라, 서버에서 각 페이지의 HTML을 사전에 생성하는 것이다.
생성된 HTML은 해당 페이지에 필요한 최소한의 자바스크립트 코드와 연결된다. 그후 브라우저에 의해 페이지가 로드되면, 자바스크립트 코드가 실행되어 페이지와 유저가 상호작용(인터랙티브 하게)할 수 있게 된다.
이러한 과정을 Hydration이라 한다.
→ Pre-rendering을 사용하면 성능과 SEO가 향상될 수 있다. 검색엔진 봇이 완성된 HTML 콘텐츠를 바로 읽을 수 있어 페이지 내용을 정확히 파악하기때문이다.
Next.js에서의 SSG vs SSR
next.js 의 꽃이자 핵심기능인 서버쪽에서 불러와서 사용하는 방법. 그방법에 관하여 2가지 종류가 있다.
- Static Site Generation(정적 생성)
- Server-side Rendering(서버사이드 렌더링)
→ 둘의 차이는 웹 페이지를 위한 HTML을 언제 생성하느냐에 있다.
SSG?
빌드 타임에 HTML을 생성하는 프리 렌더링 방법이다. 프리 렌더링된 HTML은 매 요청마다 재사용된다. next build
명령을 통해 웹 앱을 프로덕션 레벨로 빌드할 때 HTML이 생성되며, 이후 클라이언트들이 요청할 때마다 이미 만들어진 HTML을 재사용한다.
SSR?
매 요청마다 HTML을 생성하는 프리 렌더링 방법이다. 클라이언트가 요청하는 시점에 매번 서버에서 HTML을 새로 생성한다.
핵심 차이점:
- SSG: 빌드 시점에 HTML 생성 → 빠른 응답 속도
- SSR: 서버 요청 시점에 HTML 생성 → 최신 데이터 보장, but 서버 부하 조심
SSG는 next dev
시에 자동으로 업데이트 되기 때문에 개발레밸과 프로덕션 레벨의 차이가 있다.
ISR?
기본적으로는 SSG 처럼 동작하지만, 특정 시간이 지나면 data가 업데이트 되었는지 확인하고 만약 업데이트 되었다면 새로운 data를 가진 페이지를 생성해 이를 보내준다.
Next.js 에서의 렌더링 방식
SSR의 렌더링 과정
- JavaScript 파일이 서버로 전송된다
- 유저가 브라우저에서 요청할 때마다, 서버가 최신 데이터를 받아와 HTML을 렌더링한다
- 렌더링된 HTML 파일을 유저의 브라우저에 전달한다
- 최신 데이터를 받아오기 위해
fetch('https://...', { cache: 'no-store' })
옵션을 사용하여 캐시를 비활성화한다
// 캐시 비활성화로 매 요청마다 새로운 데이터
const data = await fetch('https://api.example.com/data', {
cache: 'no-store'
})
CSR의 렌더링 과정
- HTML 파일이 서버로 전송된다
- 유저의 요청이 있을 때 HTML 파일을 브라우저로 보내주고, 그 다음 클라이언트가 API 서버에 데이터를 요청한다
- 데이터가 모두 받아지면, 데이터를 화면에 업데이트한다
jsx/tsx
파일 상단에'use client';
를 작성하여 구현할 수 있다- 클라이언트 컴포넌트에서는 리액트 훅들을 사용할 수 있다 (예:
useState
등)
// 파일 상단에 'use client' 선언 - 맨날 선언되는 use client...
'use client';
import { useState, useEffect } from 'react';
export default function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(res => res.json())
.then(setData);
}, []);
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
SSG의 렌더링 과정
- 유저의 요청 시에 데이터를 받아왔던 SSR과는 다르게, 빌드 타임에 데이터를 받아온다
- HTML + JSON 파일이 빌드되고, 이 파일들이 서버로 전송된다
- 유저가 페이지를 요청할 때마다, 빌드 시 미리 만들어놓은 HTML+JSON이 포함된 완전한 페이지를 전달한다
- 기본적인
fetch
함수를 사용하면 구현이 가능하다 fetch
함수의 기본 옵션 값이cache: 'force-cache'
이기 때문에 별도 옵션을 주지 않으면 SSG 방식으로 동작한다
// 기본 fetch - cache: 'force-cache'가 기본값
const data = await fetch('https://api.example.com/data')
// 명시적으로 작성하는 경우
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache'
})
CSR에서 기본 fetch 사용 시 SSG가 되는가?
아니다. CSR('use client'
)에서는 기본 fetch
를 사용해도 SSG가 되지 않는다.
CSR에서 fetch
를 사용하면 그냥 클라이언트에서 API 호출이 실행될 뿐이다.
ISR의 렌더링 과정
Next.js에서 SSG 방식으로 사전 렌더링된 페이지는 빌드 타임에 생성된 이후 내용이 고정되므로, 이후에 발생하는 데이터 변화가 반영되지 않는다. 이 경우 ISR을 사용하면 정적 페이지에 유통기한을 설정하여 특정 주기마다 페이지가 다시 생성되도록 설정할 수 있다.
- 초기 빌드: SSG처럼 빌드 타임에 정적 HTML 페이지를 생성한다
- 캐시된 페이지 제공: 설정된 시간(revalidate) 동안은 기존에 생성된 정적 페이지를 제공한다
- 백그라운드 재생성: 설정된 시간이 지나면 백그라운드에서 새로운 데이터를 확인하고 페이지를 재생성한다
- 점진적 업데이트: 새 페이지가 생성되면 다음 요청부터 업데이트된 페이지를 제공한다
구현 방법:
interface Post {
id: string
title: string
content: string
}
export const revalidate = 3600 // invalidate every hour
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts: Post[] = await data.json()
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
revalidate
옵션을 줌으로써 구현이 가능하다.
⇒ Next.js 프로젝트에서 데이터가 주기적으로 변하는 페이지에 ISR을 적용하면 사용자 경험을 향상시킬 수 있으며, 적절한 유효 기간을 설정해 두면 서버 부하를 최소화하면서도 최신 데이터 제공을 가능하게 한다.
Next.js 렌더링 방식 선택
SSG 방식 우선 권장
Next.js에서는 가능하면 SSG 렌더링 방식 사용을 권장한다. 페이지가 한 번 빌드되고 CDN에 의해 서비스되는 것이 매 요청마다 서버가 페이지를 렌더링하는 것보다 훨씬 빠르기 때문이다.
SSG 사용 조건: "유저 요청 이전에 페이지 렌더링이 가능한 경우"
- 불러올 데이터가 없는 경우
- 불러올 데이터가 있지만, 빌드 타임에 불러와도 괜찮은 경우
SSG 적합 사례:
- 마케팅 페이지
- 블로그 포스트
- 이커머스 제품 목록
- 도움말이나 문서 페이지
SSR 사용 조건:
- 유저 요청 전에 데이터를 불러오면 안 되는 경우
- 유저 요청에 따라 불러올 데이터가 바뀌는 경우
- 페이지가 자주 업데이트되고 매 요청마다 컨텐츠가 바뀌는 경우
- 프리렌더링된 페이지가 항상 최신 상태를 유지해야 하는 경우
CSR 사용 조건:
- 자주 업데이트되는 데이터를 관리해야 하는 경우
- 사용자와의 실시간 상호작용이 중요한 경우
SEO를 위한 선택 순서: SSG → ISR → SSR → CSR
요약
SSG (Static Site Generation)
- 빌드 시점에 데이터를 받아와 정적 HTML 생성
- 사용 시기: 데이터가 자주 변하지 않는 경우 (블로그, 문서, 회사 소개 등)
ISR (Incremental Static Regeneration)
- SSG + 주기적 업데이트 (백그라운드에서 재생성)
- 사용 시기: 가끔 업데이트되는 콘텐츠 (상품 목록, 뉴스, 댓글 수 등)
SSR (Server-side Rendering)
- 매 요청마다 서버에서 HTML 생성
- 사용 시기: 실시간 데이터가 중요한 경우 (대시보드, 사용자별 개인화 페이지)
CSR (Client-side Rendering)
- 브라우저에서 JavaScript로 렌더링
- 사용 시기: 사용자 상호작용이 많은 경우 (폼, 채팅, 게임 등)
참고 자료: