Next.js 프로젝트 보일러플레이트 만들기: 시작 템플릿 구축하기
프로젝트를 시작할 때 매번 환경설정을 처음부터 하는 것은 꽤 귀찮은 일입니다. 특히 Next.js 프로젝트를 여러 번 진행하다 보면, 자주 쓰는 라이브러리와 폴더 구조, ESLint/Prettier 설정 등이 비슷해집니다. 이럴 때 보일러플레이트를 만들어 두면, 새로운 프로젝트를 단 몇 분 만에 시작할 수 있습니다. 이 글에서는 Next.js 15(App Router 기반)로 보일러플레이트 프로젝트를 만드는 방법을 단계별로 설명합니다.

이런 것을 만들 수 있습니다
이 글을 끝까지 따라 하면, 바로 실무에서 활용 가능한 Next.js 보일러플레이트가 준비됩니다. 즉, 프로젝트를 새로 시작할 때 복잡한 초기 설정을 건너뛰고 곧바로 개발에 집중할 수 있습니다.
GitHub에서 완성된 보일러플레이트 확인하기 →
주요 특징
- Next.js 15 (App Router) + TypeScript 환경
- Tailwind CSS + shadcn/ui 기반 컴포넌트 시스템
- ESLint + Prettier 자동 코드 정리
- Husky + lint-staged 커밋 전 검증 자동화
- 확장성과 유지보수에 최적화된 폴더 구조
boilerplate-nextjs-2508/
├── .husky/ # Git hooks 설정
├── public/ # 정적 파일
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── globals.css # 전역 스타일
│ │ ├── layout.tsx # 루트 레이아웃
│ │ └── page.tsx # 홈 페이지
│ ├── components/
│ │ └── ui/ # shadcn/ui 컴포넌트
│ │ └── button.tsx # 버튼 컴포넌트
│ ├── lib/
│ │ └── utils.ts # 유틸리티 함수
│ └── services/
│ ├── local-storage.ts # LocalStorage 래퍼
│ ├── session-storage.ts # SessionStorage 래퍼
│ └── logger.ts # 로거 서비스
├── .gitignore # Git 무시 파일
├── .gitmessage # 커밋 메시지 템플릿
├── commitlint.config.cjs # 커밋 메시지 검증
├── components.json # shadcn/ui 설정
├── eslint.config.mjs # ESLint 설정
├── next.config.ts # Next.js 설정
├── package.json # 의존성 및 스크립트
├── pnpm-lock.yaml # pnpm 락파일
├── postcss.config.mjs # PostCSS 설정
├── prettier.config.js # Prettier 설정
└── tsconfig.json # TypeScript 설정프로젝트 생성 및 github 연동
Next.js boilerplate 프로젝트의 기초를 만들고 GitHub 저장소와 연결합니다. create-next-app으로 최신 Next.js 프로젝트를 생성하고, 기본 설정을 커스터마이징한 후 버전 관리 시스템을 구축합니다.

1. 프로젝트 생성
create-next-app을 사용해 Next.js 프로젝트를 생성합니다.
이 도구는 대화형 프롬프트를 통해 프로젝트의 기본 설정을 구성할 수 있게 해줍니다.
npx create-next-app@latest my-boilerplate각 옵션에 대한 권장 설정과 이유
- TypeScript:
Yes- 타입 안정성과 개발 생산성 향상 - ESLint:
Yes- 코드 품질 관리 필수 - Tailwind CSS:
Yes- 빠른 스타일링과 일관성 src/directory:Yes- 프로젝트 구조 명확화- App Router:
Yes- Next.js 14 이후 주 Router 방법 - Turbopack:
Yes- Next.js 15부터 안정화되어 빌드 속도 대폭 개선 - Import alias:
No- custom 없이 기본@/경로 사용

import type { Metadata, Viewport } from "next"
import "./globals.css"
export const metadata: Metadata = {
title: "Next.js Boilerplate 2508",
description: "Next.js 프로젝트 보일러플레이트",
}
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
// maximumScale: 1, // 최대 확대 배율 제한
// userScalable: false, // 확대/축소 불가 설정
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="ko">
<body className="flex flex-col">{children}</body>
</html>
)
}3. CSS 설정 및 폰트 적용
기본 스타일과 한글 폰트를 설정합니다.
Pretendard는 한글과 영문이 조화롭게 어울리는 오픈소스 폰트입니다. 가독성이 뛰어나고 다양한 굵기를 지원하며 유지보수도 잘 되는 폰트로 추천합니다.
html, body, :root에 height: 100%를 적용하면 자식 요소들이 부모의 전체 높이를 참조할 수 있게 됩니다. 이를 통해 풀 스크린 레이아웃이나 sticky footer 같은 레이아웃 패턴을 쉽게 구현할 수 있습니다.
@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-dynamic-subset.min.css");
@import "tailwindcss";
html,
body,
:root {
height: 100%;
}
body {
font-family: "Pretendard Variable", Pretendard, -apple-system,
BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI",
"Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
}
...export default function Home() {
return (
<div className="flex items-center justify-center text-5xl flex-1">
Hello World!
</div>
);
}
# Git 초기화
git init
# develop 브랜치 생성 및 전환
git checkout -b develop
# GitHub 원격 저장소 연결 (SSH 방식, github 프로젝트 페이지 확인)
git remote add origin git@github.com:YOUR_USERNAME/boilerplate-nextjs-2508.git
# 모든 파일 스테이징 - (staing: 명령어를 통해 github에 추가할 파일들 선택)
git add .
# 첫 커밋
git commit -m "first commit: Next.js boilerplate initial setup"
# develop 브랜치 push
git push -u origin develop1. 프로젝트 생성
create-next-app을 사용해 Next.js 프로젝트를 생성합니다.
이 도구는 대화형 프롬프트를 통해 프로젝트의 기본 설정을 구성할 수 있게 해줍니다.
npx create-next-app@latest my-boilerplate각 옵션에 대한 권장 설정과 이유
- TypeScript:
Yes- 타입 안정성과 개발 생산성 향상 - ESLint:
Yes- 코드 품질 관리 필수 - Tailwind CSS:
Yes- 빠른 스타일링과 일관성 src/directory:Yes- 프로젝트 구조 명확화- App Router:
Yes- Next.js 14 이후 주 Router 방법 - Turbopack:
Yes- Next.js 15부터 안정화되어 빌드 속도 대폭 개선 - Import alias:
No- custom 없이 기본@/경로 사용
3. CSS 설정 및 폰트 적용
기본 스타일과 한글 폰트를 설정합니다.
Pretendard는 한글과 영문이 조화롭게 어울리는 오픈소스 폰트입니다. 가독성이 뛰어나고 다양한 굵기를 지원하며 유지보수도 잘 되는 폰트로 추천합니다.
html, body, :root에 height: 100%를 적용하면 자식 요소들이 부모의 전체 높이를 참조할 수 있게 됩니다. 이를 통해 풀 스크린 레이아웃이나 sticky footer 같은 레이아웃 패턴을 쉽게 구현할 수 있습니다.

코드 품질 자동화: Prettier, ESLint
이번 섹션에서는 코드 스타일과 품질을 자동으로 관리하는 필수 도구들을 설정합니다. Prettier로 일관된 코드 포맷팅을 적용하고, ESLint로 잠재적 문제를 사전에 발견하여 깔끔하고 안정적인 코드베이스를 유지합니다.
function getUserData() {
const name = "John" // ⚠️ 'name' is assigned but never used
var age = 25 // ❌ Unexpected var, use let or const
let userStatus = "active" // ⚠️ 'userStatus' is never reassigned. Use 'const'
console.log("Getting user") // ⚠️ Unexpected console statement
const data = fetchData() // ❌ 'fetchData' is not defined
return data
}
// 미사용 import
import { useState, useEffect } from "react" // ⚠️ 'useEffect' is defined but never used
const Component = () => {
const [count, setCount] = useState(0)
// any 타입 사용
const handleClick = (e: any) => { // ⚠️ Unexpected any. Specify a different type
setCount(count + 1)
}
}1. Prettier 및 Import 정렬 플러그인 설치
코드 포맷팅을 자동화하고 import 문을 체계적으로 정렬하기 위해 Prettier와 관련 플러그인들을 설치합니다.
pnpm add -D prettier prettier-plugin-tailwindcss @trivago/prettier-plugin-sort-imports설치할 패키지들
- prettier: 코드 포맷터 핵심 패키지
- prettier-plugin-tailwindcss: Tailwind CSS 클래스 자동 정렬
- @trivago/prettier-plugin-sort-imports: import 문 자동 정렬 및 그룹화
플러그인 기능
- Tailwind 클래스를 권장 순서대로 자동 정렬 (레이아웃 → 스타일 → 효과)
- import 문을 논리적 그룹으로 분류하고 알파벳순 정렬
- 불필요한 import 정리
prettier 설정 파일을 생성하고 아래 내용을 추가합니다.
module.exports = {
// 플러그인 설정
plugins: [
"@trivago/prettier-plugin-sort-imports", // import 문 정렬
"prettier-plugin-tailwindcss", // Tailwind 클래스 정렬
],
// 기본 포맷팅 규칙
bracketSpacing: true, // 객체 중괄호 내 공백 { foo: bar }
semi: false, // 세미콜론 미사용
singleQuote: false, // 큰따옴표 사용
tabWidth: 2, // 들여쓰기 2칸
trailingComma: "es5", // ES5 호환 trailing comma
// Import 정렬 규칙
importOrder: [
"^react$", // 1. React를 최상단에
"<THIRD_PARTY_MODULES>", // 2. 외부 라이브러리
"^[@]+/", // 3. @로 시작하는 alias 경로
"^[.]+/", // 4. 상대 경로 import
],
importOrderSeparation: true, // 그룹 간 빈 줄 추가
importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"],
}// Before
import "./globals.css"
import { useState } from "react"
import Link from "next/link"
import React from "react"
import { Button } from "@/components/ui/button"
// After (자동 정렬됨)
import React from "react"
import Link from "next/link"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import "./globals.css"build: prettier 및 import 정렬 플러그인 설정
Prettier와 관련 플러그인들을 설치하고 설정 파일을 추가했습니다.
- prettier-plugin-tailwindcss: Tailwind 클래스 자동 정렬
- @trivago/prettier-plugin-sort-imports: import 문 그룹화 및 정렬
- 세미콜론 미사용, 큰따옴표 사용 등 코드 스타일 규칙 정의
모든 소스 파일에 포맷팅을 적용하여 일관된 코드 스타일을 확보했습니다.4. ESLint와 Prettier 호환성 설정
ESLint와 Prettier가 충돌 없이 함께 작동하도록 설정합니다. Next.js는 기본적으로 ESLint가 설치되어 있으므로, Prettier와의 호환성만 추가하면 됩니다.
역할 분담
- ESLint: 코드 품질 검사 (버그 가능성, 미사용 변수 등)
- Prettier: 코드 포맷팅 (들여쓰기, 따옴표 등)
충돌 예시
- ESLint: "세미콜론을 써야 함"
- Prettier: "세미콜론 안 씀" → eslint-config-prettier가 ESLint의 포맷팅 규칙을 끔
pnpm add -D eslint-config-prettierimport { FlatCompat } from "@eslint/eslintrc"
import { dirname } from "path"
import { fileURLToPath } from "url"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
})
const eslintConfig = [
...compat.extends(
"next/core-web-vitals", // Next.js 권장 규칙 (이미 eslint-config-next 포함)
"next/typescript", // TypeScript 지원
"prettier" // Prettier 충돌 방지 (마지막에!)
),
// 커스텀 규칙 추가 가능
...compat.config({
rules: {
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_", // _로 시작하는 매개변수는 무시
varsIgnorePattern: "^_", // _로 시작하는 변수는 무시
},
],
},
}),
]
export default eslintConfig
build: ESLint와 Prettier 호환성 설정 추가
ESLint 설정을 확장하여 코드 품질 검사를 강화하고 Prettier와의 충돌을 방지했습니다.
- eslint-config-prettier 패키지 추가로 포맷팅 규칙 충돌 해결
- TypeScript 미사용 변수 규칙 설정 (_prefix 변수 무시)
- console 메서드 제한 규칙 추가 (warn, error, info는 허용)
- prefer-const, no-var 등 코드 품질 규칙 적용
- lint, lint:fix, format, check 스크립트 추가
이제 ESLint는 코드 품질을, Prettier는 코드 포맷팅을 담당하여 역할이 명확히 분리됩니다.1. Prettier 및 Import 정렬 플러그인 설치
코드 포맷팅을 자동화하고 import 문을 체계적으로 정렬하기 위해 Prettier와 관련 플러그인들을 설치합니다.
pnpm add -D prettier prettier-plugin-tailwindcss @trivago/prettier-plugin-sort-imports설치할 패키지들
- prettier: 코드 포맷터 핵심 패키지
- prettier-plugin-tailwindcss: Tailwind CSS 클래스 자동 정렬
- @trivago/prettier-plugin-sort-imports: import 문 자동 정렬 및 그룹화
플러그인 기능
- Tailwind 클래스를 권장 순서대로 자동 정렬 (레이아웃 → 스타일 → 효과)
- import 문을 논리적 그룹으로 분류하고 알파벳순 정렬
- 불필요한 import 정리
4. ESLint와 Prettier 호환성 설정
ESLint와 Prettier가 충돌 없이 함께 작동하도록 설정합니다. Next.js는 기본적으로 ESLint가 설치되어 있으므로, Prettier와의 호환성만 추가하면 됩니다.
역할 분담
- ESLint: 코드 품질 검사 (버그 가능성, 미사용 변수 등)
- Prettier: 코드 포맷팅 (들여쓰기, 따옴표 등)
충돌 예시
- ESLint: "세미콜론을 써야 함"
- Prettier: "세미콜론 안 씀" → eslint-config-prettier가 ESLint의 포맷팅 규칙을 끔
pnpm add -D eslint-config-prettierprettier 설정 파일을 생성하고 아래 내용을 추가합니다.
module.exports = {
// 플러그인 설정
plugins: [
"@trivago/prettier-plugin-sort-imports", // import 문 정렬
"prettier-plugin-tailwindcss", // Tailwind 클래스 정렬
],
// 기본 포맷팅 규칙
bracketSpacing: true, // 객체 중괄호 내 공백 { foo: bar }
semi: false, // 세미콜론 미사용
singleQuote: false, // 큰따옴표 사용
tabWidth: 2, // 들여쓰기 2칸
trailingComma: "es5", // ES5 호환 trailing comma
// Import 정렬 규칙
importOrder: [
"^react$", // 1. React를 최상단에
"<THIRD_PARTY_MODULES>", // 2. 외부 라이브러리
"^[@]+/", // 3. @로 시작하는 alias 경로
"^[.]+/", // 4. 상대 경로 import
],
importOrderSeparation: true, // 그룹 간 빈 줄 추가
importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"],
}husky 적용
Husky는 Git commit, push 시 자동으로 스크립트를 실행해 코드 품질과 규칙을 유지하게 해주는 도구입니다.
이 섹션에서는 커밋 전 코드 포맷 검사, 푸시 전 빌드 테스트, 커밋 메시지 규칙 검사를 설정합니다.
commit 메세지 작성
↓
Husky 훅 호출
↓
lint-staged / commitlint 실행
↓
(push 시) 빌드 테스트 실행
↓
문제 없으면 커밋/푸시 진행
↓
1차 검증된 코드가 공유 코드로 이동# 1. 패키지 설치
pnpm i -D husky lint-staged @commitlint/cli
# 2. Husky 초기화
npx husky init
# 3. pre-commit 훅 설정 - commit 실행 전 staged 파일들 lint 검사
echo "pnpm lint-staged" > .husky/pre-commit
# pre-push 훅 추가 - push 실행 전 build 테스트
echo "pnpm build" > .husky/pre-push
# 4. commit-msg 훅 생성 - 커밋 메시지 규칙 검사
echo "pnpm commitlint --edit \$1" > .husky/commit-msg3. Git 메시지 템플릿 자동 등록 및 lint-staged 설정
package.json에 설정을 추가하여 프로젝트 설치 시 자동으로 Git 템플릿이 등록되도록 하고, lint-staged의 동작을 정의합니다.
-
Git commit message 자동 등록
- 프로젝트 루트에 만든
.gitmessage템플릿을 postinstall 스크립트로 자동 등록 - 프로젝트를 설치하면 Git이 자동으로 커밋 메시지 템플릿을 사용하도록 구성됩니다.
- 프로젝트 루트에 만든
-
lint-staged 설정
- 커밋할 때 staged된 파일만 대상으로 Prettier를 실행
- 불필요한 전체 파일 포맷팅을 방지하고, 커밋 속도를 유지
{
"scripts": {
...
"postinstall": "husky && git config commit.template ./.gitmessage"
},
"devDependencies": {
...
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
}module.exports = {
rules: {
// 제목(subject) 규칙
"subject-empty": [2, "never"], // 제목은 반드시 존재해야 함
"subject-full-stop": [2, "never", "."], // 제목 끝에 마침표(.) 사용 금지
// 타입(type) 규칙
"type-enum": [ // 허용되는 커밋 타입 목록
2,
"always",
[
"feat", // 새로운 기능
"fix", // 버그 수정
"docs", // 문서 수정
"style", // 코드 스타일 (포맷팅, 세미콜론 등)
"refactor", // 코드 리팩토링
"test", // 테스트 추가/수정
"build", // 빌드 시스템/의존성
"ci", // CI 설정
"perf", // 성능 개선
"chore" // 기타 변경사항
]
],
"type-empty": [2, "never"], // 타입 필수 입력
"type-case": [2, "always", "lower-case"], // 타입은 소문자만
// 본문(body) 규칙
"body-empty": [0, "never"], // 본문은 선택사항 (0 = 비활성)
"body-leading-blank": [2, "always"], // 제목과 본문 사이 빈 줄 필수
// 푸터(footer) 규칙
"footer-leading-blank": [2, "always"], // 본문과 푸터 사이 빈 줄 필수
},
};5. Husky 테스트 및 GitHub Push
설정한 Git hooks가 정상적으로 작동하는지 테스트하고 GitHub에 push합니다. 각 단계에서 자동화된 검사가 실행되는 것을 확인할 수 있습니다.
테스트 과정
- git add: 변경사항 스테이징
- git commit: pre-commit hook 실행 (lint-staged)
- git push: pre-push hook 실행 (build 테스트)
확인 사항
- 커밋 시 자동 포맷팅 적용
- 커밋 메시지 규칙 검증
- 푸시 전 빌드 성공 확인
git add .
git commit
git push origin developgit push origin
> boilerplate-nextjs-2508@0.1.0 build /Users/mech2cs/work/mech2cs/boilerplate-nextjs-2508
> next build
▲ Next.js 15.4.6
Creating an optimized production build ...
✓ Compiled successfully in 1000ms
✓ Linting and checking validity of types
✓ Collecting page data
✓ Generating static pages (5/5)
✓ Collecting build traces
✓ Finalizing page optimization
Route (app) Size First Load JS
┌ ○ / 121 B 100 kB
└ ○ /_not-found 991 B 101 kB
+ First Load JS shared by all 99.8 kB
├ chunks/540-d81d811e227e740f.js 43.8 kB
├ chunks/97be0265-f694bf0abffa3d2c.js 54.1 kB
└ other shared chunks (total) 1.89 kB
○ (Static) prerendered as static content
Enumerating objects: 27, done.
Counting objects: 100% (27/27), done.
Delta compression using up to 12 threads
Compressing objects: 100% (15/15), done.
Writing objects: 100% (19/19), 27.42 KiB | 9.14 MiB/s, done.
Total 19 (delta 8), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (8/8), completed with 5 local objects.
To github.com:sijunnoh/boilerplate-nextjs-2508.git
726eab4..4f7ee5d develop -> develop3. Git 메시지 템플릿 자동 등록 및 lint-staged 설정
package.json에 설정을 추가하여 프로젝트 설치 시 자동으로 Git 템플릿이 등록되도록 하고, lint-staged의 동작을 정의합니다.
-
Git commit message 자동 등록
- 프로젝트 루트에 만든
.gitmessage템플릿을 postinstall 스크립트로 자동 등록 - 프로젝트를 설치하면 Git이 자동으로 커밋 메시지 템플릿을 사용하도록 구성됩니다.
- 프로젝트 루트에 만든
-
lint-staged 설정
- 커밋할 때 staged된 파일만 대상으로 Prettier를 실행
- 불필요한 전체 파일 포맷팅을 방지하고, 커밋 속도를 유지
5. Husky 테스트 및 GitHub Push
설정한 Git hooks가 정상적으로 작동하는지 테스트하고 GitHub에 push합니다. 각 단계에서 자동화된 검사가 실행되는 것을 확인할 수 있습니다.
테스트 과정
- git add: 변경사항 스테이징
- git commit: pre-commit hook 실행 (lint-staged)
- git push: pre-push hook 실행 (build 테스트)
확인 사항
- 커밋 시 자동 포맷팅 적용
- 커밋 메시지 규칙 검증
- 푸시 전 빌드 성공 확인
git add .
git commit
git push origin develop# 1. 패키지 설치
pnpm i -D husky lint-staged @commitlint/cli
# 2. Husky 초기화
npx husky init
# 3. pre-commit 훅 설정 - commit 실행 전 staged 파일들 lint 검사
echo "pnpm lint-staged" > .husky/pre-commit
# pre-push 훅 추가 - push 실행 전 build 테스트
echo "pnpm build" > .husky/pre-push
# 4. commit-msg 훅 생성 - 커밋 메시지 규칙 검사
echo "pnpm commitlint --edit \$1" > .husky/commit-msgshadcn/ui - 복사하여 사용하는 컴포넌트 시스템
shadcn/ui는 일반적인 컴포넌트 라이브러리와 다른 독특한 접근 방식을 제공합니다. npm 패키지로 설치하는 대신, 필요한 컴포넌트의 소스 코드를 직접 프로젝트에 복사하여 사용합니다.
shadcn/ui의 해결책:
- 완전한 소유권: 컴포넌트 코드가 내 프로젝트에 직접 존재
- 자유로운 커스터마이징: 소스 코드를 직접 수정 가능
- 선택적 설치: 필요한 컴포넌트만 골라서 추가
- 의존성 최소화: 외부 라이브러리 의존도가 낮음

✔ Which components would you like to add? › button
✔ You need to create a components.json file to add components. Proceed? … yes
✔ Which color would you like to use as the base color? › Neutral
✔ Writing components.json.
✔ Checking registry.
✔ Installing dependencies.
✔ Created 1 file:
- src/components/ui/button.tsx"use client"
import { Button } from "@/components/ui/button"
export default function Home() {
return (
<div className="flex flex-1 flex-col items-center justify-center gap-3 text-5xl">
<Button onClick={() => alert("Hello World!")}>Hello World!</Button>
<Button variant="ghost" onClick={() => alert("Hello World!")}>
Hello World!
</Button>
<Button variant="destructive" onClick={() => alert("Hello World!")}>
Hello World!
</Button>
<Button size="lg" onClick={() => alert("Hello World!")}>
Hello World!
</Button>
</div>
)
}...
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
button:not([disabled]),
[role="button"]:not([disabled]) {
cursor: pointer;
}
}
{
...
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prepare": "husky",
"ui": "npx shadcn@latest",
"postinstall": "husky && git config commit.template ./.gitmessage"
},
...
}
feat: shadcn/ui 설정 및 Button 컴포넌트 추가
재사용 가능한 UI 컴포넌트 시스템인 shadcn/ui를 프로젝트에 통합했습니다.
- shadcn/ui 초기 설정 (components.json, tailwind.config.ts 업데이트)
- Button 컴포넌트 및 관련 유틸리티 추가
- Radix UI 기반 접근성 보장
- 버튼 커서 포인터 스타일 글로벌 CSS 추가
- package.json에 ui 관련 스크립트 추가 (pnpm ui add)
컴포넌트는 src/components/ui에 소스 코드 형태로 관리되어 자유로운 커스터마이징이 가능합니다.✔ Which components would you like to add? › button
✔ You need to create a components.json file to add components. Proceed? … yes
✔ Which color would you like to use as the base color? › Neutral
✔ Writing components.json.
✔ Checking registry.
✔ Installing dependencies.
✔ Created 1 file:
- src/components/ui/button.tsxWrapper 클래스 - 안전하고 재사용 가능한 구조
프로젝트에서 특정 라이브러리나 기능을 직접 사용하면,
추후 유지보수가 어렵거나 브라우저 환경에서 발생할 수 있는 에러를 방지하기 어렵습니다.
이런 단점을 보완하기 위해 특정 기능을 중앙화하여 관리하는 것이 좋습니다.
이번 섹션에서는 logger, localStorage, sessionStorage 등을 wrapper 클래스로 구현하고, ESLint와 조합하여 휴먼 에러를 최소화하는 방법을 다룹니다.
Wrapper 클래스의 장점
- 안전성 확보: 브라우저 환경에서만 접근하도록 처리
- 재사용성 향상: 공통 로직을 한 곳에서 관리
- 일관된 인터페이스: localStorage, sessionStorage, logger 모두 동일한 방식으로 사용
- 추후 확장 용이: 예를 들어 Sentry 연동, 로깅 포맷 변경 등을 한 곳에서 적용

class Logger {
debug(message: string, ...args: unknown[]): void {
console.log(`[DEBUG]`, message, ...args)
}
info(message: string, ...args: unknown[]): void {
console.log(`[INFO]`, message, ...args)
}
warn(message: string, ...args: unknown[]): void {
console.log(`[WARN]`, message, ...args)
}
error(message: string | Error, ...args: unknown[]): void {
console.log(`[ERROR]`, message, ...args)
}
}
export const logger = new Logger()
class LocalStorage { private _isAvailable: boolean constructor() { this._isAvailable = this.checkAvailability() } private checkAvailability(): boolean { try { if (typeof window === 'undefined' || !window.localStorage) { return false } const testKey = '__local_storage_test__' window.localStorage.setItem(testKey, 'test') window.localStorage.removeItem(testKey) return true } catch { return false } } setItem(key: string, value: unknown): void { if (this._isAvailable) { window.localStorage.setItem(key, JSON.stringify(value)) } } getItem<T>(key: string): T | null { if (this._isAvailable) { const item = window.localStorage.getItem(key) return item ? JSON.parse(item) : null } return null } removeItem(key: string): void { if (this._isAvailable) { window.localStorage.removeItem(key) } } clear(): void { if (this._isAvailable) { window.localStorage.clear() } } get available(): boolean { return this._isAvailable } } export const localStorage = new LocalStorage()
class LocalStorage {
private _isAvailable: boolean
constructor() {
this._isAvailable = this.checkAvailability()
}
private checkAvailability(): boolean {
...
}
setItem(key: string, value: unknown): void {
...
}
getItem<T>(key: string): T | null {
...
}
removeItem(key: string): void {
...
}
clear(): void {
...
}
get available(): boolean {
return this._isAvailable
}
}
export const localStorage = new LocalStorage()class SessionStorage { private _isAvailable: boolean constructor() { this._isAvailable = this.checkAvailability() } private checkAvailability(): boolean { try { if (typeof window === 'undefined' || !window.sessionStorage) { return false } const testKey = '__session_storage_test__' window.sessionStorage.setItem(testKey, 'test') window.sessionStorage.removeItem(testKey) return true } catch { return false } } setItem(key: string, value: unknown): void { if (this._isAvailable) { window.sessionStorage.setItem(key, JSON.stringify(value)) } } getItem<T>(key: string): T | null { if (this._isAvailable) { const item = window.sessionStorage.getItem(key) return item ? JSON.parse(item) : null } return null } removeItem(key: string): void { if (this._isAvailable) { window.sessionStorage.removeItem(key) } } clear(): void { if (this._isAvailable) { window.sessionStorage.clear() } } get available(): boolean { return this._isAvailable } } export const sessionStorage = new SessionStorage()
class SessionStorage {
private _isAvailable: boolean
constructor() {
this._isAvailable = this.checkAvailability()
}
private checkAvailability(): boolean {
...
}
setItem(key: string, value: unknown): void {
...
}
getItem<T>(key: string): T | null {
...
}
removeItem(key: string): void {
...
}
clear(): void {
...
}
get available(): boolean {
return this._isAvailable
}
}
export const sessionStorage = new SessionStorage()4. Eslint로 휴먼에러 최소화
Wrapper 클래스를 활용해 console, localStorage, sessionStorage에 대한 직접 접근을 금지하고, 프로젝트 전반에서 안전하게 사용하도록 Eslint 규칙을 적용합니다.
적용할 규칙
- no-console:
console직접 호출 금지 → logger 사용 유도 - no-restricted-globals:
localStorage/sessionStorage직접 접근 금지 - 예외 처리:
src/core폴더는 Wrapper 구현부이므로 규칙 제외
기대 효과
- 프로덕션 코드에
console.log노출 방지 - SSR 환경에서 storage 에러 방지
- 일관된 로깅 포맷 유지
- 타입 안전성 보장
...
const eslintConfig = [
...
...compat.config({
rules: {
...
// console 직접 호출 금지
"no-console": "error",
// localStorage, sessionStorage 직접 호출 금지
"no-restricted-globals": [
"error",
{
name: "localStorage",
message: "Use localStorageWrapper from '@/services/localStorage' instead of direct localStorage access.",
},
{
name: "sessionStorage",
message: "Use sessionStorageWrapper from '@/services/sessionStorage' instead of direct sessionStorage access.",
},
],
},
}),
// services 폴더 내에서는 예외 허용
{
files: ["src/services/**/*.ts"],
rules: {
"no-console": "off",
"no-restricted-globals": "off",
},
},
]
export default eslintConfig
5. Wrapper Class 관련 커밋 및 GitHub Push
Logger와 Storage Wrapper 구현이 완료되었으니 커밋하고 GitHub에 push합니다.
커밋 전 확인사항
- Logger 클래스 구현 (src/core/logger/index.ts)
- LocalStorage Wrapper 구현 (src/core/storage/local-storage.ts)
- SessionStorage Wrapper 구현 (src/core/storage/session-storage.ts)
- ESLint 규칙 추가 (console, storage 직접 사용 금지)
- 테스트 및 동작 확인
git add .
git commit
git push origin developfeat: Logger 및 Storage Wrapper 클래스 추가
- Logger: 환경별 로그 제어, 컬러 출력, 타임스탬프
- Storage: SSR 안전, 타입 체크, JSON 자동 처리
- ESLint 규칙으로 직접 사용 금지, Wrapper 사용 강제4. Eslint로 휴먼에러 최소화
Wrapper 클래스를 활용해 console, localStorage, sessionStorage에 대한 직접 접근을 금지하고, 프로젝트 전반에서 안전하게 사용하도록 Eslint 규칙을 적용합니다.
적용할 규칙
- no-console:
console직접 호출 금지 → logger 사용 유도 - no-restricted-globals:
localStorage/sessionStorage직접 접근 금지 - 예외 처리:
src/core폴더는 Wrapper 구현부이므로 규칙 제외
기대 효과
- 프로덕션 코드에
console.log노출 방지 - SSR 환경에서 storage 에러 방지
- 일관된 로깅 포맷 유지
- 타입 안전성 보장
5. Wrapper Class 관련 커밋 및 GitHub Push
Logger와 Storage Wrapper 구현이 완료되었으니 커밋하고 GitHub에 push합니다.
커밋 전 확인사항
- Logger 클래스 구현 (src/core/logger/index.ts)
- LocalStorage Wrapper 구현 (src/core/storage/local-storage.ts)
- SessionStorage Wrapper 구현 (src/core/storage/session-storage.ts)
- ESLint 규칙 추가 (console, storage 직접 사용 금지)
- 테스트 및 동작 확인
git add .
git commit
git push origin developclass Logger {
debug(message: string, ...args: unknown[]): void {
console.log(`[DEBUG]`, message, ...args)
}
info(message: string, ...args: unknown[]): void {
console.log(`[INFO]`, message, ...args)
}
warn(message: string, ...args: unknown[]): void {
console.log(`[WARN]`, message, ...args)
}
error(message: string | Error, ...args: unknown[]): void {
console.log(`[ERROR]`, message, ...args)
}
}
export const logger = new Logger()
마치며
이번 튜토리얼에서는 Next.js 15를 기반으로 한 완전한 보일러플레이트 프로젝트를 구축하는 과정을 다뤘습니다.
완성된 코드는 GITHUB 저장소를 참조해주세요.
구축한 주요 기능들
- 프로젝트 기초: Next.js 15 + TypeScript + Tailwind CSS
- 코드 품질 자동화: Prettier, ESLint, lint-staged로 일관된 코드 스타일 유지
- Git Hook 자동화: Husky를 통한 커밋 전 검증과 빌드 테스트
- 컴포넌트 시스템: shadcn/ui로 확장 가능한 UI 컴포넌트 기반 구축
- Wrapper 클래스: Logger, Storage를 안전하고 일관되게 사용할 수 있는 추상화 계층
참조자료
공식 문서
- Next.js 공식 문서 - App Router, TypeScript, 최적화 가이드
- Tailwind CSS 문서 - 유틸리티 클래스와 커스터마이징
- TypeScript 핸드북 - TypeScript 기본 문법과 설정
코드 품질 도구
- Prettier 설정 가이드 - 코드 포맷팅 규칙
- ESLint 규칙 레퍼런스 - 린팅 규칙과 커스터마이징
- Husky 문서 - Git Hook 자동화
UI 라이브러리
- shadcn/ui - 복사 가능한 컴포넌트 라이브러리