본문 바로가기

AI Development

매번 설명 반복하다 지쳤다면 — Claude Code 4레이어 컨텍스트 시스템

반응형

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

기존에 직접 하던 방식:

  1. git diff 직접 확인 (3분)
  2. PR 본문 직접 작성 (5분)
  3. 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 자동 생성

 

반응형