React만으로는 부족한 것
Article
React 편 2강에서 이상한 걸 봤다. Vite로 만든 프로젝트의 index.html이 텅 비어 있었다.
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
그때는 "화면은 전부 자바스크립트가 채운다"고만 하고 넘어갔다. 이번 강의는 거기서 시작한다. 그게 왜 문제인가.
검색엔진이 보는 것
구글 크롤러가 이 페이지를 가져간다고 하자. HTTP 요청을 보내면 서버는 저 HTML을 그대로 돌려준다.
- <body>
- <div id="root"></div>
- <script src="/src/main.jsx"></script>
- </body>
- 제목도, 본문도, 링크도 없다
아무 내용이 없다. 상품 이름도, 블로그 글도, 아무것도 없다.
크롤러가 자바스크립트를 실행해주기를 기대할 수는 있다. 실제로 구글은 실행한다. 하지만 시간이 오래 걸리고, 늘 성공하지도 않으며, 다른 검색엔진과 SNS 크롤러는 대부분 실행하지 않는다.
카카오톡에 링크를 붙여넣었을 때 미리보기가 안 뜨는 이유가 이것이다.
사용자가 겪는 것
SEO만 문제가 아니다. 첫 화면이 뜨기까지 무슨 일이 벌어지는지 보자.
React 편 15강에서 useEffect로 데이터를 받아왔다. 그건 화면이 그려진 뒤에 실행된다. 그래서 순서가 이렇게 된다.
- 01
HTML 을 받는다
빈 div 하나뿐이다. 화면은 흰색이다.
- 02
자바스크립트 번들을 내려받는다
React 와 우리 코드 전부. 이게 끝나야 아무것도 시작된다.
- 03
React 가 실행되어 화면을 그린다
이제 로딩 스피너가 보인다.
- 04
그제야 데이터를 요청한다
useEffect가 실행된다. 서버 응답을 또 기다린다.
각 단계가 앞 단계를 기다린다. 이걸 요청 폭포(waterfall)라 부른다.
서버에서 미리 그려두면
해결책은 단순하다. 서버가 완성된 HTML을 보내주면 된다.
<body>
<h1>노트북</h1>
<p>1,200,000원</p>
<button>담기</button>
</body>
크롤러는 자바스크립트 없이도 내용을 읽는다. 사용자는 흰 화면 대신 글자를 즉시 본다.
CSR
Client Side Rendering
- 브라우저가 만든다
- HTML 은 빈 껍데기
- JS 를 받고 실행해야 보인다
- 크롤러가 내용을 못 읽는다
SSR
Server Side Rendering
- 서버가 만들어 보낸다
- HTML 에 내용이 있다
- 즉시 보인다
- 크롤러가 읽는다
그래서 SSR은 CSR을 없애는 게 아니라 앞에 한 단계를 덧붙이는 것이다. 첫 화면은 서버가, 이후 상호작용은 브라우저가 맡는다.
직접 만들면 어떻게 되나
React를 서버에서 실행하는 건 가능하다. renderToString()이라는 함수가 있다.
import { renderToString } from "react-dom/server";
const html = renderToString(<App />);
이 한 줄로 끝날 것 같지만, 실제로 돌아가는 앱을 만들려면 이런 것들이 따라온다.
- SSR 을 직접 구현한다면
- 서버 라우팅
- 데이터 미리 받기
- 번들링
- 코드 분할
- 하이드레이션
- 캐싱
- 이미지 · 폰트 최적화
전부 만들 수 있다. 다만 그게 우리 앱의 기능은 아니다. 매번 똑같이 반복되는 일이다.
Next.js 가 하는 일
Next.js는 이 목록을 전부 가져간다.
| 필요한 것 | Next.js 가 주는 것 |
|---|---|
| 라우팅 | 폴더 구조가 곧 URL |
| 서버 렌더링 | 기본값. 아무것도 안 해도 된다 |
| 데이터 가져오기 | 컴포넌트에서 바로 await |
| 번들링 · 코드 분할 | Turbopack 이 자동으로 |
| 캐싱 | "use cache" 한 줄 |
| 이미지 · 폰트 | next/image · next/font |
| SEO 메타데이터 | metadata 객체를 내보내면 끝 |
React 편 1강에서 **"React는 라이브러리다"**라고 했다. 화면 그리는 일만 하고 나머지는 직접 고르라는 뜻이었다.
Next.js는 프레임워크다. 나머지를 대신 정해준다.
React
라이브러리
- 화면 그리기만 한다
- 라우팅 · 통신 · 빌드는 직접
- 내가 React 를 부른다
- 자유롭지만 결정할 게 많다
Next.js
프레임워크
- 앱의 구조를 정해준다
- 규칙대로 파일을 두면 동작한다
- Next.js 가 내 코드를 부른다
- 편하지만 규칙을 배워야 한다
"내가 부르는가, 불리는가"가 둘을 가르는 기준이다. Next.js에서는 page.tsx라는 이름으로 파일을 두면 Next.js가 알아서 그 파일을 찾아 실행한다. 우리가 부르지 않는다.
무엇을 얻고 무엇을 잃나
정직하게 짚는다.
| 얻는 것 | 잃는 것 |
|---|---|
| SEO 와 링크 미리보기 | 서버가 필요하다 (정적 배포만으로 부족할 때가 있다) |
| 빠른 첫 화면 | 배울 규칙이 늘어난다 |
| 최적화가 기본값 | 프레임워크의 방식을 따라야 한다 |
| 서버 코드와 클라이언트 코드가 한 곳에 | 그 경계를 늘 의식해야 한다 |
마지막 줄이 이 시리즈의 절반을 차지한다. 어디까지가 서버이고 어디부터가 브라우저인가. Part 3에서 본격적으로 다룬다.
이 시리즈가 다루는 것
Next.js에는 두 가지 라우터가 있다. 옛날 방식인 Pages Router와 지금 방식인 App Router다.
이 시리즈는 App Router만 다룬다. 새 프로젝트는 전부 이쪽이고, 서버 컴포넌트를 비롯한 최신 기능도 여기에만 있다.
기준 버전은 Next.js 16이다. 이 버전에서 꽤 많은 것이 바뀌었다.
- 01
Turbopack 이 기본 번들러가 됐다
빌드가 2~5배, 새로고침이 최대 10배 빨라졌다. 설정할 게 없다.
- 02
캐싱이 완전히 opt-in 으로 바뀌었다
예전에는 Next.js 가 알아서 캐싱했다. 이제는
"use cache"를 쓴 곳만 캐싱된다. 13강에서 다룬다. - 03
middleware.ts 가 proxy.ts 로 바뀌었다
이름과 실행 환경이 명확해졌다. 18강에서 다룬다.
- 04
params 와 cookies 를 반드시 await 해야 한다
동기 접근이 완전히 사라졌다. 5강에서 만난다.
인터넷에 있는 예전 글이 지금 버전과 다르게 말하는 경우가 많다. 이 시리즈는 16 기준으로만 쓴다.
정리하면
- React만 쓰면 브라우저가 HTML을 만든다. 그래서 크롤러가 빈 페이지를 본다.
- 사용자도 손해를 본다. JS를 받고 실행하고 데이터를 요청하는 동안 흰 화면을 본다.
- 서버가 완성된 HTML을 보내면 둘 다 해결된다. 이후 자바스크립트가 이벤트를 붙이는 과정이 하이드레이션이다.
- SSR을 직접 만들 수는 있지만, 우리 앱의 기능이 아닌 일에 시간을 쓰게 된다.
- React는 라이브러리, Next.js는 프레임워크다. 내가 부르는가, 불리는가의 차이다.
- SEO가 필요 없는 앱이라면 순수 React로 충분하다.
- 이 시리즈는 App Router와 Next.js 16만 다룬다.
다음 강의에서는 프로젝트를 만들고, 폴더를 만드는 것만으로 URL이 생기는 걸 본다.