유리의 개발새발
[Next] 14. React Server Component 본문
📌 이것은, App Router의 가장 핵심 개념 중 하나다.
React Server Component란?
리액트 18부터 등장한, 완전히 새로운 유형의 컴포넌트입니다.
기존 리액트 컴포넌트와는 다르게, 오직 서버에서만 실행되고,
브라우저에서는 실행조차 되지 않습니다.
📌 특징 요약
- ✅ 서버에서만 실행됨
- ❌ 브라우저로 JS 번들 전송 ❌ (코드 자체가 클라이언트에 가지 않음)
- ✅ 비동기(fetch, DB 쿼리 등) 가능
- ❌ useState, useEffect 등 클라이언트 훅 사용 불가
🤔 이게 뭔 말인가 싶죠? 네, 저도 그렇습니다.
"컴포넌트가 리액트인데 브라우저에서 안 돌면 대체 뭔데?"
"그럼 JSX는 뭐고 렌더링은 어디서 하는 건데?"
이런 생각이 드는 게 정상입니다.
📌 근데, 이게 왜 등장한 건데?
🤯 React 팀이 생각한 문제들
기존의 React 앱은 대부분 이렇게 돌아갔습니다
- 서버에서 HTML을 내려줌 (SSR or SSG)
- 클라이언트에서 JavaScript로 다시 앱을 실행함 (Hydration)
- 모든 컴포넌트 로직, 상태 관리, fetch 등 대부분이 클라이언트 측에서 처리됨
→ 그래서 생긴 문제들
- ❌ 불필요한 JS 번들 낭비
서버에서 이미 렌더링한 내용인데도,
클라이언트가 또 똑같은 컴포넌트 JS를 받음 - ❌ 초기 로딩 느림
모든 페이지 컴포넌트가 JS로 번들링되어 브라우저로 가니까
초기 페이지 진입 시 JS 로딩/파싱/실행 지연 발생 - ❌ 데이터 가져오기가 반복됨
서버에서 데이터 불러오고 렌더링했는데,
클라이언트가 다시 hydration할 때 또 fetch함
(CSR 환경이라면 모든 데이터를 클라이언트에서 새로 불러옴)
그래서 등장한게 서버 컴포넌트래요.
js 번들 줄이려고 하는거니까, 서버 컴포넌트, 클라이언트 컴포넌트를 분리하면 됩니다.
리액트 팀 일 너무 열심히 하시는거 아닌가요?


난 당연히 반대일 줄 알았는데, 생각해보니 서버 컴포넌트로 구성을 해야 렌더링(횟수)이 덜 되는구나...
그래서 뭐 어떻게 분리하냐?
서버 컴포넌트는 우리가 따로 뭘 할 필요가 없습니다. 왜냐면 디폴트가 서버 컴포넌트거든요.
그러면, 클라이언트 컴포넌트를 쓰려면? 파일 최상단에 "use client" 쓰면 끝-
항목 | 서버 | 컴포넌트 (기본) | 클라이언트 컴포넌트 ("use client")
| 렌더링 위치 | 서버 (Node.js 등) | 브라우저 |
| 브라우저로 전송됨? | ❌ 전송되지 않음 | ✅ JS 번들로 전송됨 |
| 상태/이벤트 | ❌ 없음 | ✅ 있음 (useState, onClick, 등) |
| 비동기 처리 | ✅ await fetch() 가능 | ❌ 직접 fetch 어려움 (별도 처리 필요) |
| 사용 목적 | 데이터 표시, SEO, SSR 최적화 | 사용자 인터랙션, 이벤트 처리, 상태 변경 |
쉽게 말해 사용자와 상호 작용이 있으면 client component 아니면 전부 server component
그래, 용도는 알겠어.
🤔 비동기(fetch 등)도 해야 하고, 상태(useState)도 써야 한다면?
서버에서 데이터를 먼저 fetch하고, 그걸 클라이언트 컴포넌트로 넘겨라.
- 비동기(fetch)는 서버 컴포넌트에서 처리하고
- 상태 관리와 이벤트(onClick 등)는 클라이언트 컴포넌트에서 처리

⚠️ 서버 컴포넌트 사용 시 주의사항
App Router 기반으로 개발할 때 대부분의 컴포넌트는 서버 컴포넌트입니다.
하지만 서버 컴포넌트라고 해서 아무 코드나 막 쓰면 에러 납니다. 특히 클라이언트 전용 기능과 엮이는 순간 조심해야 합니다.
1. ❌ 서버 컴포넌트에는 브라우저에서 실행될 코드가 포함되면 안 된다
window, document, localStorage 같은 브라우저 API는 절대 사용 불가입니다.
왜냐면 서버 컴포넌트는 Node.js 환경에서 실행되기 때문입니다.
// ❌ 에러 발생
export default function Page() {
console.log(window.innerWidth); // ReferenceError: window is not defined
return <div>페이지</div>;
}
2. 🔄 클라이언트 컴포넌트는 클라이언트에서만 실행되지 않는다 (❗중요 개념)
클라이언트 컴포넌트도 최초 렌더링 시에는 서버에서도 실행됩니다.
이유는 서버에서 HTML을 미리 만들어서 보내주기 때문 (SSR)
즉, "use client"를 붙여도 초기 렌더는 서버에서 합니다.
하지만, 클라이언트 훅(useEffect)이나 브라우저 API는 여전히 클라이언트에서만 동작합니다.
"use client";
export default function Counter() {
console.log("이 코드는 서버에서도 한 번 실행됨 (SSR)");
return <button>클릭</button>;
}
3. ❌ 클라이언트 컴포넌트에서는 서버 컴포넌트를 import 할 수 없다
단방향(import) 구조입니다:
- ✅ 서버 → 클라이언트: 가능
- ❌ 클라이언트 → 서버: 불가능
만약 쓰면, 넥스트가 자동으로 서버 컴포넌트를 클라이언트 컴포넌트로 바꿔버림.
그렇지만 그러면 성능이 안 좋아지겠죠? 그냥 칠드런으로 하면 됨 그게 더 효율적임
4. ❌ 서버 컴포넌트에서 클라이언트 컴포넌트에게 "직렬화 되지 않는 Props"를 전달하면 안 된다
React는 서버 컴포넌트 → 클라이언트 컴포넌트로 props를 JSON 직렬화해서 넘깁니다.
따라서 함수, 클래스 인스턴스, Symbol, Date 등은 props로 넘기면 에러 납니다.
// ❌ 에러 발생 예시
export default function Page() {
const callback = () => alert("clicked");
return <ClientButton onClick={callback} />;
}
- props는 오직 문자열, 숫자, 배열, 객체, boolean, null 등 직렬화 가능한 값만 넘겨야 함
- 콜백이나 상태 처리는 클라이언트 컴포넌트 내부에서 처리하도록 분리
'Next' 카테고리의 다른 글
| [Next] 16. 데이터 캐싱 (1) | 2025.08.03 |
|---|---|
| [Next] 15. App Router에서 데이터 패칭 (0) | 2025.08.02 |
| [Next] 13.App Router (6) | 2025.08.01 |
| [Next] 12. Page Router (1) | 2025.07.31 |
| [Next] 11. 배포 (2) | 2025.07.31 |