프리 렌더링(Pre-rendering)을 톺아보자! 🖼️
프리 렌더링(Pre-rendering)은 페이지를 사전에 생성하여,
브라우저가 초기 로드 시 바로 콘텐츠를 표시할 수 있도록 하는 기술입니다.
이는 SEO 개선, 빠른 사용자 경험, 초기 로드 시간 단축에 유리합니다.
프리 렌더링이란? 🤔
프리 렌더링은 HTML을 미리 생성하여 클라이언트에 제공하는 방식을 의미합니다.
React는 기본적으로 클라이언트에서 렌더링(클라이언트 사이드 렌더링, CSR)하지만,
프리 렌더링을 통해 초기 HTML을 서버 또는 빌드 시점에 준비할 수 있습니다.
프리 렌더링의 장점
1️⃣ SEO 개선
검색 엔진 크롤러는 초기 HTML 내용을 읽습니다. 프리 렌더링을 사용하면, 콘텐츠가 미리 제공되어 SEO 성능이 향상됩니다.
2️⃣ 초기 로드 시간 단축
브라우저가 HTML과 CSS를 바로 렌더링할 수 있으므로, 사용자에게 빠른 첫 화면을 보여줄 수 있습니다.
3️⃣ UX 향상
사용자는 로딩 스피너 대신 즉각적인 콘텐츠를 확인할 수 있어 만족도가 높아집니다.
React에서 프리 렌더링 방법들 🛠️
React에서 프리 렌더링을 구현하는 주요 방법은
Static Site Generation(SSG), Server-Side Rendering(SSR), 그리고 Pre-rendering 도구를 사용하는 것입니다.
각 방법은 목적과 상황에 따라 적합한 사용 사례가 있습니다.
1️⃣ Static Site Generation(SSG)
SSG는 빌드 시점에 HTML을 생성하여 정적인 파일로 저장합니다.
이후 사용자 요청 시 이 파일을 제공하므로 FCP 성능이 매우 우수합니다.
사용 사례
- 블로그, 문서 사이트, 마케팅 페이지처럼 정적인 콘텐츠가 많을 때
- 자주 변경되지 않는 데이터를 사용할 때.
React에서 SSG 구현 방법 (파일 라우터 방식)
React는 Next.js를 통해 SSG를 지원합니다.
📌 generateStaticProps와 fetch를 활용하여 빌드 시 정적 HTML을 생성하면 됩니다.
// pages/index.js
export async function getStaticProps() {
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
return {
props: {
posts,
},
};
}
export default function Home({ posts }) {
return (
<div>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
작동 원리
- getStaticProps: 빌드 시 데이터를 가져옵니다.
- Next.js는 HTML과 JSON 데이터를 미리 생성해 정적 파일로 제공합니다.
React에서 SSG 구현 방법 (앺 라우터 방식)
Next.js의 앱 라우터에서 SSG를 구현하려면,
📌 generateStaticParams와 fetch를 활용하여 빌드 시 정적 HTML을 생성하면 됩니다.
import { notFound } from 'next/navigation';
export async function generateStaticParams() {
const res = await fetch('https://api.example.com/products'); // API 호출
const products = await res.json();
return products.map((product: { id: string }) => ({
id: product.id, // URL의 동적 경로에 들어갈 값
}));
}
export default async function ProductPage({ params }: { params: { id: string } }) {
const res = await fetch(`https://api.example.com/products/${params.id}`, { cache: 'force-cache' });
if (!res.ok) return notFound(); // 에러 처리
const product = await res.json();
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
핵심 포인트
- generateStaticParams 활용
- 동적 경로([id])에 대해 빌드 시 생성할 페이지 리스트를 미리 정의
- return 값이 [id]의 정적 경로를 결정함
- fetch에서 cache: 'force-cache' 사용
- Next.js는 기본적으로 SSG에서는 cache: 'force-cache'를 사용하여 빌드 시 데이터를 캐싱
- fetch가 실행되었을 때 빌드 시점의 데이터를 가져오도록 설정
- 빌드 시 정적 파일로 생성
- next build 실행 시 해당 경로에 대한 정적 HTML이 생성됨
- 배포 후에는 API 요청 없이도 미리 생성된 HTML을 제공
2️⃣ Server-Side Rendering(SSR)
SSR은 요청 시점에 서버에서 HTML을 생성하여 브라우저에 제공합니다.
사용자 요청마다 새로운 HTML을 생성하므로, 동적인 콘텐츠에 적합합니다.
사용 사례
- 사용자의 요청마다 최신 데이터를 보여줘야 하는 경우
- 사용자 권한에 따라 다른 콘텐츠를 렌더링해야 하는 경우
React에서 SSR 구현 방법 (파일 라우터 방식)
Next.js는 SSR을 지원하며, getServerSideProps를 통해 구현합니다.
// pages/profile.js
export async function getServerSideProps(context) {
const response = await fetch(`https://api.example.com/user/${context.params.id}`);
const user = await response.json();
return {
props: {
user,
},
};
}
export default function Profile({ user }) {
return (
<div>
<h1>{user.name}'s Profile</h1>
<p>{user.bio}</p>
</div>
);
}
작동 원리
- getServerSideProps: 클라이언트 요청마다 호출되며, HTML을 동적으로 생성합니다.
React에서 SSR 구현 방법 (앺 라우터 방식)
Next.js의 앱 라우터에서 서버 사이드 렌더링)을 구현하려면,
📌 서버 컴포넌트에서 fetch를 사용하고 cache: 'no-store' 옵션을 설정하면 됨
import { notFound } from 'next/navigation';
export default async function ProductPage({ params }: { params: { id: string } }) {
const res = await fetch(`https://api.example.com/products/${params.id}`, { cache: 'no-store' });
if (!res.ok) return notFound(); // 에러 처리
const product = await res.json();
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
핵심 포인트
- fetch에서 cache: 'no-store' 설정
- cache: 'no-store'를 설정하면 매 요청마다 새로운 데이터를 가져와 SSR을 수행
- 즉, 정적 HTML이 아닌 실시간 데이터가 포함된 페이지가 생성됨
- 서버 컴포넌트(Server Component) 사용
- 기본적으로 Next.js의 app 디렉토리에서는 서버 컴포넌트가 기본
- 클라이언트에서 데이터를 가져오는 것이 아니라 서버에서 데이터를 가져온 후 HTML을 렌더링
- SEO 및 최신 데이터 반영
- 검색 엔진이 완전히 렌더링된 HTML을 가져갈 수 있어 SEO에 유리
- API 데이터가 자주 변경될 경우 SSR이 적합
3️⃣ Pre-rendering 도구 사용
React 프로젝트에 크롤러와 빌드 도구를 사용하여 프리 렌더링을 구현할 수 있습니다.
이 방식은 기존 React SPA에도 적용 가능합니다.
대표적인 도구
- React Snap: 기본적으로 Puppeteer를 사용해 페이지를 크롤링하고 정적 HTML을 생성한다.
- Prerender.io: 동적 렌더링(Dynamic Rendering) 기법을 활용해, CSR앱에서 서버에서 정적 HTML을 제공
React Snap 사용 예제
yarn add --save react-snap
// package.json
"scripts": {
"build": "react-scripts build && react-snap"
},
"reactSnap": {
"inlineCss": true,
"puppeteerArgs": ["--no-sandbox"],
"cacheAjaxRequests": true
}
- postbuild: 빌드가 완료되면 자동으로 react-snap 실행
- inlineCss: CSS를 인라인으로 삽입하여 렌더링 속도 향상
- puppeteerArgs: Headless 크롤링을 위한 옵션 (--no-sandbox는 보안 설정)
- cacheAjaxRequests: AJAX 요청을 캐싱하여 정적 HTML 생성 시 데이터 포함
작동 원리
React Snap은 빌드된 React 앱을 실행하며, HTML 파일을 사전 생성합니다.
- yarn build → React 앱 빌드
- react-snap 실행 → 빌드된 페이지를 스냅샷(정적 HTML)으로 변환
- dist 또는 build 폴더에서 생성된 HTML을 확인
📌 주의점:
- React Snap은 window 객체를 직접 사용하는 코드가 있으면 오류가 발생할 수 있음 → typeof window !== "undefined"로 체크 필요
- 동적 페이지가 많다면 Next.js 같은 프레임워크로 이동하는 것이 더 효과적
Prerender.io 사용 예제
const express = require("express");
const prerender = require("prerender-node");
const app = express();
app.use(prerender.set("prerenderToken", "YOUR_PRERENDER_IO_TOKEN"));
app.use(express.static("build")); // React 앱 빌드된 파일 제공
app.listen(3000, () => {
console.log("Server is running on http://localhost:3000");
});
- prerender-node 미들웨어를 적용하여 SEO 크롤러 요청 시 프리렌더링된 HTML 제공
- 일반 사용자는 CSR을 통해 페이지를 로드하고, 검색 엔진에는 정적 HTML을 반환
- prerenderToken을 설정하여 Prerender.io의 클라우드 서비스와 연결
📌 React Snap vs. Prerender.io 비교
특징 | React Snap | Prerender.io |
방식 | 빌드 시 정적 HTML 생성 | 런타임에 동적 렌더링 제공 |
SEO | 정적 HTML 제공 | SEO 크롤러에게만 정적 HTML 제공 |
적용 대상 | 정적 사이트(SSG) | 동적 CSR 앱 |
캐싱 | 정적 파일로 유지 | 프리렌더 서버에서 캐싱 |
추가 서버 필요 | ❌ (정적 파일 배포) | ✅ Express, Nginx 등 필요 |
- React Snap → 정적 사이트(SSG)로 변환하여 배포할 수 있는 경우 사용
- Prerender.io → 동적 페이지가 많고 기존 CSR(클라이언트 사이드 렌더링) 방식을 유지하면서 SEO 개선이 필요할 때 사용
프리 렌더링 활용 팁 🛠️
1️⃣ 최적의 전략 선택하기
- 정적인 콘텐츠 위주라면 SSG
- 데이터가 자주 변경된다면 SSR
- SEO가 중요한 SPA라면 Pre-rendering 도구를 사용
2️⃣ SEO 친화적인 HTML 생성
- <head>에 meta 태그와 title을 명확히 작성
- <link rel="canonical"> 태그로 중복 페이지 방지.
3️⃣ 동적 콘텐츠와 정적 콘텐츠 혼합
- Next.js의 ISR(Incremental Static Regeneration) 기능으로 일부 페이지만 정적 생성 후 동적으로 갱신 가능.
export async function getStaticProps() {
const data = await fetch('https://api.example.com/data');
return {
props: { data },
revalidate: 60, // 60초마다 재생성
};
}
4️⃣ 사용자 경험 극대화
- 프리 렌더링과 Lazy Loading을 결합하여 성능 최적화
- React Suspense를 사용해 비동기 데이터를 우아하게 처리
프리 렌더링 vs 클라이언트 렌더링 비교
특징 | 프리 렌더링 | 클라이언트 렌더링 |
SEO | 우수 (HTML 미리 생성) | 약함 (JS 실행 후 콘텐츠 렌더링) |
초기 로드 속도 | 빠름 (HTML 즉시 제공) | 느림 (JS 로드 및 실행 필요) |
유지 보수 | 복잡 (빌드/서버 설정 필요) | 간단 |
사용 사례 | SEO, 초기 로드가 중요한 애플리케이션 | 대화형/동적인 애플리케이션 |
🌷전설의 개발자가 되어봅시당!🌷
'Front-end > React' 카테고리의 다른 글
🚀 React의 Lazy Loading: 로딩 성능 최적화의 비밀 🔄 (0) | 2025.02.18 |
---|---|
React의 Link 컴포넌트 vs. 전통적인 <a> 태그: 뭐가 다를까? (1) | 2025.02.13 |
[React] Transition의 단계와 동작 원리 톺아보기!🌟 (2) | 2025.01.31 |
[React] Suspense의 동작 방식 및 비동기 처리 톺아보기 🎭 (4) | 2025.01.30 |
[React] 데이터 로딩 전략의 모든 것 🛠️ (0) | 2025.01.28 |