
지난 글에서 Claude Code의 Custom Commands로 구현부터 PR까지 자동화한 이야기를 했어요.
오늘은 그중에서도 제 워크플로우의 심장 같은 존재, /spec 명령어를 깊게 파헤쳐보려고 합니다.
솔직히 AI 코딩 도구를 쓰면서 이런 답답함, 한 번쯤 느끼셨을 거예요.
/spec은 이 문제를 ‘명세서 기반 개발’로 정면 돌파합니다.
AI에게 “구현해줘”라고 말하는 대신,
결정을 명세서에 미리 담고/spec으로 실행한다.
이 한 줄이 생각보다 큰 차이를 만들더라고요.
/spec이 뭔가요?한 줄 요약은 이렇게 할게요.
명세서를 주면, Claude가 분석→계획→구현→검증→보고까지 “개발 프로세스”를 그대로 수행하는 명령어
/spec user-auth # specs/user-auth.md를 읽고 구현
그리고 여기서 가장 중요한 키워드가 하나 있습니다.
보통 AI에게 기능 구현을 부탁하면 이런 흐름이 반복되죠.
AI: "인증은 JWT로 할까요? 세션으로 할까요?"
나: "JWT요"
AI: "토큰 저장은 어디에 할까요?"
나: "AsyncStorage요"
AI: "에러 처리는 어떻게 할까요?"
나: (이미 지침)
/spec은 이 핑퐁을 줄이는 게 아니라, 애초에 필요 없게 만들어요.
/spec이 동작하는 핵심 원칙 4가지/spec은 “코드 생성”이라기보다, 코드를 만들기 위한 규칙 세트에 가까워요.
명세서가 충분히 상세하면 Claude는 추가 질문 없이 구현에 들어갑니다.
즉 명세서는 단순 문서가 아니라 실행 가능한 문서가 됩니다.
“일단 돌아가는 코드”가 아니라
까지 기본값으로 깔고 들어가요. 목표는 프로덕션에 올려도 부끄럽지 않은 코드입니다.
필수부터 먼저 완성하고, 시간이 남으면 권장/선택 기능으로 넘어갑니다.
이 구조 덕분에 “시간이 부족해서 다 못 했어요”여도 핵심 기능은 살아있어요.
테스트는 /create-jest, 문서는 /w-context로 분리했어요.
/spec은 구현과 검증에만 집중합니다.
/spec 실행 흐름: Phase 0 → Phase 6/spec이 “그냥 생성”이 아니라 “개발 프로세스”인 이유가 여기 있어요.
총 7단계를 항상 같은 순서로 밟습니다.
Phase 0: 명세서 로드
Phase 1: 명세서 분석 및 이해
Phase 2: 구현 전 검토 (질문 필요 시)
Phase 3: 구현 계획 수립
Phase 4: 코드 구현
Phase 5: 검증
Phase 6: 완료 보고
각 단계를 짧게(하지만 핵심은 빠짐없이) 살펴볼게요.
/spec # 인자 없이 실행하면 목록 표시
/spec user-auth # specs/user-auth.md 로드
파일이 없으면 이렇게 깔끔하게 실패합니다.
❌ specs/user-auth.md 파일을 찾을 수 없습니다.
사용 가능한 명세서: [목록]
이 단계가 품질을 결정합니다.
Claude가 바로 코딩부터 하지 않고, 명세서에서 필요한 정보를 구조적으로 뽑아내요.
| 섹션 | 뽑아내는 정보 |
|---|---|
| 개요 | 기능 목적, 해결하려는 문제 |
| 사용자 시나리오 | 플로우/유스케이스 |
| 기능 요구사항 | 🔴 필수 → 🟡 권장 → 🟢 선택 정렬 |
| 구현 위치 | 생성/수정할 파일 경로 |
| 기존 코드 활용 | 참고할 패턴/모듈 |
| 타입 정의 | 인터페이스/타입 |
| API 설계 | 엔드포인트, 요청/응답 |
| 에러 처리 | 에러 코드/메시지/처리 |
그리고 여기서 중요한 한 가지.
기존 코드의 설계 의도와 사용 패턴을 놓치면 “프로젝트랑 따로 노는 코드”가 나오거든요.
# 예: libs/fetch 모듈을 활용한다면
cat libs/fetch/CONTEXT.md
이 과정을 넣어두니, 결과물이 “그럴듯한 예제 코드”가 아니라 우리 코드베이스의 일부처럼 나오기 시작했습니다.
/spec이 “질문이 없다”는 건 무조건 묻지 않는다가 아니라,
정말 필요한 경우에만 묻도록 설계했다는 뜻입니다.
여기서 얻은 인사이트는 딱 하나였어요.
질문이 많이 나온다면
/spec이 문제라기보다, 명세서가 덜 익은 것이다.
명세서 요구사항을 바탕으로 Todo를 뽑고, 우선순위를 고정합니다.
1. 🔴 필수 요구사항 (모두 완료해야 함)
2. 🟡 권장 요구사항 (시간이 허락하면)
3. 🟢 선택 요구사항 (추가 개선)
그리고 보통 이런 흐름으로 구현 순서를 잡아요.
1. 타입 정의
2. 에러/결과 모델
3. 핵심 로직
4. 유틸리티
5. API 라우트
6. UI 컴포넌트
7. 페이지 통합
8. export 정리
타입부터 잡아두면 이후 구현이 안정적으로 굴러가서, 결과적으로 수정량이 줄었습니다.
여기서는 명세서 + 컨벤션을 근거로 구현합니다.
원칙은 딱 5가지로 고정했어요.
이게 좋아서라기보다, 원칙이 고정돼 있으니 결과가 안정적입니다.
코드만 만들고 끝내면 AI 도구가 아니라 “코드 생성기”죠.
/spec은 검증을 기본 프로세스에 넣었습니다.
pnpm build # 타입 체크
pnpm lint # 린트 체크
pnpm test -- <관련 테스트 경로> # 관련 테스트 (필요 시)
그리고 중요한 포인트.
에러가 나면 “에러 났어요”로 끝나는 게 아니라, 수정 후 재시도합니다.
이 부분 때문에 “결과물의 신뢰도”가 확 올라갔어요.
마지막에 구조화된 보고서를 출력합니다. 예시는 이런 느낌이에요.
## ✅ 구현 완료: 사용자 인증
### 요약
> 소셜 로그인을 통한 사용자 인증 시스템
### 구현된 요구사항
- [x] 🔴 Apple 로그인
- [x] 🔴 Google 로그인
- [x] 🟡 자동 로그인
### 생성된 파일
| 파일 | 설명 |
|------|------|
| `libs/auth/types.ts` | 타입 정의 |
| `libs/auth/core.ts` | 핵심 로직 |
### 검증 결과
| 항목 | 결과 |
|------|------|
| 타입 체크 | ✅ 통과 |
| 린트 | ✅ 통과 |
### 사용 예시
```typescript
import { signInWithApple } from "@/libs/auth";
const user = await signInWithApple();
/create-jest
/w-context
덕분에 “뭐가 됐고, 뭐가 남았고, 다음은 뭘 하면 되는지”가 한눈에 들어옵니다.
---
## 좋은 명세서 작성법: 질문을 0에 가깝게 만드는 체크리스트
`/spec` 품질은 결국 **명세서 품질**에 달려있습니다.
제가 가장 효과를 봤던 팁들만 모아볼게요.
### 1) 구현 위치를 무조건 적는다
```markdown
## 구현 위치
### 생성할 파일
- `libs/auth/types.ts`
- `libs/auth/core.ts`
- `libs/auth/index.ts`
### 수정할 파일
- `stores/authStore.ts`
경로가 없으면 100% “어디에 만들까요?”가 나옵니다.
## 타입 정의
```typescript
interface AuthUser {
id: string;
email: string;
name: string;
provider: 'apple' | 'google' | 'kakao';
}
타입이 있으면 결과물이 흔들리지 않아요. (그리고 나중에 타입 수정 지옥이 줄어듭니다.)
### 3) 에러 케이스는 표로 정리
```markdown
## 에러 처리
| 상황 | 코드 | 사용자 메시지 | 처리 |
|---|---|---|---|
| 네트워크 끊김 | NETWORK_ERROR | 인터넷 연결을 확인해주세요 | 재시도 |
| 토큰 만료 | TOKEN_EXPIRED | 다시 로그인해주세요 | 로그인 이동 |
표로 쓰면 “빼먹는 에러”가 확 줄어듭니다.
AI가 “어느 것부터?”를 고민할 필요가 없게 만드는 장치입니다.
## 기존 코드 활용
- `libs/api/client.ts` - API 호출 패턴 참고
- `stores/authStore.ts` - 상태 관리 패턴 참고
이게 있으면 결과물이 “우리 코드”에 훨씬 자연스럽게 붙습니다.
제가 실제로 링크 드라퍼에서 사용한 명세서 일부입니다.
# 공유 익스텐션 메타데이터 크롤링
## 개요
iOS Share Extension에서 공유된 URL의 메타데이터를 크롤링하여
링크 정보를 자동으로 채우는 기능
## 기능 요구사항
### 🔴 필수
- Open Graph 메타데이터 추출
- 제목/설명/이미지 URL 파싱
- 타임아웃 및 에러 처리
### 🟡 권장
- 이미지 없을 시 파비콘 폴백
- 캐싱으로 중복 요청 방지
## 구현 위치
### 생성할 파일
- `libs/crawler/types.ts`
- `libs/crawler/parser.ts`
- `libs/crawler/index.ts`
## 타입 정의
```typescript
interface LinkMetadata {
title: string | null;
description: string | null;
imageUrl: string | null;
faviconUrl: string | null;
}
| 에러 상황 | 처리 방법 |
|---|---|
| 네트워크 타임아웃 | 5초 후 빈 메타데이터 반환 |
| 파싱 실패 | URL만 저장, 메타데이터는 null |
이 정도만 적어도 `/spec`이 **질문 없이 구현**을 끝내는 편이었습니다.
---
## `/spec`이 바꾼 내 개발 방식
### Before: 대화형 개발(=핑퐁 지옥)
```text
나: "인증 기능 만들어줘"
AI: "어떤 방식으로?"
나: "소셜 로그인"
AI: "어떤 제공자?"
나: "Apple, Google"
AI: "토큰 저장은?"
나: (이때부터 집중력 붕괴)
나: "/spec user-auth"
AI: 분석 → 계획 → 구현 → 검증 → 보고
나: (검증 통과한 코드 받음)
시간이 줄어든 것도 좋았지만, 더 큰 변화는 이거였어요.
명세서가 같으면 결과도 비슷하다.
즉, 코드 품질이 ‘컨디션’이 아니라 ‘프로세스’에 의해 결정된다.
/spec은 단순히 “코드를 잘 뽑는 명령어”가 아니라,
명세서를 쓰는 시간이 들긴 해요.
그런데 그 시간은 대부분 “AI와 핑퐁하며 잃는 시간”을 통째로 대체하더라고요.
다음 글(3편)에서는 /create-jest를 다룹니다.
/spec으로 만든 코드에 테스트를 자동으로 붙이는 흐름이 어떻게 굴러가는지 공유해볼게요.
| 순서 | 제목 | 상태 |
|---|---|---|
| 1편 | Claude Code Commands로 개발 워크플로우 자동화하기 | ✅ 완료 |
| 2편 | /spec - 명세서 기반 코드 구현 자동화 | ✅ 현재 글 |
| 3편 | /create-jest - 테스트 코드 자동 생성 | 예정 |
| 4편 | /w-context - AI를 위한 문서 작성 | 예정 |
| 5편 | /pr + /apply-review - PR 워크플로우 자동화 | 예정 |
링크 드라퍼는 단순 저장 툴이 아니라, “다시 꺼내보게 만드는” 링크 관리 도구를 지향합니다.