Next.js 15+ 설치 및 프로젝트 초기 구조 분석

웹 프레임워크의 진화 속도는 가공할 만큼 빠릅니다. 그 중심에 서 있는 Next.js 15는 단순히 성능 개선을 넘어, 서버와 클라이언트의 경계를 허물고 개발자 경험(DX)을 극대화하는 방향으로 나아가고 있습니다.
과거의 웹이 단순히 서버에서 만든 정적 페이지를 던져주는 방식이었다면, 이제는 사용자의 상호작용에 따라 실시간으로 반응하면서도 SEO와 초기 로딩 속도를 모두 잡아야 하는 복합적인 환경이 되었습니다. Next.js 15는 이러한 현대 웹의 요구사항을 가장 우아하게 해결해주는 도구입니다.
1. Deep Dive: Next.js 15의 심장, App Router 이해하기
Next.js 15의 가장 큰 특징은 App Router 시스템의 성숙입니다. 이를 이해하기 위해 한 가지 비유를 들어보겠습니다.
[Analogy] 거대한 백화점의 안내 데스크와 매장
기존의 방식(Pages Router)이 고객이 물건을 찾을 때마다 매번 안내 데스크(서버)에 가서 위치를 물어보고 전체 지도를 다시 받아오는 방식이었다면, Next.js 15의 App Router는 고객이 자주 가는 층(Layout)은 그대로 둔 채, 구경하고 싶은 매장(Page)의 물건만 쏙쏙 바꿔주는 스마트한 쇼핑 시스템과 같습니다. 필요한 부분만 부분적으로 업데이트하되, 전체적인 구조는 유지하여 사용자에게 끊김 없는 경험을 제공하죠.
작동 원리의 핵심: Server Components
Next.js 15에서는 기본적으로 모든 컴포넌트가 Server Components로 동작합니다. 이는 자바스크립트 번들 크기를 획기적으로 줄여줍니다. 브라우저가 해석해야 할 무거운 코드는 서버에서 미리 처리하고, 최종 결과물인 HTML만 클라이언트에 전달하기 때문입니다.
2. Hands-on: 프로젝트 설치 및 실전 구조 설계
이제 막 프로젝트를 시작한다고 가정해 봅시다. 단순히 "설치"하는 것을 넘어, 확장 가능한 구조를 잡는 것이 시니어의 역량입니다.
단계 1: 프로젝트 생성
터미널에서 아래 명령어를 입력하여 최신 버전의 Next.js 프로젝트를 생성합니다.
npx create-next-app@latest my-next15-project
설치 과정에서 다음과 같은 옵션을 권장합니다:
- TypeScript: Yes (안정적인 타입 시스템 구축)
- ESLint / Tailwind CSS: Yes (스타일링과 코드 품질 관리)
- src/ directory: Yes (소스 코드 보호 및 정리)
- App Router: Yes (필수 선택)
- Import Alias (@/*): Yes (경로 지옥 탈출)
단계 2: 초기 폴더 구조 분석 (실제 이커머스 적용 사례)
설치가 완료되면 src/app 폴더를 중심으로 구조가 생성됩니다. 이를 실무에서 자주 쓰이는 이커머스 대시보드 구조로 재구성해 보겠습니다.
// src/app/dashboard/layout.tsx
// 모든 대시보드 페이지에서 공통으로 사용되는 레이아웃입니다.
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<section className="flex min-h-screen">
<nav className="w-64 bg-gray-100 p-4">
{/* 사이드바 메뉴 로직 (예: 상품 관리, 주문 내역) */}
<p className="font-bold">Admin Menu</p>
</nav>
<main className="flex-1 p-8">
{children} {/* 여기에 개별 페이지(page.tsx) 내용이 들어갑니다. */}
</main>
</section>
);
}
// src/app/dashboard/products/page.tsx
// 실제 상품 목록을 보여주는 서버 컴포넌트입니다.
import { Suspense } from 'react';
export default async function ProductsPage() {
// 서버에서 직접 데이터를 페칭합니다. (API Route 없이도 가능)
// Next.js 15에서는 fetch의 기본 캐싱 정책이 'no-store'로 변경되어
// 실시간성이 강조된 데이터를 기본으로 합니다.
const products = await fetch('https://api.example.com/products').then(res => res.json());
return (
<div>
<h1 className="text-2xl font-bold mb-4">상품 리스트</h1>
<div className="grid gap-4">
{products.map((item: any) => (
<div key={item.id} className="border p-4 rounded-lg shadow-sm">
<h3>{item.name}</h3>
<p className="text-blue-600">{item.price}원</p>
</div>
))}
</div>
</div>
);
}
3. Troubleshooting: 흔히 겪는 초기 난관들
프로젝트 초기 세팅 시 다음과 같은 상황에 직면할 수 있습니다.
- Error: "use client" 지시어 누락
- 상황: useState나 useEffect 같은 리액트 훅을 사용했는데 에러가 발생합니다.
- 해결: Next.js 15는 서버 컴포넌트가 기본입니다. 브라우저의 이벤트(클릭, 입력)나 상태가 필요한 컴포넌트 최상단에 'use client';를 반드시 선언하세요.
- Hydration Mismatch
- 상황: 서버에서 만든 HTML과 클라이언트에서 렌더링한 결과가 다를 때 발생합니다. (예: new Date() 출력)
- 해결: 시간 정보처럼 변하는 값은 useEffect 안에서 업데이트하거나, suppressHydrationWarning 속성을 활용하세요.
4. Trade-offs: 무엇을 얻고 무엇을 고려해야 하는가?
Next.js 15를 도입함으로써 얻는 명확한 이점은 **성능(Lighthouse 점수)**과 개발 생산성입니다. 하지만 모든 약에는 부작용이 있듯 고려할 점도 있습니다.
- 러닝 커브: 서버 컴포넌트와 클라이언트 컴포넌트의 통신 방식(Server Actions 등)을 익히는 데 시간이 필요합니다.
- 캐싱 정책의 변화: Next.js 15부터는 fetch 요청이 기본적으로 캐싱되지 않습니다. 성능 최적화를 위해 의도적으로 force-cache 설정을 관리해야 하는 번거로움이 생길 수 있습니다.
- 라이브러리 호환성: 일부 오래된 외부 라이브러리가 서버 컴포넌트 구조에서 제대로 동작하지 않을 수 있어 확인이 필요합니다.
요약 및 제언
Next.js 15는 현대적인 웹 애플리케이션을 구축하기 위한 가장 견고한 기반입니다. app 디렉토리 기반의 구조는 복잡한 비즈니스 로직을 효율적으로 분리하게 해주며, 서버 컴포넌트는 사용자에게 필요한 최소한의 코드만 전송하게 돕습니다.
오늘 바로 프로젝트를 생성하고, 여러분의 비즈니스 로직을 이 구조 위에 얹어보시는 건 어떨까요? 특히 Server Components를 활용해 API 호출 횟수를 줄이는 실험부터 시작해 보시길 권장합니다.