이전 세션에서 살펴본 CSR의 한계를 Next.js는 어떻게 해결할까요?
| CSR 문제 | Next.js 해결책 |
|---|---|
| 빈 HTML (SEO에 불리) | 서버에서 HTML을 미리 렌더링 (SSR/SSG) |
| 대용량 번들 (느린 로딩) | 자동 코드 분할 - 페이지별 필요한 코드만 전송 |
| API 키 노출 | 서버 컴포넌트에서 민감 정보 처리 |
| 데이터 워터폴 | 서버에서 렌더링 전에 필요한 데이터를 미리 수집할 수 있어 워터폴을 줄이기 쉬움 |
위 표에서 "자동 코드 분할"이 언급되었는데, 이것이 정확히 무엇이고 왜 중요한지 알아봅시다.
React SPA(CSR)에서는 앱의 모든 코드가 하나의 커다란 번들로 묶여 전송됩니다. 홈 페이지에 접속했을 뿐인데, 관리자 페이지·설정 페이지·대시보드 코드까지 전부 다운로드해야 하는 것이죠.
Next.js의 자동 코드 분할은 이 방식을 뒤집습니다. 한 덩어리가 아니라 페이지마다 따로 포장해서, 사용자가 방문한 페이지에 필요한 코드만 전송합니다.
다운로드할 JavaScript 양이 줄어들고, 브라우저가 파싱·컴파일해야 하는 코드도 줄어듭니다. 특히 모바일 환경에서 체감 차이가 큽니다.
CSR (코드 분할 없음)
사용자 → 홈 페이지 접속 → bundle.js (500 KB) 전체 다운로드
Next.js (자동 코드 분할)
사용자 → 홈 페이지 접속 → 홈 전용 코드 (5 KB) + 공유 코드 (84 KB)만 다운로드
대시보드 페이지의 코드에서 에러가 발생하더라도, 홈 페이지나 소개 페이지는 영향을 받지 않습니다. 코드가 페이지별로 분리되어 있기 때문입니다.
단, 공통 레이아웃이나 공유 컴포넌트에서 에러가 발생하면 여러 페이지에 영향이 퍼질 수 있습니다. 이런 경우에는 다음 세션에서 살펴볼 error.tsx 에러 경계를 활용하여 에러 범위를 격리할 수 있습니다.
가장 좋은 점은 개발자가 아무 설정도 하지 않아도 된다는 것입니다. React에서 React.lazy를 수동으로 설정해야 했던 것과 달리, Next.js는 빌드 시 자동으로 처리합니다.
Next.js는 서버에서 HTML을 미리 렌더링할 수 있습니다. 빌드 시점에 미리 생성하거나, 사용자 요청 시마다 서버에서 생성하는 등 상황에 따라 전략을 선택할 수 있는데, 이 부분은 이후 챕터에서 자세히 다룹니다.
create-next-app으로 새 프로젝트를 만듭니다:
npx create-next-app@latest my-app
설치 과정에서 기본 설정 사용 여부를 묻습니다. Yes를 선택하면 TypeScript, ESLint, Tailwind CSS, App Router가 한 번에 적용됩니다:
✔ Would you like to use the recommended Next.js defaults?
› Yes, use recommended defaults - TypeScript, ESLint, Tailwind CSS, App Router
세부 옵션을 직접 고르려면 No, customize settings를 선택하세요:
✔ Would you like to use TypeScript? … Yes
✔ Which linter would you like to use? › ESLint
✔ Would you like to use React Compiler? … No
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like your code inside a `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the import alias? … No
className="text-lg font-bold"처럼 HTML에 직접 스타일을 적용하는 유틸리티 퍼스트 방식의 CSS 프레임워크입니다.생성된 프로젝트의 디렉토리 구조입니다:
my-app/
├── app/ # App Router 핵심 디렉토리
│ ├── layout.tsx # 루트 레이아웃 (필수)
│ ├── page.tsx # 홈 페이지 (/)
│ ├── globals.css # 글로벌 스타일
│ └── favicon.ico
├── public/ # 정적 파일 (이미지, 폰트 등)
├── next.config.ts # Next.js 설정 파일
├── eslint.config.mjs # ESLint 설정 (flat config)
├── tsconfig.json # TypeScript 설정
├── postcss.config.mjs
└── package.json
핵심 파일과 폴더의 역할:
| 경로 | 역할 |
|---|---|
app/ | App Router의 핵심. 이 폴더의 구조가 곧 URL 구조 |
app/layout.tsx | 모든 페이지를 감싸는 루트 레이아웃 (html, body 태그 포함) |
app/page.tsx | 홈 페이지 (/ 경로) |
public/ | 정적 파일. /image.png로 바로 접근 가능 |
next.config.ts | 리다이렉트, 이미지 도메인 등 Next.js 설정 |
프로젝트 디렉토리로 이동한 뒤 개발 서버를 실행합니다:
cd my-app
npm run dev
브라우저에서 http://localhost:3000에 접근하면 Next.js 기본 페이지가 보입니다.
이제 app/page.tsx를 열어 내용을 수정해 봅시다:
// app/page.tsx
export default function Home() {
return (
<main>
<h1>안녕하세요!</h1>
<p>나의 첫 Next.js 앱입니다.</p>
</main>
);
}
파일을 저장하면 브라우저가 자동으로 새로고침됩니다. 이것이 Fast Refresh입니다. 코드를 수정하면 전체 페이지를 새로고침하지 않고, 변경된 컴포넌트만 즉시 반영하며, 가능한 경우 컴포넌트의 상태(useState 값 등)도 유지됩니다.
다음 세션에서는 App Router의 핵심인 파일 기반 라우팅을 학습합니다.