프로젝트 세팅 시간을 줄이기 위한 Next.js 보일러플레이트 제작기
1. 문제 정의
회사에서 새로운 프로젝트를 시작할 때마다 동일한 작업이 반복됐다.
Next.js 프로젝트 생성, 린터 설정, 테스트 환경 구축, CI/CD 파이프라인 작성, 에러 모니터링 연동...
create-next-app이 만들어주는 건 뼈대뿐이다.
프로덕션 수준의 프로젝트를 시작하려면 매번 동일한 세팅 작업을 처음부터 다시 해야 했다.
문제는 이 세팅 작업이 단순 반복임에도 시간이 상당히 든다는 점이었다.
- ESLint, Prettier, Husky, lint-staged 설정
- Vitest + Playwright 테스트 환경 구축
- Storybook + Chromatic 시각적 테스트 연동
- Sentry 에러 모니터링 설정
- GitHub Actions CI/CD 파이프라인 작성
- MSW API 모킹 환경 구성
이 작업들을 매번 반복하는 건 비효율적이었다. 한 번 잘 만들어두고 재사용하는 게 낫다고 판단했다.
2. 설계 목표
단순한 스타터 템플릿이 아닌, 다음 조건을 충족하는 보일러플레이트를 목표로 했다.
- 회사 기술 스택과 동일한 구성
- clone 후 바로 개발을 시작할 수 있는 상태
- 테스트, 모니터링, CI/CD까지 포함된 프로덕션 레디 구조
- 선택적 기능은 환경변수로 on/off 가능 (예: Sentry)
- 새 기능 추가 시 참고할 수 있는 예제 코드 포함
3. 기술 스택
회사에서 사용하는 기술 스택을 기준으로 선정했다.
| 분류 | 기술 | 선정 이유 |
|---|---|---|
| Framework | Next.js 16 | 사내 표준 프레임워크 |
| Language | TypeScript strict | 런타임 에러 사전 차단 |
| Styling | Tailwind CSS 4 | 빠른 UI 개발, 사내 사용 중 |
| Server State | TanStack Query 5 | 서버 상태 캐싱 및 동기화 |
| Client State | Zustand 5 | 최소한의 보일러플레이트로 클라이언트 상태 관리 |
| Schema Validation | Zod 4 | 런타임 타입 검증, TypeScript 타입 추론 연동 |
| Unit Test | Vitest 4 | Vite 기반으로 빠른 실행 속도 |
| E2E Test | Playwright | 크로스 브라우저 E2E 테스트 |
| API Mocking | MSW 2 | 테스트와 Storybook에서 API 모킹 공유 |
| Component Dev | Storybook 10 | 컴포넌트 단위 개발 및 문서화 |
| Visual Regression | Chromatic | PR 단위 시각적 회귀 테스트 자동화 |
| Monitoring | Sentry 10 | 프로덕션 에러 추적 |
| Linter/Formatter | ESLint 9 + Prettier | 코드 스타일 일관성 유지 |
| Git Hooks | Husky + lint-staged | 커밋 전 자동 검사 |
상태 관리 분리 전략
상태 관리는 서버 상태와 클라이언트 상태를 명확히 분리했다.
- 서버 상태 → TanStack Query: API 응답 캐싱, 리페칭, 동기화
- 클라이언트 상태 → Zustand: UI 상태, 로컬 전용 상태
두 영역을 하나의 도구로 관리하면 책임이 섞인다. 역할을 분리함으로써 각 상태의 생명주기를 독립적으로 관리할 수 있다.
테스트 이중 전략
테스트는 두 레이어로 구성했다.
- Vitest → 단위 테스트, 컴포넌트 테스트, Storybook 스토리 테스트
- Playwright → E2E 테스트 (Chromium, Firefox, WebKit)
MSW를 API 모킹 레이어로 두어 Vitest와 Storybook에서 동일한 핸들러를 공유한다. 모킹 로직을 한 곳에서 관리하므로 테스트 간 API 응답의 일관성을 유지할 수 있다.
4. 프로젝트 구조
src/
├── app/ # App Router 페이지
├── components/ # 공통 컴포넌트
│ └── [Feature]/
│ ├── Feature.tsx
│ └── Feature.stories.tsx
├── hooks/ # 커스텀 훅 (TanStack Query 래핑)
├── stores/ # Zustand 스토어
├── lib/ # 유틸리티
│ └── validations/ # Zod 스키마
├── types/ # 공유 타입
├── constants/ # 상수
├── providers/ # React Context Providers
└── mocks/ # MSW 핸들러
e2e/ # Playwright E2E 테스트
.storybook/ # Storybook 설정
.github/workflows/ # GitHub Actions CI/CD구조 설계 원칙
- 컴포넌트와 스토리는 같은 디렉토리에 위치시켜 응집도를 높였다.
- hooks 디렉토리에서 TanStack Query를 래핑하여, 컴포넌트가 데이터 fetching 로직에 직접 의존하지 않도록 했다.
- mocks 디렉토리에서 MSW 핸들러를 중앙 관리하여, 테스트와 Storybook이 동일한 모킹 데이터를 사용한다.
5. 에러 처리 흐름
Next.js App Router의 에러 바운더리 구조와 Sentry를 연동했다.
에러 발생
→ error.tsx (페이지 레벨 에러 바운더리)
→ global-error.tsx (전역 에러 바운더리)
→ Sentry로 에러 전송| 파일 | 역할 |
|---|---|
error.tsx | 페이지 레벨 에러 처리 + Sentry 전송 |
global-error.tsx | 전역 에러 처리 + Sentry 전송 |
instrumentation.ts | 서버 사이드 Sentry 초기화 |
instrumentation-client.ts | 클라이언트 사이드 Sentry 초기화 |
Sentry DSN을 설정하지 않으면 모니터링이 비활성화되고, 에러 없이 정상 동작한다. 새 프로젝트에서 Sentry가 필요하지 않은 단계에서도 문제 없이 사용할 수 있다.
6. CI/CD 파이프라인
GitHub Actions로 두 개의 워크플로우를 구성했다.
CI Pipeline
main/prod 브랜치 push 및 PR에서 자동 실행된다.
Lint → Type Check → Unit Test → Build → E2E Test각 단계가 순차적으로 실행되며, 하나라도 실패하면 파이프라인이 중단된다.
Chromatic Pipeline
모든 PR에서 Storybook 스토리의 시각적 변경을 자동 감지한다.
CHROMATIC_PROJECT_TOKEN미설정 시 자동 skipmain브랜치 변경은 자동 승인- PR에서 시각적 변경이 감지되면 리뷰 후 승인/거부
두 워크플로우 모두 pnpm store 캐시를 사용하여 반복 실행 시 설치 시간을 단축했다.
7. DX(Developer Experience) 설계
보일러플레이트의 핵심은 clone 후 빠르게 개발에 진입하는 것이다. 이를 위해 다음 요소들을 포함했다.
즉시 시작 가능한 환경
git clone <repo-url>
nvm use
pnpm install
cp .env.example .env
pnpm dev모든 환경변수는 선택사항이다. 아무것도 설정하지 않아도 개발 서버가 정상 동작한다.
자동화된 코드 품질 관리
커밋 시점에 Husky + lint-staged가 자동으로 ESLint와 Prettier를 실행한다. 개발자가 별도로 린트를 실행할 필요가 없다.
예제 코드 제공
각 레이어에 예제 코드를 포함했다.
- API Route Handler 예제
- TanStack Query 커스텀 훅 예제
- Zustand 스토어 예제
- Zod 스키마 예제
- MSW 핸들러 예제
새 기능을 추가할 때 예제를 참고하여 동일한 패턴으로 작성할 수 있다.
8. 회고
- 보일러플레이트 제작에 투자한 시간은 이후 프로젝트에서 빠르게 회수된다.
create-next-app은 출발점일 뿐이다. 프로덕션까지의 거리는 생각보다 멀다.- 핵심은 기술 스택 나열이 아니라 각 도구가 어떻게 연결되는지 설계하는 것이다.
- 선택적 기능(Sentry, Chromatic)은 환경변수로 제어하여, 프로젝트 상황에 맞게 점진적으로 활성화할 수 있도록 했다.
9. 향후 계획
- npm 패키지로 배포하여 사내에서 더 빠르고 편하게 프로젝트를 시작할 수 있도록 개선