# 사전학습
바이브 코딩시, 쓰는 기본 스펙 파일들
* PRD.md
Product Requirement Document
* TRD.md
Technical Requirement Document
* AGENTS.md
AI 지침서
* API_SPEC.md ( 웹개발시)
등이 쓰임. 하지만, 새로운 프로젝트를 할때마다 정하는게 귀찮을 경우, Github 의 공식 SDD 를 쓰자.
1. 설치
# 설치
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
# 프로젝트 생성
specify init my-project --ai claude
# 기존 프로젝트에서 생성
specify init . --ai claude
# 설치 확인
specify check
2. 명령어는 claude 내에서 /speckit. 으로 확인 가능 함
3. 설계를 한후 docs/ 넣어둔다. 책에 있는 샘플파일을 첨부했다.
claude 실행 후, 프롬프트 입력한다.
/speckit-constitution 다음 원칙으로 constitution을 생성해줘:
- TypeScript strict 모드 필수
- API 응답은 API_SPEC.md의 형식을 정확히 따를 것
- 에러 응답은 { error: { code, message }} 형식
- Zod로 모든 요청 검증
- 비즈니스 로직은 src/server/services/에 분리
생성된 md 파일은 .specify/memory/constitution.md 이 위치에 있다.
더 안전한 사항을 위해서 아래와 같은 가드레일을 써준다.
# Tika Constitution
## Core Principles
### I. Specification-Driven Development (SDD)
모든 구현은 명세를 기반으로 하며, 명세는 구현보다 우선한다.
- **Why**: 명세 없는 구현은 불확실성을 야기하고 팀 간 혼선을 초래한다.
- **원칙**: 명세와 구현이 불일치하면 명세를 먼저 수정한 후 구현을 변경한다.
### II. Type Safety (NON-NEGOTIABLE)
타입 안전성은 협상 불가능한 필수 요구사항이다.
- **Why**: 런타임 에러의 대부분은 타입 문제에서 발생한다. 컴파일 시점에 잡는 것이 비용이 가장 적다.
- **원칙**: TypeScript strict 모드, any 타입 절대 금지, 타입 체크 통과 없이는 커밋 불가.
### III. Contract-First API Design
API는 명확한 계약을 기반으로 설계되고 구현된다.
- **Why**: 프론트엔드-백엔드 간 인터페이스 불일치는 통합 단계에서 큰 비용을 발생시킨다.
- **원칙**: API_SPEC.md에 정의된 요청/응답 형식을 정확히 준수한다.
### IV. Validated Inputs, Safe Outputs
모든 외부 입력은 검증되고, 모든 출력은 안전해야 한다.
- **Why**: 신뢰할 수 없는 입력은 보안 취약점과 런타임 에러의 주요 원인이다.
- **원칙**: Zod를 통한 입력 검증, 타입 안전한 출력 보장.
### V. Separation of Concerns
각 계층은 명확한 책임을 갖고, 계층 간 경계를 넘지 않는다.
- **Why**: 책임이 혼재된 코드는 테스트, 유지보수, 확장이 어렵다.
- **원칙**: 비즈니스 로직은 서비스 레이어에, 프레젠테이션 로직은 UI 레이어에 집중.
### VI. Test-Driven Development (TDD)
테스트는 구현 이후가 아닌 이전에 작성된다.
- **Why**: 사후 테스트는 구현에 맞춰 작성되어 실제 요구사항 검증에 실패한다.
- **원칙**: Red-Green-Refactor 사이클을 엄격히 준수한다.
## 🚨 Guardrails (절대 준수 사항)
AI 코딩 에이전트가 실수로 위험한 작업을 수행하지 않도록 명시적으로 금지하는 규칙들이다.
**이 규칙들은 어떤 상황에서도 위반할 수 없다.**
### 데이터베이스 금지 명령어
- `DROP TABLE`, `DROP DATABASE` — 절대 금지
- `TRUNCATE` — 절대 금지
- `DELETE FROM` (WHERE 절 없이) — 절대 금지
- `ALTER TABLE DROP COLUMN` — 사용자 명시적 허가 필요
### 데이터베이스 안전 규칙
- 삭제/리셋 작업 시 반드시 사용자 승인 요청
- 삭제 전 백업 또는 복구 방법 안내
- 테스트 데이터 존재 시 DB 리셋 대신 SQL로 해결
- 운영 DB 자동 변경 절대 금지
### Git 금지 명령어
- `git push --force` — 절대 금지
- `git reset --hard` — 절대 금지
- `git clean -fd` — 사용자 확인 필요
- `git branch -D` (main/master) — 절대 금지
### 패키지 관리 금지 명령어
- `npm audit fix --force` — 절대 금지
- `rm -rf node_modules && npm install` — 사용자 확인 필요
- 메이저 버전 자동 업그레이드 — 절대 금지
### 파일 시스템 금지 명령어
- `rm -rf /` 또는 루트 경로 삭제 — 절대 금지
- 프로젝트 외부 파일 수정 — 절대 금지
- `.env` 파일 삭제 — 사용자 확인 필요
- `src/` 디렉토리 전체 삭제 — 절대 금지
### 안전 작업 원칙
- 파괴적 작업(삭제, 초기화) 전 반드시 사용자 확인
- 복구 불가능한 작업은 백업 방법 먼저 안내
- 자동화된 스크립트의 파괴적 명령 실행 금지
- 의심스러운 작업은 실행 전 사용자에게 설명 및 확인
## Architecture Constraints
### Immutable Boundaries
시스템 경계는 명확하고 불변이다.
- Frontend (`src/client/`) ↔ Backend (`src/server/`) 직접 참조 금지
- Shared (`src/shared/`)만 양방향 참조 가능
- 경계를 넘는 모든 통신은 명세된 API를 통해서만
### Single Source of Truth
각 관심사는 단 하나의 정의 위치를 갖는다.
- 타입: `src/shared/types/`
- 검증 스키마: `src/shared/validations/`
- 비즈니스 로직: `src/server/services/`
- DB 스키마: `src/server/db/schema.ts`
### No Direct Database Access from Frontend
프론트엔드는 데이터베이스에 직접 접근할 수 없다.
- **Why**: 보안, 비즈니스 로직 중복, 트랜잭션 관리 문제
- **원칙**: 모든 데이터 접근은 API를 통해서만
## Quality Standards
### Non-Negotiable Quality Gates
다음 검증을 통과하지 못하면 커밋할 수 없다.
1. TypeScript 타입 체크 통과
2. 모든 테스트 통과
3. 빌드 성공
4. 명세 문서와 일치
### Security Requirements
- 환경 변수에 민감 정보 저장, 코드에 하드코딩 금지
- SQL Injection 방지: ORM 파라미터 바인딩만 사용
- XSS 방지: 모든 입력 검증 + React 자동 이스케이핑
- 에러 메시지에 내부 구현 상세 노출 금지
## Governance
### Constitution Authority
이 Constitution은 모든 다른 개발 관행, 가이드, 제안보다 우선한다.
### Amendment Process
Constitution 수정은 다음 절차를 따른다:
1. 변경 제안 (이유, 영향 범위, 대안 분석 포함)
2. 팀 전체 검토 및 논의
3. 합의 도출
4. 영향받는 코드의 마이그레이션 계획 수립
5. 문서화 및 승인
6. 버전 업데이트
### Enforcement
- 모든 PR은 Constitution 준수 여부 검증 필수
- 위반 사항 발견 시 즉시 수정
- 예외 허용 시 문서화 및 제한적 범위 명시
### Living Document
- Constitution은 프로젝트 진화에 따라 성장한다
- 하지만 핵심 원칙(Core Principles)은 신중히 변경한다
- 실무 세부사항은 CLAUDE.md에 위임
**실무 개발 가이드는 CLAUDE.md 참조**
---
**Version**: 1.0.0
**Ratified**: 2026-02-13
**Last Amended**: 2026-02-13
프로젝트를 생성하자
@docs/TRD.md를 참조해서 next.js 15 프로젝트를 생성해줘.
- App Router 기반
- TypeScript strict 모드
- Tailwind CSS 포함
- src 디렉터리 사용
프로젝트에서 사용할 .envl.local 을 만들고 필요한 정보를 선언해 두자. ( postgreSQL 접속 정보 )
테스트 환경을 구성하자. 다음 프롬프트를 이용하면 된다.
Jest 테스트 환경을 구성해줘.
- Next.js 15 App Router와 호환되어야 해.
- TypeScript strict 모드 사용
- @testing-library/react와 @testing-library/jest-dom 포함
- 경로 별칭 @/는 src/를 가리켜야 해.
db 접속 구성을 설정하자
@docs/DATA_MODEL.md 파일의 3. Drizzle 스키마 정의 섹션을 참고해서 src/server/db/schema.ts에 구현해줘.
- 로컬 PostgreSQL 연결 (postgres 드라이버 사용)
- 환경 변수는 DATABASE_URL 사용 ( .env.local 참고 )
drizzle 스크립트 가 생성하면 직접 실행한다. 스크립트가 없으면 생성해달라고 부탁한다.
npm run db:generate
npm run db:migrate
postgreSQL 에 접속해서 정상 생성을 확인한다.
CLAUDE.md 파일 ( 또는 AGENTS.md 파일 ) 을 업데이트 한다.
@CLAUDE.md 파일을 SDD 스펙에 맞게 업데이트 해줘.
- 프로젝트 개요와 구조
- 기술 스택
- 명세 문서 경로
- 코딩 컨벤션
- SDD 워크플로 규칙
업데이트 된 CLAUDE.md 파일 예제
# CLAUDE.md - Tika Development Guide
> **핵심 원칙은 `.specify/memory/constitution.md` 참조**
> 이 문서는 구체적인 구현 방법과 실무 가이드를 다룬다.
## 프로젝트 개요
Tika는 티켓 기반 칸반 보드 TODO 앱이다.
Next.js App Router 기반으로, 프론트엔드와 백엔드를 디렉토리 수준에서 분리한다.
src/shared/에서 타입과 검증 스키마를 공유한다.
## 프로젝트 구조
```
tika/
├── app/api/ # 백엔드 진입점 (Route Handlers)
├── src/
│ ├── server/ # 백엔드 로직 (services, db, middleware)
│ ├── client/ # 프론트엔드 로직 (components, hooks, api)
│ └── shared/ # 공유 타입, Zod 스키마, 상수
└── docs/ # 프로젝트 명세 문서
```
## 기술 스택
- **Framework**: Next.js 15 (App Router)
- **Language**: TypeScript (strict mode)
- **Frontend**: React 19
- **Styling**: Tailwind CSS 4
- **Drag & Drop**: @dnd-kit/core + @dnd-kit/sortable
- **ORM**: Drizzle ORM 0.38.x
- **DB**: PostgreSQL (로컬: node-postgres, 배포: Vercel Postgres)
- **Validation**: Zod
- **Testing**: Jest + React Testing Library
- **Deployment**: Vercel
## 환경 설정
### 환경 변수
```bash
# .env.local
DATABASE_URL=postgresql://user:password@localhost:5432/tika
```
### 경로 별칭
- `@/` → `src/`
- `@/app/` → `app/`
- `@/shared/` → `src/shared/`
- `@/server/` → `src/server/`
- `@/client/` → `src/client/`
## 명세 문서 (구현 전 필수 확인)
| 문서 | 용도 |
|------|------|
| docs/PRD.md | 제품 요구사항 |
| docs/TRD.md | 기술 요구사항 |
| docs/REQUIREMENTS.md | 상세 요구사항 (FR + NFR + US) |
| docs/API_SPEC.md | API 엔드포인트 명세 |
| docs/DATA_MODEL.md | DB 스키마, ERD, 비즈니스 규칙 |
| docs/COMPONENT_SPEC.md | 컴포넌트 계층, Props, 이벤트 |
| docs/TEST_CASES.md | TDD용 테스트 케이스 정의 |
## 코딩 컨벤션
### TypeScript
```typescript
// ✅ Good
interface Ticket {
id: number;
title: string;
}
export const TICKET_STATUS = {
BACKLOG: 'BACKLOG',
TODO: 'TODO',
} as const;
type TicketStatus = typeof TICKET_STATUS[keyof typeof TICKET_STATUS];
// ❌ Bad
interface ITicket { ... } // I 접두사 사용 금지
enum TicketStatus { ... } // enum 대신 const 객체 사용
let data: any; // any 사용 금지
```
### 백엔드 (app/api/ + src/server/)
#### Route Handler 패턴
```typescript
// app/api/tickets/route.ts
import { createTicketSchema } from '@/shared/validations/ticket';
import { ticketService } from '@/server/services/ticketService';
export async function POST(request: Request) {
// 1. 요청 파싱
const body = await request.json();
// 2. Zod 검증
const result = createTicketSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ error: { code: 'VALIDATION_ERROR', message: result.error.message } },
{ status: 400 }
);
}
// 3. 서비스 호출
const ticket = await ticketService.create(result.data);
// 4. 응답 반환
return Response.json(ticket, { status: 201 });
}
```
#### 서비스 레이어 패턴
```typescript
// src/server/services/ticketService.ts
import { db } from '@/server/db';
import { tickets } from '@/server/db/schema';
import type { CreateTicketInput, Ticket } from '@/shared/types';
export const ticketService = {
async create(input: CreateTicketInput): Promise<Ticket> {
// 비즈니스 로직
const position = await this.calculatePosition(input.status);
// DB 쿼리
const [ticket] = await db
.insert(tickets)
.values({ ...input, position })
.returning();
return ticket;
},
async calculatePosition(status: string): Promise<number> {
// 복잡한 로직은 별도 메서드로 분리
const lastTicket = await db
.select()
.from(tickets)
.where(eq(tickets.status, status))
.orderBy(desc(tickets.position))
.limit(1);
return lastTicket[0]?.position ?? 0 - 1024;
},
};
```
#### 에러 응답 형식
```typescript
// ✅ 올바른 에러 응답
return Response.json(
{
error: {
code: 'TICKET_NOT_FOUND',
message: '티켓을 찾을 수 없습니다'
}
},
{ status: 404 }
);
// ❌ 잘못된 에러 응답
return Response.json({ message: 'Not found' }, { status: 404 });
return Response.json({ error: 'Not found' }, { status: 404 });
```
### 프론트엔드 (src/client/)
#### 컴포넌트 패턴
```typescript
// src/client/components/ticket/TicketCard.tsx
import type { TicketWithMeta } from '@/shared/types';
interface TicketCardProps {
ticket: TicketWithMeta;
onEdit?: (id: number) => void;
onDelete?: (id: number) => void;
}
export const TicketCard = ({ ticket, onEdit, onDelete }: TicketCardProps) => {
return (
<div className="p-4 bg-white rounded shadow">
<h3>{ticket.title}</h3>
{ticket.description && <p>{ticket.description}</p>}
</div>
);
};
```
#### API 호출 패턴
```typescript
// src/client/api/ticketApi.ts
import type { CreateTicketInput, Ticket } from '@/shared/types';
export const ticketApi = {
async create(input: CreateTicketInput): Promise<Ticket> {
const res = await fetch('/api/tickets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input),
});
if (!res.ok) {
const error = await res.json();
throw new Error(error.error?.message ?? 'Unknown error');
}
return res.json();
},
};
// 컴포넌트에서 사용
import { ticketApi } from '@/client/api/ticketApi';
const handleCreate = async (data: CreateTicketInput) => {
try {
const ticket = await ticketApi.create(data);
// ...
} catch (error) {
console.error(error);
}
};
```
## SDD 워크플로우
### 1. 구현 전 명세 확인
```
API 구현 → API_SPEC.md 확인
컴포넌트 → COMPONENT_SPEC.md 확인
DB 작업 → DATA_MODEL.md 확인
타입 정의 → src/shared/types 확인
```
### 2. TDD 사이클
```
1. TEST_CASES.md에서 테스트 케이스 확인
2. 테스트 코드 작성 (Red) - 실패하는 테스트
3. 최소 구현 (Green) - 테스트 통과
4. 리팩토링 (Refactor) - 코드 개선
5. 명세 일치 확인
```
### 3. 구현 순서
```
1. src/shared/types - 타입 정의
2. src/shared/validations - Zod 스키마
3. __tests__/ - 테스트 코드
4. src/server/services/ - 비즈니스 로직
5. app/api/ - Route Handler
6. src/client/api/ - API 호출 함수
7. src/client/components/ - UI 컴포넌트
```
## 개발 명령어
### 일반 개발
```bash
npm run dev # 개발 서버 실행
npm run build # 프로덕션 빌드
npm run start # 프로덕션 서버 실행
npm run lint # ESLint 실행
```
### 테스트
```bash
npm run test # 테스트 실행
npm run test:watch # watch 모드
npx tsc --noEmit # 타입 체크
```
### 데이터베이스
```bash
npm run db:generate # 마이그레이션 생성
npm run db:migrate # 마이그레이션 실행
npm run db:studio # Drizzle Studio 실행
npm run db:seed # 시드 데이터 생성
```
## 검증 체크리스트
### 커밋 전
- [ ] `npx tsc --noEmit` 타입 체크 통과
- [ ] `npm run test` 모든 테스트 통과
- [ ] `npm run build` 빌드 성공
- [ ] console.log 제거 확인
- [ ] .env 파일 미포함 확인
### PR 전
- [ ] 명세 문서와 일치 확인
- [ ] 테스트 커버리지 충분
- [ ] 레이어 분리 준수 (Route Handler vs Service)
- [ ] Zod 검증 누락 없음
- [ ] 에러 응답 형식 일치
## 금지 사항
### 절대 하지 말 것
- ❌ any 타입 사용
- ❌ 명세 없는 기능 추가
- ❌ 테스트 삭제 또는 `.skip()`
- ❌ console.log 커밋
- ❌ .env 파일 커밋
- ❌ src/client/에서 DB 직접 접근
- ❌ Route Handler에 비즈니스 로직 작성
### 확인 필요
- ⚠️ DB 스키마 변경 → 마이그레이션 생성
- ⚠️ shared 타입 변경 → 영향 범위 확인
- ⚠️ API 응답 형식 변경 → API_SPEC.md 먼저 수정
- ⚠️ 패키지 추가/업그레이드 → 호환성 확인
## 문제 해결
### 타입 에러
```bash
# 타입 체크
npx tsc --noEmit
# 캐시 삭제 후 재시도
rm -rf .next
npm run build
```
### 테스트 실패
```bash
# 단일 테스트 실행
npm run test -- path/to/test.test.ts
# 상세 로그
npm run test -- --verbose
```
### DB 연결 오류
```bash
# 환경 변수 확인
echo $DATABASE_URL
# DB 상태 확인
psql $DATABASE_URL -c "SELECT 1"
```
## Git 워크플로우
### 커밋 메시지
```bash
feat: 티켓 생성 API 구현
fix: 티켓 삭제 시 404 에러 수정
refactor: ticketService 로직 분리
test: 티켓 목록 조회 테스트 추가
docs: API_SPEC.md 에러 코드 추가
```
### 브랜치 전략
- `main`: 프로덕션
- `feature/*`: 기능 개발
- `fix/*`: 버그 수정
---
**핵심 원칙과 거버넌스는 `.specify/memory/constitution.md` 참조**
이제 speckit 을 이용해서 단계별로 구현해 나간다.
/speckit-specify @docs/API_SPEC.md의 POST /api/tickets 명세를 확인하고 구현에 필요한 요구사항을 정리해줘.
/speckit-plan POST /api/tickets를 구현할 계획을 세워줘.
Zod 검증, Route Handler, Service 레이어 분리를 고려해.
-> plan.md , spec.md 파일 등이 생성됨.
/speckit-tasks
-> 위에서 생성된 파일 기준으로 task.md 가 생성
/speckit-implements Phase 1 부터 순서대로 구현해줘.
각 Phase 완료 후, 다음으로 넘어가.
@docs/TEST_CASES.md의 TC-API-001 (티켓생성)을 Jest 테스트로 변환해줘.
-> 이렇게 생성된 테스트를 npm test 로 직접 실행하고, 완료 하도록 한다.
** 흐름 정리 **
/speckit.constitution <- 프로젝트 최초 1회
---- 반복 ----
/speckit.specify <- 기능별 명세 확인
/speckit.clarify <- (선택) 모호함 제거
/speckit.plan <- 구현 계획 수립
/speckit.tasks <- 작업 분해
/speckit.analyze <- (선택) 정합성 검증
/speckit.implement <- 구현 실행
테스트 검증
---- 반복 ----
반복중에 중요사항의 기록을 위해 아래와 같은 프롬프트로 스킬을 만들어서 쓰도록 한다.
매번 프롬프트 입력 후 코드를 수정하고 git에 반영할 때,
다음 내용을 자동으로 기록하는 시스템을 만들어줘:
1. 내가 입력한 프롬프트 내용
2. 변경된 파일 목록과 라인 수
3. 작업 날짜/시간, 브랜치명
4. 테스트 결과 (있다면)
요구사항:
- CHANGELOG.md에 상세 이력 기록
- CLAUDE.md에 최근 변경사항 요약 ( 최근 7-14일)
- /changelog "요약" 명령어로 실행
- Hook보다는 Skill 방식으로 구현 (사용자가 원할 때만 실행)
형식:
## [브랜치명] - YYYY-MM-DD HH:MM
### Prompt
> "사용자가 입력한 요청"
### Changes
- **Added**: 새 기능 (`파일경로`)
- **Modified**: 수정 내용 (`파일경로`)
### Files Modified
- `파일명` (+10, -2 lines)
# 사용예제
/changelog 지금 수정 내역을 반영한 뒤 커밋 메시지를 만들어서 푸시하고 기록해줘.
changelog를 통해 update 문서나 코드들 리스트업해서 알려줘. 제대로 반영이 되는 건지 확인해보고 싶어.
아직, 본격적인 프로젝트를 하지 않아서 인지 이러한 SDD 의 번거로운 작업 없이도 사실 잘 만들어 오고 있다..
흐음;; 'ㅁ';