Claude Code를 처음 쓰면 이런 상황이 반복돼요.
세션 1: "우리 프로젝트는 TypeScript 쓰고, Fastify야, 테스트는 Vitest야"
세션 2: 또 설명
세션 3: 또 설명
매번 새 Claude한테 온보딩을 반복하는 거예요. 이게 진짜 시간 낭비예요.
4레이어 시스템을 구축하면 Claude가 세션 시작과 동시에 프로젝트 전문가 상태로 시작해요.
레이어 1: CLAUDE.md → 프로젝트 메모리
레이어 2: 슬래시 커맨드 → 반복 작업 자동화
레이어 3: Skills → 도메인 전문가 주입
레이어 4: MCP 연동 → 실제 워크플로우 연결
레이어 1 — CLAUDE.md
파일 위치
프로젝트 루트/
├── CLAUDE.md ← 전체 프로젝트 규칙
├── src/
│ └── CLAUDE.md ← src 전용 추가 규칙 (선택)
└── tests/
└── CLAUDE.md ← 테스트 전용 규칙 (선택)
Claude Code는 세션 시작 시 현재 디렉토리의 CLAUDE.md를 자동으로 읽어요. 하위 디렉토리 CLAUDE.md는 해당 폴더 작업 시 추가로 읽어요.
나쁜 예 vs 좋은 예
나쁜 예 — 이렇게 쓰면 소용없어요
# 프로젝트
TypeScript 써. 테스트 써. 좋은 코드 써.
결과: Claude가 TypeScript는 쓰지만 어떤 스타일인지, 어떤 테스트 프레임워크인지 모르고 추측해서 코드를 써요.
좋은 예 — 실제 프로젝트에 쓰는 형태
# 프로젝트: 커머스 백엔드 API
## 스택
- Runtime: Node.js 22, TypeScript 5.4 (strict mode)
- Framework: Fastify 5 (Express 절대 쓰지 말 것)
- ORM: Prisma 6 + PostgreSQL 16
- Test: Vitest 2 (Jest 아님)
- 패키지 매니저: pnpm 9 (npm, yarn 절대 쓰지 말 것)
- 유효성 검사: Zod
## 아키텍처 규칙
레이어 구조 (이 방향으로만 의존):
Route → Service → Repository → DB
- /src/routes: 라우터만, 비즈니스 로직 없음
- /src/services: 비즈니스 로직, DB 직접 접근 금지
- /src/repositories: Prisma 쿼리만
- /src/schemas: Zod 스키마 정의
## 코딩 컨벤션
- 함수: 20줄 이하
- 모든 async 함수: try-catch 필수
- 타입: any 금지 → unknown 사용
- 로깅: console.log 금지 → logger.info/error 사용
- 에러: throw new AppError('메시지', statusCode) 형태
## 명령어
- 개발 서버: pnpm dev
- 테스트 전체: pnpm test
- 테스트 특정 파일: pnpm test src/services/payment.test.ts
- 빌드: pnpm build
- DB 마이그레이션: pnpm prisma migrate dev
- 타입 체크: pnpm typecheck
## 절대 수정 금지
- prisma/migrations/ (마이그레이션 파일들)
- .env.production
- src/config/constants.ts
## 새 기능 만들 때 순서
1. Repository 먼저
2. Service 구현
3. Route 연결
4. 테스트 작성 (이 순서 꼭 지킬 것)
실제 효과 비교
CLAUDE.md 없이 요청:
나: "유저 목록 조회 API 만들어줘"
Claude 결과:
import express from 'express' ← Fastify 써야 하는데 Express 씀
const router = express.Router()
router.get('/users', async (req, res) => {
const users = await prisma.user.findMany() ← N+1 위험
console.log('users fetched') ← console.log 쓰면 안 됨
res.json(users)
})
CLAUDE.md 있을 때:
나: "유저 목록 조회 API 만들어줘"
Claude 결과:
// src/repositories/user.repository.ts
export class UserRepository {
async findAll(page: number, limit: number) {
return prisma.user.findMany({
take: limit,
skip: (page - 1) * limit,
select: { id: true, email: true, createdAt: true }
// 비밀번호 필드 자동 제외
})
}
}
// src/services/user.service.ts
export class UserService {
constructor(private repo: UserRepository) {}
async getUsers(page: number, limit: number) {
try {
return await this.repo.findAll(page, limit)
} catch (error) {
logger.error('유저 목록 조회 실패', { error })
throw new AppError('유저 목록 조회 실패', 500)
}
}
}
// 테스트도 자동으로 작성
스택, 아키텍처, 컨벤션을 다 지킨 코드가 처음부터 나와요.
레이어 2 — 슬래시 커맨드
반복하는 작업이 있으면 커맨드로 만들어요.
파일 구조
.claude/
└── commands/
├── pr.md
├── review.md
└── feature.md
PR 생성 커맨드
.claude/commands/pr.md:
현재 브랜치 변경사항을 분석하고 PR을 만들어줘.
실행 순서:
1. `git diff main --stat` 으로 변경 파일 파악
2. `git diff main` 으로 실제 변경 내용 분석
3. 아래 형식으로 PR 본문 작성:
---
## 변경 이유
(왜 이 변경이 필요했는지)
## 주요 변경사항
- 변경 1
- 변경 2
## 테스트 방법
(어떻게 테스트했는지, 어떻게 테스트해야 하는지)
## 체크리스트
- [ ] 테스트 통과
- [ ] 타입 에러 없음
- [ ] CLAUDE.md 규칙 준수
---
4. `gh pr create --title "제목" --body "본문"` 실행
사용 예시:
# PR 올리기 전
/pr
# Claude 실행 결과:
# git diff main --stat 실행 중...
# 변경된 파일: src/services/payment.ts, src/routes/payment.ts, tests/payment.test.ts
# PR 제목: feat: 결제 취소 API 추가
# PR 본문 작성 완료
# gh pr create 실행 중...
# PR 생성 완료: https://github.com/yourrepo/pull/47
기존에 직접 하던 방식:
- git diff 직접 확인 (3분)
- PR 본문 직접 작성 (5분)
- gh 명령어 직접 실행 (2분)
→ 총 10분 → /pr 한 번으로 30초
코드 리뷰 커맨드
.claude/commands/review.md:
$ARGUMENTS 파일을 코드 리뷰해줘. 인자 없으면 현재 변경사항 전체 리뷰.
검토 항목:
1. 버그 가능성 (null 체크 누락, 타입 오류, edge case)
2. 보안 취약점 (인증 누락, SQL 인젝션, 민감 정보 노출)
3. 성능 문제 (N+1 쿼리, 불필요한 루프, 메모리 누수)
4. 아키텍처 위반 (레이어 규칙, 의존성 방향)
5. 테스트 누락
출력 형식:
🚨 Critical: 반드시 수정 (배포 전)
⚠️ Warning: 수정 권장
💡 Suggestion: 선택사항
✅ Good: 잘 된 부분
각 항목에 파일명:줄번호 명시.
사용 예시:
/review src/services/payment.ts
# Claude 출력:
# 🚨 Critical (2개)
# - payment.ts:34 - userId null 체크 없음. undefined 전달 시 DB 에러
# 수정: if (!userId) throw new AppError('userId 필수', 400)
# - payment.ts:67 - 결제 실패 시 재고 롤백 로직 없음. 트랜잭션 필요
#
# ⚠️ Warning (1개)
# - payment.ts:89 - N+1 쿼리 발생. orders 루프 안에서 user 조회 중
# 수정: include: { user: true } 추가
#
# ✅ Good
# - 에러 처리 일관성 좋음
# - Zod 스키마 검증 잘 됨
기능 개발 커맨드
.claude/commands/feature.md:
$ARGUMENTS 기능을 구현해줘.
반드시 이 순서로 진행:
1. 구현 계획 먼저 작성 (코드 작성 전)
2. "계획 검토해줘" 메시지 출력하고 대기
3. 내 승인 받은 후에만 구현 시작
4. Repository → Service → Route → Test 순서로 구현
5. 각 단계 완료 후 보고
계획 형식:
- 영향받는 파일 목록
- 새로 만들 파일 목록
- DB 스키마 변경 필요 여부
- 예상 소요 시간
사용 예시:
/feature 쿠폰 적용 결제 API
# Claude 출력:
# ## 구현 계획
#
# **영향받는 파일:**
# - src/repositories/coupon.repository.ts (신규)
# - src/services/payment.service.ts (수정: 쿠폰 할인 로직 추가)
# - src/routes/payment.ts (수정: couponCode 파라미터 추가)
#
# **새로 만들 파일:**
# - src/repositories/coupon.repository.ts
# - tests/services/payment.test.ts (기존에 추가)
#
# **DB 변경:** Coupon 테이블 신규 (마이그레이션 필요)
#
# 계획 검토해줘. 승인하면 구현 시작할게요.
나: ㅇㅋ 진행해
# Claude: Repository부터 시작할게요...
계획 없이 바로 구현하면 30% 확률로 방향이 틀려요. 계획 검토 한 번으로 그 낭비를 막아요.
레이어 3 — Skills
특정 도메인에서 Claude가 더 잘 하게 만드는 파일이에요.
파일 구조
.claude/
└── skills/
├── database.md
├── security.md
└── api-design.md
DB 최적화 Skill
.claude/skills/database.md:
# DB 작업 가이드
## 반드시 피해야 할 패턴
### N+1 쿼리 (절대 금지)
```typescript
// ❌ 이렇게 쓰면 유저 100명 → DB 쿼리 101번
const users = await prisma.user.findMany()
for (const user of users) {
user.posts = await prisma.post.findMany({ where: { userId: user.id } })
}
// ✅ 이렇게 써야 함 → DB 쿼리 1번
const users = await prisma.user.findMany({
include: { posts: true }
})
페이지네이션 없는 전체 조회 (금지)
// ❌ 데이터 100만 건이면 서버 뻗음
const all = await prisma.order.findMany()
// ✅ 항상 페이지네이션
const orders = await prisma.order.findMany({
take: 20,
skip: (page - 1) * 20,
orderBy: { createdAt: 'desc' }
})
트랜잭션 써야 하는 경우
- 여러 테이블 동시 수정
- 결제 처리
- 재고 차감
// ✅ 트랜잭션 예시
const result = await prisma.$transaction(async (tx) => {
const order = await tx.order.create({ data: orderData })
await tx.inventory.update({
where: { productId: orderData.productId },
data: { quantity: { decrement: orderData.quantity } }
})
return order
})
CLAUDE.md에 이걸 추가해요:
```markdown
## Skills 참조
- DB 관련 작업 시: .claude/skills/database.md 참고
- 보안 관련 작업 시: .claude/skills/security.md 참고
효과: DB 코드 요청할 때마다 N+1, 트랜잭션 자동으로 고려해서 코드 써요.
레이어 4 — MCP 연동
자주 쓰는 서비스를 Claude에 직접 연결해요.
GitHub 연동
claude mcp add github npx @anthropic/mcp-github
연동 전:
나: "main 브랜치 최근 이슈 목록 알려줘"
Claude: "GitHub에 직접 확인해보세요."
연동 후:
나: "main 브랜치 최근 이슈 목록 알려줘"
Claude: GitHub API 호출 →
"현재 열린 이슈:
#47 결제 실패 시 에러 메시지 불명확 (2일 전)
#45 모바일에서 로그인 버튼 안 보임 (3일 전)
#43 쿠폰 중복 적용 가능 버그 (5일 전)"
나: "#43 수정해줘"
Claude: 이슈 읽기 → 코드 분석 → 수정 → PR 생성까지 자동
DB 직접 조회
claude mcp add postgres npx @anthropic/mcp-postgres \
--connection-string "postgresql://user:pass@localhost/mydb"
연동 후:
나: "오늘 결제 실패 건수랑 이유 알려줘"
Claude: SQL 실행 →
"오늘 결제 실패: 47건
- 카드 한도 초과: 23건 (49%)
- 잘못된 카드번호: 15건 (32%)
- 네트워크 오류: 9건 (19%)
가장 많이 실패한 시간대: 오후 12-2시 (점심 시간)"
코드 짜다가 DB 확인하러 DBeaver 열 필요 없어요. 터미널에서 바로 물어보면 돼요.
최종 디렉토리 구조
프로젝트 루트/
├── CLAUDE.md ← 레이어 1: 프로젝트 메모리
├── .claude/
│ ├── commands/ ← 레이어 2: 반복 작업 자동화
│ │ ├── pr.md /pr
│ │ ├── review.md /review
│ │ └── feature.md /feature
│ └── skills/ ← 레이어 3: 전문 지식 주입
│ ├── database.md
│ ├── security.md
│ └── api-design.md
└── (MCP는 ~/.claude/config.json) ← 레이어 4: 서비스 연동
실제 하루 워크플로우 비교
4레이어 전 (하루 8시간):
9:00 Claude 세션 시작, 프로젝트 설명 (15분)
9:15 기능 구현 요청, 컨벤션 맞지 않아서 재요청 (30분)
11:00 PR 본문 직접 작성 (10분)
14:00 코드 리뷰 요청, 항목 하나하나 설명 (20분)
...
4레이어 후 (하루 8시간):
9:00 Claude 세션 시작 (CLAUDE.md 자동 로드, 0분)
9:00 /feature 쿠폰 결제 API
9:02 계획 검토 후 승인
9:30 구현 완료 (테스트 포함)
10:00 /review → 즉시 피드백
10:05 /pr → PR 자동 생성
'AI Development' 카테고리의 다른 글
| Git Worktrees + Claude Code — 혼자서 팀처럼 병렬 개발하는 법 (0) | 2026.04.13 |
|---|---|
| Claude Code가 스스로 에러 잡게 만드는 법 — Hooks 실전 설정 (0) | 2026.04.13 |
| AI 에이전트에 Shell Access 주면 안 되는 이유 — 실제 해킹 사례와 방어법 (0) | 2026.04.13 |
| Claude Advisor Strategy 실전 가이드 — Opus 성능을 Sonnet 비용으로 (0) | 2026.04.13 |
| 하네스 엔지니어링(Harness Engineering) 완전 정리 — AI가 좋은 코드를 짜게 만드는 법 (1) | 2026.04.10 |