Next.js 프로젝트가 커지면 겪게 되는 문제들
Next.js: 몸집이 커지면 옷도 바꿔 입어야 한다
처음엔 create-next-app 한 방이면 충분했죠. 근데 사용자가 늘고, 기능이 덕지덕지 붙다 보면 어느 순간 빌드 시간이 10분을 넘어가고 서버가 헐떡거리는 시점이 옵니다.
엔터프라이즈급으로 넘어가려면 전략을 바꿔야 합니다. 제가 겪은 삽질을 바탕으로 핵심 전략을 정리해 봤습니다.
트래픽이 터지기 전에 알아야 할 것들
스케일링 문제는 항상 "갑자기" 찾아옵니다. 어느 날 마케팅 팀이 광고를 집행하고, 트래픽이 10배 뛰고, 서버가 죽습니다. 그때 "아, 미리 대비할 걸"이라고 후회해봤자 늦었습니다.
그래서 트래픽이 터지기 전에 아키텍처를 점검해야 합니다. Next.js 앱이 커졌을 때 겪는 전형적인 문제는 세 가지입니다: 빌드 시간 증가, 서버 부하 급증, 코드베이스 복잡도 폭발. 각각의 해결 전략을 딱 짚어드리겠습니다.
1. 렌더링의 뷔페화: 골라 먹는 재미
모든 페이지를 SSR(서버 사이드 렌더링)로 돌린다? 서버 비용으로 회사 통장 거덜 낼 작정인가요. Next.js가 깡패인 이유는 페이지별로 렌더링 방식을 섞을 수 있어서입니다.
- 약관, 회사 소개 페이지: 무조건 SSG입니다. 빌드할 때 HTML 딱 한 번 만들어서 CDN에 뿌려놓으면, 서버 부하가 '0'입니다. 서버가 죽어도 이 페이지들은 살아있습니다.
- 상품 리스트: ISR(Incremental Static Regeneration) 쓰세요. "1분마다 한 번만 새로고침 해줘"라고 설정하면, DB 쿼리를 1분에 딱 한 번만 날립니다. 10만 명이 접속해도 DB는 평온하죠. Next.js 15에서는
revalidate옵션이 더 세밀해져서, 태그 기반 캐시 무효화도 가능합니다. - 마이페이지, 대시보드: 이건 어쩔 수 없이 SSR이나 CSR로 가야죠. 개인화된 정보니까요. 하지만 여기서도 꼼수가 있습니다. 페이지의 고정 부분(레이아웃, 사이드바)은 정적으로 만들고, 개인화된 부분만 Streaming으로 나중에 채우면 체감 속도가 확 올라갑니다.
- 실시간 데이터 (주식, 채팅): CSR이 맞습니다. WebSocket이나 Server-Sent Events로 실시간 업데이트를 받아야 하니까요.
이걸 적재적소에 섞어 쓰는 게 아키텍트의 실력입니다. "이 페이지는 얼마나 자주 바뀌는가?", "개인화가 필요한가?"를 기준으로 판단하세요.
PPR (Partial Prerendering): 게임 체인저
Next.js 15에 실험적으로 도입된 PPR은 한 페이지 안에서 정적/동적 영역을 나눠줍니다. 프레임워크가 자동으로 정적 부분은 빌드 시 생성하고, 동적 부분은 Suspense와 함께 스트리밍합니다. 개발자가 렌더링 전략을 일일이 선택할 필요가 줄어드는 미래가 오고 있습니다.
2. API: 모놀리스를 찢어라
pages/api 폴더 안에 모든 백엔드 로직을 때려 박고 계신가요? 프로젝트 초기엔 좋죠. 심지어 권장합니다. 근데 API 엔드포인트가 50개를 넘어가면? 그게 병목의 원흉이 됩니다.
무거운 비즈니스 로직은 별도 마이크로서비스(NestJS, Go 등)로 떼어내고, Next.js는 BFF(Backend For Frontend) 역할만 하게 하세요. 즉, 프론트엔드가 쓰기 편하게 데이터를 '가공'해서 전달하는 셔틀 역할에 집중시키는 겁니다.
가벼운 로직(리다이렉트, 간단한 인증, A/B 테스트)은 Edge Functions를 쓰세요. 사용자가 지구 반대편에 있어도, 가장 가까운 서버에서 0.1초 만에 처리해 줍니다. Vercel이 이거 하나는 기가 막히게 잘 만들었습니다.
Route Handlers vs Server Actions
Next.js 15에서는 Route Handlers(app/api의 후속)와 Server Actions 두 가지 옵션이 있습니다.
- Route Handlers: 외부에서 호출해야 하는 API (웹훅, 모바일 앱 등)에 적합
- Server Actions: Next.js 앱 자체에서만 쓰이는 서버 로직에 적합. API 라우트를 만들 필요가 없어 파일 수가 줄고, 타입 안전성이 끝까지 보장됩니다.
초기에는 모든 서버 로직을 Server Actions로 시작하고, 외부 연동이 필요할 때만 Route Handlers를 추가하는 전략을 추천합니다.
3. 이미지: 트래픽 도둑 잡기
LCP 점수 깎아먹고, 데이터 요금 폭탄 터트리는 주범이 바로 이미지입니다.
<img> 태그 그냥 쓰지 마세요. 제발 next/image 쓰세요.
얘가 알아서 WebP/AVIF로 변환해주고, 사이즈 줄여주고, Lazy Loading까지 해줍니다. 코드 한 줄 바꿨을 뿐인데 성능 점수가 20점 오르는 기적을 볼 수 있습니다.