포스팅 목차
2. 기존 SSR에 대해서 - Next.js + Page Router 기준
1. RSC란 무엇인가?
React Server Components의 정의
RSC(React Server Components)는 React 18부터 공식 지원되는 서버 전용 컴포넌트입니다.
Next.js가 필수이고 Next.js + App Router에서만 실용적으로 사용이 가능합니다.
"Server Component" 단어를 통해 CSR보다는 SSR(Server Side Rendering)에 용이할 것을 예상할 수 있습니다.
기존에는 Next.js + Page Router로 SSR을 구현하였지만,
React 18이상부터는 Next.js + App Router를 리액트에서는 더 권장하고 있습니다.
기존 SSR 방식과 현재 바뀐 SSR 방식의 비교에 대해서는 아래서 차차 설명하도록 하겠습니다.
Next.js + App Router에서는 Client Component와 Server Component로 나뉩니다.
Next.js + App Router 페이지 구조 예시 - Server Component
// app/products/page.tsx
// 기본 = Server Component
export default async function ProductsPage() {
// 서버에서만 실행
// 브라우저 실행 안 됨
// JavaScript 브라우저로 안 감
const products = await db.product.findMany();
return (
<div>
<h1>상품 목록</h1>
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2>
<AddToCartButton productId={product.id} />
</div>
))}
</div>
);
}
Next.js + App Router 페이지 구조 예시 - Client Component
// app/products/AddToCartButton.tsx
'use client'; // 명시적 구분!
export default function AddToCartButton({ productId }) {
// 브라우저에서 실행
// JavaScript 브라우저로 감
const [isAdding, setIsAdding] = useState(false);
return <button onClick={...}>담기</button>;
}
위 예시를 보면 Server Component에는 정적인 콘텐츠들이,
그리고 Client Component에는 인터렉션이 필요한, 동적인 콘텐츠들로 구성되어 있는것을 볼 수 있습니다.
정적인 콘텐츠의 기준?
질문 1: 사용자가 클릭하거나 입력하나?
→ Yes: 동적 (Client Component)
질문 2: 실시간으로 변하나?
→ Yes: 동적 (Client Component)
질문 3: 브라우저 API를 쓰나? (localStorage, window 등)
→ Yes: 동적 (Client Component)
→ 질문1+질문2+질문3 이 모두 No일 때 정적 (Server Component)
정적 콘텐츠 예시) 블로그 포스트, 대시보드 통계, 상품 목록 표시 등
기존에 있는 데이터들이 변경 될 일이 거의 없음.
2. 기존 SSR에 대해서 - Next.js + Page Router 기준
SSR의 실행 흐름을 보면 아래와 같습니다. Next.js Page Router 기준 (bundle.js 300KB는 예시 값입니다)

위와 같이 SSR에서는 CSR과는 다르게 사용자는 채워진 HTML을 받아볼 수 있지만,
bundle.js를 다운받고 Hydration이 완료되기 전까지는 버튼 등 이벤트 리스너를 동작시킬 수 없습니다.
이말은 즉 bundle.js크기가 클 수록, 다운로드 시간이 길어지고, 이벤트 리스너도 느리게 동작한다는 것입니다.
bundle.js란?
아래와 같이 React 라이브러리, 모든 컴포넌트 코드, hook 로직, 데이터 fetching 로직, 라우팅 라이브러리 등
웹 개발 시 구현된/사용된 모든 파일을 하나로 결합한 파일.
// 1. React 라이브러리 자체
import React from 'react';
import ReactDOM from 'react-dom';
// 2. 모든 컴포넌트 코드
function ProductList() { ... }
function ProductCard() { ... }
function Header() { ... }
function Footer() { ... }
// ... 수십 개의 컴포넌트
// 3. 모든 Hook 로직
const [products, setProducts] = useState([]);
useEffect(() => { ... }, []);
// 4. 데이터 fetching 로직
async function fetchProducts() {
const res = await fetch('/api/products');
return res.json();
}
// 5. 라우팅 라이브러리 (react-router 등)
// 6. 상태 관리 라이브러리 (mobx 등)
// 7. UI 라이브러리들
// 8. 기타 모든 의존성
Hydration이란?
Hydration = 이미 있는 HTML에 React를 "연결"하는 과정
서버에서 렌더링된 정적인 HTML에 클라이언트 측 자바스크립트를 연결하여 상호작용이 가능한 동적인 웹페이지로 만드는 과정입니다. 즉, 서버에서 생성된 HTML을 기반으로 React가 DOM에 이벤트 리스너 등 자바스크립트 속성을 부착시켜, 페이지가 완전히 로딩되기도 전에 사용자가 상호작용할 수 있도록 만드는 기술
실행 흐름
1. 서버에서 HTML 생성 → 브라우저로 전송
2. 브라우저에서 JavaScript 실행
3. 서버 HTML (실제 DOM) ←→ 클라이언트 Virtual DOM - 비교 (Reconciliation)
4. React 연결 + 이벤트 붙이기
5. 완료, 이제 React가 이 DOM을 관리, 인터랙션 가능
> 클라이언트에서 실행한 React 컴포넌트의 "이벤트 핸들러와 상태 시스템”을 서버에서 만든 HTML에 연결하는 과정
기존 SSR - Next.js + Page Router 의 구조를 보면 아래와 같습니다.
// pages/products.tsx (Pages Router)
// 아래와 같이 서버, 클라이언트 컴포넌트 구분이 없었음
// - Server Component
// - Client Component
// 모든 컴포넌트가 "양쪽 다":
// 서버에서 실행
// 브라우저에서도 실행
export default function ProductsPage({ products }) {
return (
<div>
<h1>상품 목록</h1> {/* 서버, 브라우저 모두 실행 */}
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2> {/* 서버, 브라우저 모두 실행*/}
</div>
))}
</div>
);
}
유일한 구분으로는 getServerSideProps를 사용하는 것인데,,
// 이것만 서버 전용
export async function getServerSideProps() {
// 서버에서만 실행
// 브라우저에서 실행 안 됨
const products = await db.query();
return { props: { products } };
}
// 컴포넌트는 양쪽 다
export default function ProductsPage({ products }) {
// 서버, 브라우저 모두 다 실행
return <div>...</div>;
}
이렇게 서버에서 내려줄 코드와 브라우저에 보낼 코드가 구분이 되지 않는것이 문제입니다.
아래 예시코드에서 정적인 부분과 동적인 부분을 나누어보면,
// pages/products.tsx (기존 SSR)
export async function getServerSideProps() {
const products = await db.product.findMany();
return { props: { products } };
}
export default function ProductsPage({ products }) {
const [cart, setCart] = useState([]);
const addToCart = (id) => {
setCart([...cart, id]);
};
return (
<div>
{/* 정적 */}
<header>
<h1>우리 쇼핑몰</h1>
<p>최고의 상품을 만나보세요</p>
</header>
{/* 정적 (데이터만 표시) */}
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2>
<p>{product.description}</p>
<span>{product.price}원</span>
{/* 동적 */}
<button onClick={() => addToCart(product.id)}>
담기
</button>
</div>
))}
{/* 정적 */}
<footer>
<p>© 2025 My Shop</p>
<p>서울시 강남구...</p>
</footer>
</div>
);
}
위와 같이 코드를 구현했을때 React 입장에서는 아래와 같습니다.
function ProductsPage({ products }) {
// 이 함수 전체가 하나의 컴포넌트
// React는 이 안에서 어떤 부분이 정적이고
// 어떤 부분이 동적인지 구분할 수 없음
return (
<div>
<header>...</header> // 정적
{products.map(...)} // 정적
<button onClick={...}>...</button> // 동적
<footer>...</footer> // 정적
</div>
);
}
// React: "이 컴포넌트 안에 onClick이 있네?"
// React: "그럼 전체를 Hydrate 해야겠다!"
React 입장에서는 해당 컴포넌트 안에 onClick 즉 동적인 컨텐츠가 있으므로 Hydrate 대상으로 생각합니다,
위 코드와 같이 모든 코드를 Hydrate했을 때의 단점은,
정적 컨텐츠를 렌더링하는 코드까지 모두 클라이언트로 전송된다는 것입니다.
Hydrate는 동적인 코드들만 html에 이어주면 되는 것인데,
애초에 매칭해볼 필요도 없는 정적인 코드들까지 한번씩 다 매칭 해보는 불필요한 작업을 하게 됩니다.
그래서 기존 SSR(Server Side Rendering)의 장점과 단점을 요약하자면.

여기서 핵심은 javascript가 여전히 큼, 즉 bundle.js의 크기가 여전히 크다는 것입니다.
* bundle.js 크기가 크면 사용자가 인터렉션을 사용하지 못함. (버튼 클릭 이벤트 등)
3. RSC의 장점과 어떻게 사용할지?
일단 RSC(React Server Component)를 사용했을때
브라우저와 컴포넌트간 어떻게 동작하는지 실행흐름부터 알아보겠습니다.
RSC의 동작 흐름
1. 브라우저: GET /products 요청
↓
2. 서버: Server Component 실행 (DB 조회)
↓
3. 서버: RSC Payload로 직렬화 (JSON-like)
↓
4. 브라우저: Client Component JS만 다운로드
↓
✓ 결과: 훨씬 작은 번들 크기!
*서버 컴포넌트의 "코드"는 브라우저로 안 가지만, "실행 결과"는 브라우저로 갑니다.
서버에서 일어나는 일
서버컴포넌트에서는 컴포넌트가 실행되고,
해당 컴포넌트가 payload로 직렬화 됩니다.
1) 컴포넌트 실행
// 서버에서 실행
async function ProductsPage() {
const products = await db.product.findMany();
// products = [
// {id: 1, name: "맥북 프로", price: 2000000},
// {id: 2, name: "아이패드", price: 1000000}
// ]
return (
<div>
<h1>상품 목록</h1>
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2>
<p>{product.price}원</p>
</div>
))}
</div>
);
}
2) RSC Payload로 직렬화
// 서버가 React Element를 JSON-like 형태로 변환
const rscPayload = {
"root": {
"type": "div",
"props": {},
"children": [
{
"type": "h1",
"children": "상품 목록"
},
{
"type": "div",
"props": { "key": "1" },
"children": [
{ "type": "h2", "children": "맥북 프로" },
{ "type": "p", "children": "2000000원" }
]
},
{
"type": "div",
"props": { "key": "2" },
"children": [
{ "type": "h2", "children": "아이패드" },
{ "type": "p", "children": "1000000원" }
]
}
]
}
};
// 이걸 브라우저로 전송!
이때, 컴포넌트자체는 브라우저에 전송되지 않고,
payload로 직렬화된 데이터만 브라우저로 전송됩니다.
브라우저로 전송되는 것
❌ 전송되지 않는 것
// 이 코드는 브라우저로 안 감!
async function ProductsPage() {
const products = await db.product.findMany(); // ❌
return (
<div>
<h1>상품 목록</h1>
{products.map(product => ( // ❌
<div key={product.id}>
<h2>{product.name}</h2>
<p>{product.price}원</p>
</div>
))}
</div>
);
}
// ProductsPage 함수 자체 ❌
// db.product.findMany() ❌
// products.map() ❌
// JSX 코드 ❌
✅ 전송되는 것
// RSC Payload (JSON 형태의 데이터)
{
"type": "div",
"children": [
{
"type": "h1",
"children": "상품 목록"
},
{
"type": "div",
"children": [
{ "type": "h2", "children": "맥북 프로" },
{ "type": "p", "children": "2000000원" }
]
},
{
"type": "div",
"children": [
{ "type": "h2", "children": "아이패드" },
{ "type": "p", "children": "1000000원" }
]
}
]
}
// 이건 단순한 데이터! (코드 아님!)
브라우저에서 일어나는 일
1) RSC Payload 받기
// 브라우저가 받음
const payload = {
"type": "div",
"children": [
{ "type": "h1", "children": "상품 목록" },
{ "type": "div", "children": [...] },
{ "type": "div", "children": [...] }
]
};
console.log('받은 데이터:', payload);
2) React가 이 데이터로 DOM 생성
// React (브라우저에 있는 React 라이브러리)가 처리
function renderRSCPayload(payload) {
// payload를 읽어서 실제 DOM 생성
const div = document.createElement('div');
const h1 = document.createElement('h1');
h1.textContent = '상품 목록';
div.appendChild(h1);
const productDiv1 = document.createElement('div');
const h2_1 = document.createElement('h2');
h2_1.textContent = '맥북 프로';
const p_1 = document.createElement('p');
p_1.textContent = '2000000원';
productDiv1.appendChild(h2_1);
productDiv1.appendChild(p_1);
div.appendChild(productDiv1);
// ... (반복)
return div;
}
// 실제 DOM 생성
const dom = renderRSCPayload(payload);
// 화면에 추가
document.getElementById('root').appendChild(dom);
3) 화면에 표시
<!-- 브라우저 화면 -->
<div id="root">
<div>
<h1>상품 목록</h1>
<div>
<h2>맥북 프로</h2>
<p>2000000원</p>
</div>
<div>
<h2>아이패드</h2>
<p>1000000원</p>
</div>
</div>
</div>
핵심: 실행은 서버에서, 렌더링은 브라우저에서 진행.
이렇게 Server Component 와 Client Component로 명확하게 구분 되기 때문에,
Server Component는 bundle.js로 만들 필요가 없어져서, bundle.js의 크기가 확 줄어들게 됩니다.
서버 컴포넌트와 클라이언트 컴포넌트의 흐름 차이
Server Component
실행 위치: 서버만
JavaScript 전송: ❌
DB 직접 접근: ✅
브라우저에서 보는 것: 렌더링 결과 (HTML-like)
서버: 함수 실행 → HTML-like 데이터 생성
전송: 데이터만
브라우저: 데이터 표시
크기: 작음 (데이터만)
Client Component
실행 위치: 브라우저 (서버에서도 초기 렌더링)
JavaScript 전송: ✅
이벤트 처리: ✅
브라우저에서 보는 것: JavaScript 코드 + 실행 결과
서버: 플레이스홀더 생성
전송: 데이터 + JavaScript 코드
브라우저: 코드 실행 + 이벤트 연결
크기: 큼 (코드 포함)
브라우저에서 필요한 것:
- 이벤트 핸들러 (onClick)
- 상태 관리 (useState)
- 비동기 로직 (async/await)
- UI 업데이트 (버튼 텍스트 변경)
서버에서는 할 수 없는 것:
// 서버에서 실행할 수 없음:
button.addEventListener('click', ...) // 브라우저 API
window.fetch(...) // 사용자 브라우저에서 실행되어야 함
localStorage.getItem(...) // 브라우저 저장소


위와 같은 동작 흐름을 통해 알 수 있는 점은,
기존 SSR에서의 문제점이었던 "여전히 큰 bundle.js크기" 를 확실하게 줄일 수 있다는 점입니다.
4. (보충) CSR vs SSR vs RSC 비교
CSR, SSR, RSC 사용 시 bundle.js에 포함되는 내용은 무엇이 있을까요?
CSR의 bundle.js
// 1. React 라이브러리 자체
import React from 'react';
import ReactDOM from 'react-dom';
// 2. 모든 컴포넌트 코드
function ProductList() { ... }
function ProductCard() { ... }
function Header() { ... }
function Footer() { ... }
// 3. 모든 Hook 로직
const [products, setProducts] = useState([]);
useEffect(() => { ... }, []);
// 4. 데이터 fetching 로직
async function fetchProducts() {
const res = await fetch('/api/products');
return res.json();
}
// 5. 라우팅 라이브러리
// 6. 상태 관리 라이브러리
// 7. UI 라이브러리들
// 8. 기타 모든 의존성
SSR의 bundle.js
// 1. React 라이브러리
import React from 'react';
import ReactDOM from 'react-dom';
// 2. 모든 컴포넌트 (여전히 포함!)
function ProductList() { ... }
function ProductCard() { ... }
// 3. Hydration 로직
ReactDOM.hydrate(<App />, document.getElementById('root'));
// 4. 라우팅 등
RSC의 bundle.js
// 1. React 라이브러리
import React from 'react';
// 2. Client Component만
function AddToCartButton() { ... }
function SearchBar() { ... }
// Server Component는 포함 안 됨!
비교 표
CSR SSR RSC
| 초기 HTML | 빈 HTML | 완성된 HTML | 완성된 HTML |
| JS 번들 크기 | 큰 편 | 큰 편 | 작음 |
| 초기 로딩 | 느림 | 빠름 | 빠름 |
| SEO | 나쁨 | 좋음 | 좋음 |
| Hydration | 전체 | 전체 | Client만 |
| 서버 부하 | 낮음 | 높음 | 높음 |
**RSC 즉 Next.js + App Router 사용 시에도 당연히 단점이 존재합니다.
서버 부하가 올 수 있다는 점입니다,
서버부하가 올 경우 문제점은
1. 응답 시간 지연
서버가 HTML을 생성하는 데 시간이 오래 걸려서 사용자가 흰 화면을 더 오래 보게 되는 경우
2. 서버 비용 증가
1) CPU 사용량 증가 (컴포넌트 렌더링)
2) 메모리 사용량 증가 (동시 요청 처리)
3) 트래픽이 많으면 서버 스케일업이나 인스턴스 추가 필요 → 비용 상승
3. 서버 다운 또는 타임 아웃
4. 데이터베이스 부하
erver Component에서 직접 DB 조회를 하는 경우, DB 연결 수와 쿼리 부하도 증가함
5. 실무 활용 가이드

6. 공부하면서 생겼던 의문점
1) 질문: 데이터가 실시간 업데이트일 경우?
페이지를 다시 로드하면 업데이트가 됨.
흐름:
1. 사용자가 새 상품 추가 폼 작성
2. Submit 클릭
3. API로 데이터 전송
4. DB에 저장
5. 페이지 새로고침 (또는 router.refresh())
6. Server Component 다시 실행
↓
const products = await db.product.findMany();
// 새 상품 포함된 목록 받음!
7. 새 목록으로 화면 업데이트
Server Component 업데이트 방법:
- 페이지 새로고침 (window.location.reload())
- Next.js router.refresh() (부드러운 업데이트)
- URL 변경 (router.push)
- Server Action (Next.js 14+)
실시간이 필요할 경우:
- Client Component 사용
- WebSocket, Polling 등
2) 질문: RSC와 React Query 를 어떻게 함께 사용할까? 함께 사용해도 페이지 새로고침이 필요한가?
// app/products/page.tsx (Server Component)
export default async function ProductsPage() {
const products = await db.product.findMany();
return (
<div>
<h1>상품 목록</h1>
<ProductList initialProducts={products} />
</div>
);
}
// app/products/ProductList.tsx (Client Component)
'use client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
export default function ProductList({ initialProducts }) {
const queryClient = useQueryClient();
// React Query 사용
const { data: products } = useQuery({
queryKey: ['products'],
queryFn: async () => {
const res = await fetch('/api/products');
return res.json();
},
initialData: initialProducts // Server Component에서 받은 초기 데이터
});
// 상품 추가 mutation
const addProductMutation = useMutation({
mutationFn: async (newProduct) => {
const res = await fetch('/api/products', {
method: 'POST',
body: JSON.stringify(newProduct)
});
return res.json();
},
onSuccess: () => {
// 캐시 무효화 - 자동으로 다시 fetch!
queryClient.invalidateQueries({ queryKey: ['products'] });
// 페이지 새로고침 필요하지 않음
}
});
return (
<div>
{products.map(product => (
<div key={product.id}>{product.name}</div>
))}
<button onClick={() => addProductMutation.mutate({ name: '신상품' })}>
상품 추가
</button>
</div>
);
}
흐름:
1. 페이지 로드
↓
Server Component 실행
↓
초기 데이터 fetch (await db.product.findMany())
↓
Client Component에 전달 (initialProducts)
↓
React Query가 초기 데이터로 캐시 설정
2. 사용자가 "상품 추가" 클릭
↓
addProductMutation.mutate() 실행
↓
POST /api/products
↓
서버에서 DB에 저장
↓
onSuccess 실행
↓
queryClient.invalidateQueries()
↓
React Query가 자동으로 다시 fetch
↓
GET /api/products
↓
새 데이터 받음
↓
화면 자동 업데이트
페이지 새로고침 필요 없음!
정리:
방법 필요 여부 이유
| 페이지 새로고침 | ❌ | React Query가 알아서 함 |
| queryClient.invalidateQueries() | ✅ | Client 캐시 업데이트 |
| router.refresh() | △ | Server Component 데이터도 업데이트하려면 |
3) 질문: Composition Pattern 이란?
요약: 부모 컴포넌트가 자식 컴포넌트를 "조합"해서 사용하는 패턴
"import 해서 직접 사용" 대신 "children이나 props로 받아서 사용"
일반적인 패턴 방식
// Client Component에서는 Server Component import 불가능
'use client';
import ServerComponent from './ServerComponent';
export default function ClientComponent() {
return (
<div>
<ServerComponent /> {/* import해서 직접 사용 */}
</div>
);
}
Composition Pattern
// children으로 받아서 사용
'use client';
export default function ClientComponent({ children }) {
return (
<div>
{children} {/* children으로 받아서 사용 */}
</div>
);
}
// 사용하는 곳 (부모 - Server Component)
<ClientComponent>
<ServerComponent /> {/* 여기서 전달 */}
</ClientComponent>
구체적인 예시
// app/products/page.tsx (Server Component)
import FilterableLayout from './FilterableLayout'; // Client
import ProductCard from './ProductCard'; // Server
export default async function ProductsPage() {
const products = await db.product.findMany();
return (
<FilterableLayout>
{/* Server Component를 children으로 전달 */}
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</FilterableLayout>
);
}
// app/products/FilterableLayout.tsx (Client Component)
'use client';
import { useState } from 'react';
export default function FilterableLayout({ children }) {
const [view, setView] = useState('grid');
return (
<div>
{/* 인터랙티브한 부분 (Client) */}
<div className="controls">
<button onClick={() => setView('grid')}>
그리드 보기
</button>
<button onClick={() => setView('list')}>
리스트 보기
</button>
</div>
{/* Server Component 결과 표시 */}
<div className={`products-${view}`}>
{children}
</div>
</div>
);
}
// app/products/ProductCard.tsx (Server Component)
export default function ProductCard({ product }) {
// DB에서 추가 데이터 가져오기 가능
return (
<div className="card">
<h2>{product.name}</h2>
<p>{product.price}원</p>
</div>
);
}
4) 질문: 왜 Composition Pattern이 필요한지?
문제 상황:
'use client';
import ProductCard from './ProductCard'; // Server Component
export default function FilterableLayout() {
const [view, setView] = useState('grid');
// ❌ Server Component를 직접 렌더링 불가
const products = [1, 2, 3];
return (
<div>
<button onClick={() => setView('grid')}>그리드</button>
{products.map(id => (
<ProductCard productId={id} /> // 서버 컴포넌트 직접 import 안됨
))}
</div>
);
}
이유:
FilterableLayout은 Client Component
→ JavaScript가 브라우저로 감
→ ProductCard를 import하면?
→ ProductCard도 브라우저로 가야 함
→ 하지만 ProductCard는 Server Component
→ DB 접근 코드가 브라우저로?
→ 불가능!
해결책: Composition 패턴 사용
// FilterableLayout은 children만 받음
'use client';
export default function FilterableLayout({ children }) {
const [view, setView] = useState('grid');
return (
<div>
<button onClick={() => setView('grid')}>그리드</button>
<div className={view}>
{children} {/* 이미 렌더링된 결과 */}
</div>
</div>
);
}
// 사용하는 곳에서 조합
<FilterableLayout>
<ProductCard productId={1} />
<ProductCard productId={2} />
</FilterableLayout>
동작 원리:
1. ProductCard는 서버에서 실행
→ DB 접근, 렌더링 완료
2. 결과를 데이터로 변환
→ { type: 'div', children: [...] }
3. FilterableLayout으로 전달
→ children은 이미 렌더링된 "데이터"
4. 브라우저에서:
→ FilterableLayout만 실행
→ children을 그냥 표시
5) 질문: CSR에서 JavaScript가 필수인 이유는?
CSR은 처음에는 빈 HTML을 받아오고,
브라우저가 bundle.js를 다운받아야 하는데 자바스크립트가 차단되어 다운받더라도 실행을 못합니다.
그렇기 때문에 javascript가 있어야 페이지를 받아올 수 있습니다.
6) 질문: 기존 SSR과 RSC의 차이점은?
기존 SSR RSC
| 컴포넌트 구분 | 없음 | 있음 (Server/Client) |
| 컴포넌트 실행 | 모두 양쪽 | 구분해서 실행 |
| JavaScript | 모두 브라우저로 | Client만 브라우저로 |
| 명시적 표시 | getServerSideProps만 | 'use client' |
7) 질문: bundle.js란?
React 애플리케이션의 모든 JavaScript 코드를 하나로 묶은 파일.
8) 질문 : Hydration이란?
서버에서 렌더링된 정적인 HTML에 클라이언트 측 자바스크립트를 연결하여 상호작용이 가능한 동적인 웹페이지로 만드는 과정입니다.
간단히 말하면: "죽어있는" HTML이 "살아있는" React 앱이 되는 과정!
9) 왜 Composition Pattern을 사용해야 하나요?
Client Component에서는 Server Component를 직접 import할 수 없기 때문입니다.
Server Component를 children으로 전달하면, 이미 서버에서 렌더링된 결과를 받아 사용할 수 있습니다.
7. 참고 자료
공식 문서
React 공식 문서
Next.js 공식 문서
마치며
처음에는 Server Component와 Client Component를 구분하는 것이 어색하게 느껴질 수 있고,
어떤 로직을 Server Component에 둬야 될 지, 아니면 Client Component로 둬야 될 지 모르겠을 수도 있습니다.
팁(?)은 기본적으로 Server Component로 구현하고,
인터렉션이 있는부분만 잘게 쪼개서 Client Component 구현하는게 좋습니다.
Client Component 는 최대한 적게 있는 것이 좋습니다.
RSC를 잘 사용하면 아래와 같은 이점을 얻을 수 있습니다.
- 번들 크기를 크게 줄일 수 있음
- 초기 로딩 속도가 빨라짐
- 서버 리소스를 효율적으로 활용 가능
- 보안 강화 (민감한 정보를 서버에만 둘 수 있음)
회사에서 동료들과 스터디를 하는데,
발표한김에 오랜만에 기록할겸 포스팅 해봅니다 ㅎㅎ

'웹 > (React)리액트' 카테고리의 다른 글
| (react-query)useInfiniteQuery로 무한스크롤 구현하기 (상세 설명, React with Typescript) (0) | 2024.10.29 |
|---|---|
| 상태관리 라이브러리, Redux 에 대해서/Redux Toolkit에 대해서 (2) | 2023.09.15 |
| (React)리액트 클래스에 조건문/리액트에서 클래스 다양한 방법으로 사용해보기/클래스명 여러개/className 여러개 일때/클래스에 변수 사용하기 (20) | 2023.07.11 |
| (React) 리액트 드롭다운 메뉴 만들기/마우스 오버시 드롭다운 - 1탄 (2) | 2022.12.17 |
| (React with Typescript) module not found 'sass' 에러 해결, scss 사용법 (0) | 2022.10.27 |
댓글