[Spec Kit ③] Constitution — AI에게 우리 팀의 원칙을 각인시키기
Spec Kit의 모든 단계로 흘러 들어가는 단 하나의 파일, constitution.md. 데이터 엔지니어링 팀이 dq-monitor 프로젝트의 헌법을 어떻게 세우는지, 좋은 원칙과 나쁜 원칙의 차이는 무엇인지 실전 예제와 함께 짚습니다.
2부에서 우리는 specify CLI로 dq-monitor 프로젝트에 SDD 스캐폴딩을 깔고 Claude Code와 연동했습니다. 이제 빈 캔버스 위에 첫 줄을 그을 차례인데, 그 첫 줄이 명세도 코드도 아닌 헌법(constitution) 이라는 점이 SDD의 특이한 출발점입니다. 헌법은 "이 프로젝트에서 좋은 코드란 무엇인가"를 한 번 적어두는 파일이고, 이후 등장하는 모든 /speckit.* 단계가 이 파일을 읽고 시작합니다. 이 글은 그 헌법을 데이터 엔지니어링 팀의 현실에 맞게 어떻게 잘 쓰는지에 대한 이야기입니다.
이 글에서 배우는 것
- 헌법(
.specify/memory/constitution.md)이 왜 시리즈에서 가장 레버리지가 큰 파일인지/speckit.constitution명령으로 헌법 초안을 생성하는 법- "검증 가능한 원칙" vs "듣기 좋은 구호" — 좋은 원칙과 나쁜 원칙의 차이
- 바로 가져다 쓸 수 있는 데이터 엔지니어링 팀용 헌법 전체 예제
- 헌법이
clarify·checklist·analyze게이트와 어떻게 맞물리는지
이 글은 Spec Kit 시리즈의 3부입니다. 1부에서 SDD의 철학을, 2부에서 설치와 프로젝트 구조를 다뤘다면, 3부는 본격적인 워크플로의 첫 단추인 헌법을 채웁니다.
1. 헌법이란 무엇이고, 왜 모든 단계를 지배하는가
Spec Kit의 워크플로는 다음 순서로 흐릅니다.
Constitution → Specification → Clarification → Planning
→ Task Breakdown → Implementation → Convergence
이 흐름에서 헌법은 맨 앞에 옵니다. 그런데 단지 "첫 단계"인 것이 아니라, 나머지 모든 단계의 배경으로 깔리는 단계입니다. 명세를 쓸 때도, 계획을 세울 때도, 작업을 분해할 때도, 코드를 구현할 때도 AI 에이전트는 헌법을 먼저 읽고 그 기준 위에서 판단합니다.
헌법 파일은 다음 경로에 살아 있습니다.
.specify/memory/constitution.md
경로의 memory라는 이름이 의미심장합니다. 헌법은 프로젝트의 장기 기억입니다. 1부에서 우리는 바이브 코딩의 첫 번째 실패 모드로 "맥락 유실(context loss)"을 꼽았습니다. 대화가 길어지면 초반에 합의한 결정이 컨텍스트 창 밖으로 밀려난다는 문제였죠. 헌법은 바로 그 문제에 대한 구조적 해법입니다. "우리는 타입을 100% 명시한다", "스키마는 절대 조용히 깨지 않는다" 같은 결정을 파일에 박아두면, 그 결정은 50번째 요청에서도 사라지지 않습니다.
헌법은 매
/speckit.*단계에 로드됩니다. 명세 파일은 기능마다 하나씩 생기고, 계획도 기능마다 갈리지만, 헌법은 전 단계·전 기능에 공통으로 적용되는 단 하나의 파일입니다. 한 줄을 고치면 그 효과가 이후 모든 산출물에 곱해집니다. 이것이 헌법이 시리즈 전체에서 레버리지가 가장 큰 파일인 이유입니다.
구체적으로 헌법이 담는 것은 "이 프로젝트를 관통하는 원칙"입니다. 대표적으로 네 축이 있습니다.
| 축 | 헌법이 고정하는 것 | 고정하지 않으면 생기는 일 |
|---|---|---|
| 코드 품질 (Code Quality) | 스타일·타이핑·린트 게이트 | AI가 요청마다 다른 컨벤션으로 코드를 짬 |
| 테스트 기준 (Testing Standards) | 커버리지 기준, 어떤 테스트를 의무화할지 | 테스트가 있는 코드와 없는 코드가 뒤섞임 |
| UX 일관성 (UX Consistency) | 에러 포맷, 로그·알림의 톤과 구조 | 같은 상황을 매번 다른 모양으로 출력 |
| 성능 (Performance) | 지연·처리량 예산(SLA) | "빠르게"라는 말만 있고 기준이 없어 회귀를 못 잡음 |
핵심은 헌법이 무엇을 만들지(what) 가 아니라 어떻게 잘 만들지(how well) 를 정한다는 것입니다. 기능 요구사항은 명세(2부 예고대로 4부)에서 다룹니다. 헌법은 그 위 계층, 즉 어떤 기능을 만들든 항상 지켜야 할 품질의 바닥을 까는 일입니다.
2. /speckit.constitution 실행하기
헌법을 직접 손으로 적을 수도 있지만, Spec Kit은 AI 에이전트가 초안을 잡아주는 전용 명령을 제공합니다. Claude Code 안에서 다음과 같이 호출합니다.
/speckit.constitution명령만 던지면 에이전트가 "어떤 원칙에 초점을 둘까요?"라고 되묻거나, 함께 넘긴 지시를 바탕으로 바로 초안을 작성합니다. 가장 좋은 사용법은 초점 영역을 명시한 프롬프트를 함께 주는 것입니다.
/speckit.constitution
코드 품질, 테스트 기준, 사용자 경험 일관성, 성능 요구사항에
초점을 둔 원칙을 만들어 줘. 우리는 실시간 데이터 품질 모니터링
서비스(dq-monitor)를 만드는 데이터 엔지니어링 팀이야.(영문 원문 권장 표현: "Create principles focused on code quality, testing standards, user experience consistency, and performance requirements.")
이 프롬프트를 받으면 에이전트는 대략 다음 순서로 움직입니다.
여기서 강조할 점은 4번 단계(사람의 검토) 입니다. 에이전트가 만든 초안은 출발선이지 결승선이 아닙니다. 1부에서 봤듯 SDD의 품질은 "한 방 생성"이 아니라 "단계별 사람 검증"에서 나옵니다. 헌법은 그 첫 검증 지점이고, 여기서 흐릿하게 넘어간 원칙은 이후 모든 단계에 흐릿함을 전파합니다.
그래서 초안을 받으면 가장 먼저 던질 질문은 단 하나입니다. "이 원칙을 위반했는지 기계나 리뷰어가 판정할 수 있는가?" 이 질문에 "아니오"라면, 그 원칙은 다시 써야 합니다. 다음 절이 바로 그 이야기입니다.
3. 좋은 원칙 vs 나쁜 원칙 — 검증 가능성이 전부다
헌법이 실패하는 가장 흔한 방식은 "틀린 원칙"이 아니라 "검증할 수 없는 원칙" 입니다. "우리는 깨끗한 코드를 추구한다" 같은 문장은 누구도 반대하지 않지만, 동시에 누구도 위반할 수 없습니다. 위반을 판정할 수 없는 원칙은 AI를 전혀 제약하지 못합니다. AI는 어떤 코드를 내놓아도 "이것이 깨끗하지 않다"는 반증에 부딪히지 않기 때문입니다.
좋은 원칙은 구체적이고(specific) 검증 가능(checkable) 합니다. 사람이 읽고 "이건 통과/실패"를 가를 수 있어야 하고, 가능하면 기계가 판정할 수 있으면 더 좋습니다. 아래 표가 그 차이를 보여줍니다.
| 영역 | 나쁜 원칙 (모호·검증 불가) | 좋은 원칙 (구체·검증 가능) |
|---|---|---|
| 코드 품질 | "코드는 읽기 쉬워야 한다" | "모든 공개 함수는 타입 어노테이션을 가지며, ruff와 mypy --strict가 무경고로 통과해야 한다" |
| 테스트 | "충분한 테스트를 작성한다" | "신규/변경 코드의 라인 커버리지 80% 이상, 모든 데이터 스키마는 계약 테스트(contract test)를 가진다" |
| 성능 | "알림은 빨라야 한다" | "이상 탐지 → 알림 발송까지 p95 지연 5초 이내, 단일 인스턴스가 초당 10k 이벤트를 처리한다" |
| 데이터 계약 | "스키마를 잘 관리한다" | "스키마 변경은 버전 번호를 올리고, 하위 호환을 깨는 변경은 마이그레이션 노트 없이는 머지 불가" |
| 관측성 | "로그를 잘 남긴다" | "모든 로그는 JSON 구조화 로그이며 trace_id를 포함한다. 모든 알림은 원인 메트릭으로 역추적 가능해야 한다" |
| 보안 | "보안에 신경 쓴다" | "시크릿은 코드/로그에 절대 등장하지 않고 환경변수 또는 시크릿 매니저로만 주입한다. 서비스 계정은 최소 권한을 가진다" |
오른쪽 열의 공통점은 숫자, 도구 이름, 명시적 금지가 들어 있다는 것입니다. p95 5초, 커버리지 80%, mypy --strict, 머지 불가 같은 표현은 회색지대를 없앱니다. AI가 헌법을 읽고 코드를 짤 때도, 사람이 PR을 리뷰할 때도, 나중에 /speckit.checklist가 항목을 만들 때도 모두 같은 기준으로 판정할 수 있습니다.
원칙을 쓸 때 자가진단: 문장에서 테스트 케이스나 린트 규칙, 또는 PR 리뷰 코멘트가 곧바로 연상되지 않는다면, 그 원칙은 아직 구호 단계입니다. 한 번 더 구체화하세요.
4. 데이터 엔지니어링 팀용 헌법 전체 예제
이제 dq-monitor를 만드는 데이터 엔지니어링 팀이 실제로 쓸 만한 헌법을 통째로 보겠습니다. 아래는 그대로 .specify/memory/constitution.md에 넣고 팀 사정에 맞춰 숫자만 조정하면 되는, 재사용 가능한 자산입니다. (앞서 본 "검증 가능성" 원칙을 모든 조항에 적용했습니다.)
# dq-monitor Constitution
본 헌법은 실시간 데이터 품질 모니터링 서비스(dq-monitor)의 모든
명세·계획·작업·구현에 적용되는 최상위 원칙이다. 모든 `/speckit.*`
단계는 본 문서를 기준으로 산출물을 생성·검증한다. 조항 간 충돌 시
상위 번호(낮은 번호)가 우선한다.
Version: 1.0.0
Ratified: 2026-06-23
Last Amended: 2026-06-23
## 1. 코드 품질 (Code Quality)
- 1.1 언어 표준: 서비스 코드는 Python 3.12+를 사용하며, 모든 공개
함수·메서드·모듈 경계는 타입 어노테이션을 가진다.
- 1.2 정적 게이트: `ruff check`(lint) 및 `ruff format --check`(format),
`mypy --strict`가 모두 무경고로 통과해야 CI가 그린이 된다.
게이트 미통과 코드는 머지할 수 없다.
- 1.3 함수 복잡도: 단일 함수는 순환 복잡도(cyclomatic complexity)
10을 넘지 않는다. 초과 시 분해하거나 명시적 예외 주석을 남긴다.
- 1.4 의존성: 신규 런타임 의존성 추가는 PR 설명에 도입 이유와
대안 검토를 1줄 이상 기록한다. lock 파일 갱신을 동반한다.
## 2. 테스트 기준 (Testing Standards)
- 2.1 커버리지: 신규/변경된 라인의 테스트 커버리지는 80% 이상이다.
전체 커버리지를 하락시키는 PR은 사유를 명시해야 한다.
- 2.2 계약 테스트: 모든 입력 데이터 스키마와 외부 알림 페이로드는
계약 테스트(contract test)를 가진다. 스키마는 픽스처로
고정되며, 픽스처와 실제 코드가 어긋나면 테스트가 실패한다.
- 2.3 결정성: 테스트는 외부 네트워크·실시간 시계에 의존하지 않는다.
시간은 주입 가능한 클럭으로, 외부 시스템은 페이크/목으로 대체한다.
- 2.4 회귀 테스트: 버그 수정 PR은 수정 전이라면 실패했을 회귀
테스트를 반드시 포함한다.
## 3. 데이터 계약 규율 (Data Contract Discipline)
- 3.1 스키마 버전: 모든 데이터 스키마는 명시적 버전(semver)을 가진다.
- 3.2 무무음 파괴(no silent breaking change): 하위 호환을 깨는 스키마
변경은 (a) major 버전 증가, (b) 마이그레이션 노트, (c) 명시적
승인 라벨 없이는 머지 불가하다.
- 3.3 호환 우선: 가능하면 필드 추가(가산적 변경)를 선호하고, 필드
삭제·타입 변경·의미 변경은 deprecation 기간을 둔다.
- 3.4 검증 위치: 데이터는 시스템 경계(수집 지점)에서 즉시 스키마
검증한다. 검증을 통과하지 못한 레코드는 조용히 버리지 않고
격리(dead-letter)하고 메트릭으로 집계한다.
## 4. 관측성 (Observability)
- 4.1 구조화 로그: 모든 로그는 JSON 구조화 로그이며, 최소 필드로
`timestamp`, `level`, `service`, `trace_id`, `message`를 포함한다.
- 4.2 추적성: 하나의 이벤트 처리 흐름은 단일 `trace_id`로 수집부터
알림까지 연결 가능해야 한다.
- 4.3 메트릭: 처리 지연, 큐 적체, 이상 탐지 건수, 알림 발송 결과는
메트릭으로 노출한다(예: Prometheus 형식).
- 4.4 추적 가능한 알림: 모든 알림은 그 알림을 유발한 원인 메트릭·
레코드로 역추적 가능해야 한다. 근거 없는 알림은 금지한다.
## 5. 성능 / SLA (Performance / SLA)
- 5.1 알림 지연 예산: 이상 탐지 시점부터 알림 발송까지의 지연은
p95 기준 5초, p99 기준 10초를 넘지 않는다.
- 5.2 처리량: 단일 워커 인스턴스는 초당 최소 10,000 이벤트를
안정적으로 처리한다(백프레셔 없이).
- 5.3 성능 회귀 게이트: 성능에 영향을 줄 수 있는 변경은 벤치마크
결과를 PR에 첨부한다. 5.1·5.2 예산을 깨는 변경은 머지 불가.
- 5.4 자원 한계: 정상 부하에서 워커 메모리 사용은 설정된 상한을
넘지 않으며, 초과 시 OOM 대신 백프레셔로 흡수한다.
## 6. 보안 (Security)
- 6.1 시크릿: API 키·토큰·비밀번호는 코드·설정 파일·로그에 절대
평문으로 등장하지 않는다. 환경변수 또는 시크릿 매니저로만 주입한다.
- 6.2 최소 권한: 모든 서비스 계정·토큰은 필요한 최소 범위의 권한만
가진다. 광범위(admin/wildcard) 권한 부여는 PR에서 사유를 요구한다.
- 6.3 입력 신뢰 금지: 외부에서 들어온 데이터는 신뢰하지 않고,
검증·정규화 후에만 처리·저장한다(3.4와 연계).
- 6.4 의존성 보안: 알려진 취약점(예: `pip-audit` 고위험)이 있는
의존성은 머지 전에 해소하거나 명시적 위험 수용 기록을 남긴다.
## 거버넌스 (Governance)
- 본 헌법은 살아 있는 문서다. 분기마다 또는 중대한 아키텍처 변경 시
검토하며, 개정 시 버전(semver)과 개정일을 갱신한다.
- 어떤 명세·계획·작업도 본 헌법과 충돌할 수 없다. 충돌이 불가피하면
헌법을 먼저 개정한 뒤 진행한다.
- 원칙은 검증 가능해야 한다. 새 조항을 추가할 때는 "어떻게 위반을
판정하는가"를 함께 적는다.이 예제의 의도는 "그대로 베껴 쓰라"가 아니라 각 조항이 어떻게 검증 가능하게 쓰였는지를 보여주는 데 있습니다. 모든 항목이 숫자·도구·명시적 금지를 품고 있고, 거버넌스 절은 헌법을 write-once 문서가 아니라 살아 있는 문서로 못 박습니다. 여러분 팀은 숫자(80%, p95 5초, 초당 10k)와 도구 이름(ruff, mypy, pip-audit)을 자기 스택에 맞게 바꾸면 됩니다.
5. 헌법이 이후 게이트와 맞물리는 방식
헌법은 적어두고 끝나는 문서가 아니라, 이후 품질 게이트들이 참조하는 기준점입니다. Spec Kit은 프로덕션 작업이나 의미 있는 모호성이 있는 작업에서 /speckit.constitution·/speckit.clarify·/speckit.checklist를 품질 게이트로 켤 것을 권장합니다. 이 게이트들은 모두 헌법을 위에서 비춥니다.
각 게이트가 헌법을 쓰는 방식을 구체화하면 다음과 같습니다.
| 게이트 | 헌법을 어떻게 참조하는가 |
|---|---|
/speckit.clarify | 명세의 빈틈을 질문할 때, 헌법이 정한 기준(예: SLA, 스키마 버전 정책)에 비춰 빠진 결정을 캐묻는다 |
/speckit.checklist | 헌법 조항을 검증 가능한 체크 항목으로 변환한다. "p95 5초" 같은 조항이 곧 체크리스트 한 줄이 된다 |
/speckit.analyze | spec·plan·tasks가 서로 모순되는지뿐 아니라, 헌법을 위반하는 결정이 끼어들지 않았는지 교차검증한다 |
/speckit.implement | 코드를 생성할 때 헌법의 코드 품질·테스트·보안 원칙을 적용 기준으로 삼는다 |
이 구조의 함의는 분명합니다. 헌법 한 줄이 나중에 수많은 체크리스트 항목과 분석 경고로 증폭됩니다. 반대로 헌법에서 빠뜨린 원칙은 어느 게이트에서도 잡히지 않습니다. 게이트는 헌법에 없는 기준을 스스로 발명하지 않기 때문입니다. 그래서 3절에서 강조한 "검증 가능하게 쓰기"가 이 절에서 비로소 보상받습니다 — 구체적인 조항일수록 더 많은 자동 검증으로 환원됩니다.
6. 안티패턴 — 헌법을 망치는 세 가지 방법
좋은 헌법을 쓰는 법만큼, 흔한 실패를 피하는 법도 중요합니다. 현장에서 반복되는 세 가지 안티패턴을 짚습니다.
안티패턴 1 — 범용 헌법 복붙
인터넷에서 찾은 "모범 constitution"을 통째로 붙여넣는 경우입니다. 문제는 헌법의 가치가 그 팀의 구체적 결정에 있다는 점입니다. "초당 10k 이벤트", "p95 5초"는 dq-monitor의 도메인에서 나온 숫자이지, 범용 헌법에 적혀 있을 수 없습니다. 남의 헌법은 좋은 목차일 수 있어도 좋은 내용일 수는 없습니다. 구조는 참고하되, 모든 숫자와 금지 조항은 여러분 팀이 채우세요.
안티패턴 2 — 아무도 강제하지 않는 원칙
"커버리지 80%"라고 써놓고 CI에 커버리지 게이트가 없으면, 그 원칙은 장식입니다. 검증 가능하게 쓰는 것(3절)은 필요조건일 뿐, 실제로 게이트에 연결해야 비로소 힘을 갖습니다. 헌법의 각 조항에 대해 "이것을 강제하는 메커니즘이 어디에 있는가?"(CI 잡, 린트 규칙, /speckit.checklist 항목, PR 리뷰 룰)를 물으세요. 강제 메커니즘이 없는 조항은 다음 회고에서 강제하거나 삭제할 후보입니다.
안티패턴 3 — write-once 문서로 취급
헌법을 한 번 쓰고 잊는 것입니다. 그러나 프로젝트는 자라고, 처음의 SLA가 비현실적이었음이 드러나거나, 새로운 보안 요구가 생깁니다. 헌법은 살아 있는 문서여야 합니다 — 위 예제의 거버넌스 절이 "분기마다 검토, 개정 시 버전 갱신"을 명시한 이유입니다. 헌법을 고치는 일은 패배가 아니라 학습입니다. 다만 고칠 때는 코드가 아니라 헌법을 먼저 고친다는 SDD의 방향을 지키세요.
세 안티패턴을 한 문장으로: 헌법은 남의 것이 아니라 우리 것이어야 하고, 장식이 아니라 강제되어야 하며, 박제가 아니라 살아 있어야 합니다.
마치며
헌법은 분량으로 보면 한 화면짜리 파일이지만, 레버리지로 보면 시리즈 전체에서 가장 무거운 파일입니다. 이후 명세·계획·작업·구현의 모든 단계가 이 파일을 읽고 시작하기 때문입니다. 그래서 헌법을 잘 쓰는 비결은 단 하나, 검증 가능하게 쓰는 것입니다. 숫자·도구·명시적 금지를 담아, 사람과 AI와 자동 게이트가 모두 같은 기준으로 판정할 수 있게 만드세요.
다음 4부에서는 헌법이라는 바닥 위에 드디어 무엇을 만들지를 적습니다. /speckit.specify로 dq-monitor의 요구사항을 명세하고, /speckit.clarify로 그 명세의 빈틈을 메우는 과정을 따라가 보겠습니다. 헌법이 "어떻게 잘 만들지"를 정했다면, 명세는 "무엇을 만들지"를 정합니다. 둘이 만나는 지점에서 진짜 SDD가 시작됩니다.
참고 자료