모던 리액트 + Next.js #1 왜 Next.js와 Server Components인가
리액트 기초 강좌(#1~#15)와 Todo 앱 빌드 시리즈를 마치셨다면, 클라이언트 사이드 리액트의 거의 모든 기본기는 손에 잡혔을 겁니다. 이번 시리즈에서는 한 단계 위로 올라가서 모던 리액트의 새 패러다임 — Server Components와 그것을 가장 잘 풀어낸 프레임워크인 Next.js를 다룹니다.
총 6편으로 구성됩니다.
- #1 왜 Next.js와 Server Components인가 ← 이번 글
- #2 Next.js 시작과 App Router
- #3 Server Components vs Client Components
- #4 데이터 페칭과 캐싱
- #5 Suspense와
use()로 로딩 처리 - #6 Server Actions와 폼
이번 글은 코드는 거의 없습니다. 왜 새 모델이 필요한지, 그게 뭘 푸는지를 먼저 명확히 해두는 게 다음 글들 이해에 결정적이기 때문이에요.
CSR — 우리가 지금까지 해온 방식
지금까지 우리가 만든 모든 리액트 앱은 CSR(Client-Side Rendering) 방식이었습니다.
1. 브라우저가 빈 HTML 받음 (<div id="root"></div>만 있는 껍데기)
2. 자바스크립트 번들 다운로드
3. 자바스크립트 실행 → 리액트가 화면을 그림
4. 필요하면 fetch로 데이터 가져옴 → 다시 그림이 방식의 장점은 명확합니다.
- 한 번 로드된 후 페이지 전환이 빠르다 (#15에서 다룬 SPA 이점)
- 서버는 정적 파일만 호스팅하면 된다 (배포 단순)
- 인터랙션이 풍부한 UI 구현에 강하다
그런데 단점도 분명합니다.
단점 1. 첫 화면이 늦게 보임
브라우저가 빈 화면을 받고, 자바스크립트를 다운받고, 실행하고, 그제서야 화면이 그려집니다. 인터넷이 느리거나 디바이스가 약하면 흰 화면이 오래 보이죠.
단점 2. SEO가 어렵다
검색 엔진의 크롤러는 자바스크립트를 안 돌리거나 늦게 돌립니다. 빈 HTML만 보면 본문을 인덱싱하지 못해 검색 노출이 약해집니다.
단점 3. 자바스크립트 번들이 커진다
화면을 그리기 위한 모든 코드(컴포넌트, 라이브러리, 데이터 처리 로직)가 클라이언트로 다운로드돼야 합니다. 라이브러리 하나 추가할 때마다 사용자가 받는 용량도 커집니다.
단점 4. 데이터 페칭이 워터폴
서버 응답 → JS 다운로드 → JS 실행 → fetch → 또 fetch → ... 단계마다 기다림이 누적됩니다.
SSR — 서버에서 HTML을 미리 만들기
이 문제들을 풀기 위해 고전적으로 사용된 방식이 **SSR(Server-Side Rendering)**입니다. PHP나 Ruby on Rails 같은 전통적인 웹 서버 모델로 회귀하는 거예요.
1. 브라우저가 페이지 요청
2. 서버에서 React를 실행해 HTML을 만듦
3. 완성된 HTML을 보냄 (콘텐츠가 이미 들어있음)
4. 브라우저가 그 HTML을 표시
5. 동시에 JS 번들도 받아서 인터랙션을 활성화 (hydration)hydration이라는 단어가 등장했는데, "이미 그려진 정적 HTML에 JS로 이벤트 핸들러를 붙여 살아있는 컴포넌트로 만든다"는 뜻입니다. 마른 HTML에 JS의 물을 부어 살린다는 비유에서 왔어요.
SSR은 첫 화면 속도와 SEO 문제를 풀어줍니다. 하지만 여전히 모든 컴포넌트 코드가 클라이언트로 전송돼서 hydration되어야 하므로, 번들 크기 문제는 그대로 남습니다.
RSC — React Server Components의 등장
여기서 React 팀이 한 발 더 나갑니다. "애초에 클라이언트에 갈 필요 없는 컴포넌트도 있지 않을까?"
블로그 글의 본문을 생각해보세요. 한 번 그려진 후 사용자가 클릭하거나 입력할 게 별로 없습니다. 그저 데이터를 가져와 화면에 출력할 뿐. 이런 컴포넌트라면 서버에서만 실행하고, HTML(또는 그에 가까운 표현)만 클라이언트로 보내면 됩니다. 컴포넌트의 자바스크립트 코드 자체를 클라이언트에 보낼 이유가 없어요.
이게 **React Server Components(RSC)**의 핵심 아이디어입니다.
1. 브라우저가 페이지 요청
2. 서버에서 Server Components를 실행
3. Server Components는 데이터베이스/API 직접 호출 가능
4. 결과를 직렬화된 형태로 전송 (HTML + 클라이언트 컴포넌트 자리만 표시)
5. 클라이언트는 받은 HTML을 표시 + Client Components만 hydrateRSC가 풀어내는 것들:
- 번들 크기 절감 — 정적인 컴포넌트들의 코드는 아예 클라이언트로 안 감
- 데이터 페칭이 단순해짐 — Server Component에서 그냥
await db.query(...)하면 됨 (네트워크 왕복 없음) - 민감 정보 보호 — API 키나 DB 자격증명이 클라이언트에 절대 노출 안 됨
- SEO와 첫 화면 속도 — SSR의 장점은 그대로
그래서 무엇이 어디서 실행되는가
이 시리즈에서 계속 등장할 핵심 개념입니다. 머릿속에 미리 그려두세요.
| 컴포넌트 종류 | 실행 위치 | 사용 가능한 것 | 사용 불가 |
|---|---|---|---|
| Server Components | 서버에서만 (한 번) | DB 직접 접근, 환경변수, fs 모듈, async/await | useState, useEffect, 이벤트 핸들러, 브라우저 API |
| Client Components | 서버(SSR) + 클라이언트(hydration) | useState, useEffect, 이벤트, 브라우저 API | DB 직접 접근 (보안), fs |
같은 페이지 안에서 두 종류가 공존합니다. 페이지의 정적인 콘텐츠 부분은 Server Component, 인터랙션이 필요한 부분(폼, 토글, 드롭다운 등)은 Client Component로. 이 경계를 잘 그리는 게 모던 리액트 앱 설계의 핵심이에요.
Next.js와의 관계
React Server Components는 React 자체의 기능이지만, 이걸 실제로 쓰려면 빌드 도구와 서버 인프라가 필요합니다. RSC를 직렬화하고, 라우팅을 처리하고, 캐싱을 관리하고, 서버를 띄워주는 일을 누군가 해줘야 하거든요.
Next.js는 React 팀과 가장 긴밀히 협업하는 메타프레임워크로, RSC를 정식으로 지원하는 가장 성숙한 선택지입니다 (Remix, Waku 같은 다른 옵션도 있지만요). 이 시리즈에서는 Next.js를 쓰겠습니다.
이 블로그(schoolofweb.net) 자체도 Next.js로 만들어졌습니다. 시리즈를 읽으면서 "이 사이트는 어떻게 동작할까?" 궁금해하셔도 좋아요 — Server Components로 글을 가져오고, 클라이언트에서 인터랙션이 필요한 부분만 Client Components로 분리한 구조입니다.
멘탈 모델 전환의 핵심
이 시리즈를 시작하기 전에 가장 중요한 마인드셋 두 가지:
1. "이 코드는 어디서 실행되나?"를 항상 의식하라
지금까지 우리는 "코드는 다 브라우저에서 돈다"는 단일 환경에서 살았습니다. 모던 리액트에서는 같은 파일 안에서도 서버에서 도는 코드와 클라이언트에서 도는 코드가 섞여 있을 수 있습니다. 이 경계를 의식하지 않으면 빠르게 혼란에 빠집니다.
매번 자문하세요. "이 함수는 서버에서 실행되나, 클라이언트에서 실행되나?" 그러면 데이터 페칭이 어떻게 동작해야 하는지, 환경변수에 접근 가능한지, 이벤트 핸들러를 달아도 되는지가 명확해집니다.
2. 기본은 Server Component, 필요한 것만 Client Component
새 컴포넌트를 만들 때 기본이 Server Component입니다 (Next.js App Router에서). Client Component가 필요한 명확한 이유(상태, 이벤트, 브라우저 API)가 있을 때만 'use client' 디렉티브를 붙여 변환합니다. 이 디폴트가 번들 크기를 작게 유지하는 핵심입니다.
이 시리즈에서 배우게 될 것
다음 글부터 차근차근 다룰 내용입니다.
- #2 Next.js 프로젝트 시작, App Router 파일 구조, layout 시스템
- #3
'use client'디렉티브와 서버/클라이언트 컴포넌트 경계 - #4 Server Component에서
await fetch(...)패턴, 캐싱 - #5 Suspense와
loading.tsx,use()훅으로 로딩 처리 - #6 Server Actions로 폼 제출과 mutation 다루기
마지막엔 작은 미니 프로젝트(쪽지 같은 단순 앱)로 모든 걸 합쳐봅니다.
마무리
이번 글에서는 왜 새 모델이 필요한지를 먼저 짚었습니다.
- CSR — 빠른 인터랙션 ✅, 첫 화면 느림 / SEO 약함 / 큰 번들 ❌
- SSR — 첫 화면 빠름 / SEO 좋음 ✅, 여전히 모든 코드 클라이언트로 전송 ❌
- RSC — 정적 콘텐츠는 서버에서만 실행되고 코드 자체가 클라이언트로 안 감
머릿속에 자리 잡아야 할 두 가지 마인드셋:
- "이 코드는 어디서 실행되나?"를 항상 의식
- 기본은 Server Component, 필요한 것만 Client Component
다음 글인 "모던 리액트 + Next.js #2 Next.js 시작과 App Router"에서는 실제로 Next.js 프로젝트를 만들고, App Router의 파일 기반 라우팅과 layout 시스템을 직접 만져보겠습니다.