1일차 3교시: Constitution 작성 워크숍
이 교시는 프로젝트의 최상위 원칙을 정의하는 시간입니다. 설계서 기준으로 Constitution은 모든 Spec, Plan, 코드가 준수해야 하는 불변의 아키텍처 DNA입니다.
교시 목표
- /speckit.constitution에 넣어야 할 내용을 설명할 수 있다.
- constitution과 spec의 경계를 구분할 수 있다.
- 생성된 constitution을 검토하고 수정할 수 있다.
- 로컬 브랜치와 git merge --no-ff로 설계 합의를 기록할 수 있다.
Constitution이란 무엇인가
Constitution은 이 팀이 앞으로 무엇을 지키며 개발할 것인가를 적는 문서입니다.
보통 아래 범주가 들어갑니다.
- 품질 원칙
- 테스트 원칙
- 브랜치와 리뷰 규칙
- 문서와 코드의 정합성 원칙
- 보안과 비밀정보 처리 원칙
- 성능과 접근성에 대한 최소 기준
/speckit.constitution에 넣어야 하는 내용
좋은 입력의 특징
- 특정 기능이 아니라 프로젝트 전체에 적용됩니다.
- 사람이 검토 가능한 규칙 문장입니다.
- 이후 spec과 plan을 검토할 기준으로 쓸 수 있습니다.
예시 1: Todo 앱 팀 원칙
이 프로젝트의 constitution을 작성해줘.
- 모든 기능은 자동 테스트 또는 명시적 검증 절차를 가져야 한다.
- 문서와 코드가 다르면 문서를 먼저 수정하거나 드리프트를 기록한다.
- 직접 main 브랜치에 커밋하지 않고 브랜치와 merge commit을 사용한다.
- 사용자의 데이터 손실 가능성이 있는 변경은 사전에 명시적으로 검토한다.
- 비밀값과 토큰은 저장소에 커밋하지 않는다.
- 모바일과 데스크톱 모두에서 읽기 쉬운 UI를 유지한다.
예시 2: 사내 관리자 도구
사내 관리자 도구를 위한 constitution을 작성해줘.
- 권한 검증 없는 관리자 기능은 허용하지 않는다.
- 운영 데이터 변경은 감사 로그를 남겨야 한다.
- 장애 대응을 위해 모든 외부 연동 실패는 명시적으로 로깅한다.
- 업무 규칙이 코드와 문서에서 다르면 문서를 우선 업데이트한다.
- 테스트 없이 운영 배포하지 않는다.
예시 3: 교육용 웹 서비스
교육용 웹 서비스 프로젝트의 constitution을 작성해줘.
- 학습자의 진행 상태 데이터는 추적 가능해야 한다.
- 접근성 기준을 고려해 키보드만으로 주요 기능을 사용할 수 있어야 한다.
- 과제 채점 기준과 결과 표시 기준은 문서화되어야 한다.
- 변경은 브랜치 단위로 나누고 merge commit으로 합친다.
넣으면 안 되는 내용
아래는 constitution이 아니라 spec 또는 plan에 가야 합니다.
- 사용자는 태그를 20자까지 입력할 수 있다
- 검색 결과는 최신순으로 정렬한다
- 백엔드는 FastAPI를 사용한다
- Tag 테이블과 TodoTag 테이블을 만든다
나쁜 예와 고친 예
나쁜 예
Todo 앱에서 태그를 추가하고 필터링하는 기능을 만들고 싶어.
React로 만들고 SQLite를 쓸 거야.
문제점:
- 기능 요구가 섞였습니다.
- 기술 스택이 섞였습니다.
- 팀 원칙이 보이지 않습니다.
고친 예
이 프로젝트의 constitution을 작성해줘.
- 기능 완료의 기준은 테스트 또는 명시적 검증 절차까지 포함한다.
- 문서와 코드가 다르면 문서를 먼저 정리한다.
- 직접 main에 작업하지 않는다.
- 비밀 정보는 .env로 관리하고 저장소에 커밋하지 않는다.
- 사용자의 주요 흐름은 작은 화면에서도 읽기 쉬워야 한다.
생성 후 반드시 검토할 질문
- 이 규칙은 앞으로 생길 모든 기능에도 적용되는가?
- 너무 추상적이라 검토 기준으로 쓰기 어려운 문장은 없는가?
- 팀이 실제로 지킬 수 없는 선언은 없는가?
- 보안, 테스트, 문서 정합성, 브랜치 전략이 빠지지 않았는가?
실습 단계
- /speckit.constitution을 실행합니다.
- 위 예시 중 프로젝트 성격에 맞는 입력을 바탕으로 프롬프트를 조정합니다.
- 생성된 constitution 문서를 읽고, 너무 추상적인 문장을 줄입니다.
- 아래 절차로 설계 합의를 로컬 Git 이력에 남깁니다.
git checkout -b docs/constitution
git status
git add .specify .github
git commit -m "docs: establish constitution"
git checkout main
git merge --no-ff docs/constitution -m "merge: constitution"
git push origin main
위 명령을 한 줄씩 어떻게 이해하면 되는가
초심자 입장에서는 이 블록이 그냥 외워서 치는 Git 명령처럼 보일 수 있습니다. 하지만 실제 의미는 constitution 합의를 별도 브랜치에서 기록하고, 검토 후 main에 합친다입니다.
git checkout -b docs/constitution
- docs/constitution이라는 새 브랜치를 만들고 그 브랜치로 이동합니다.
- 왜 필요한가: constitution은 프로젝트의 상위 원칙이므로, 바로 main에서 수정하기보다 합의용 변경으로 분리해 두는 편이 이력상 더 명확합니다.
git status
- 지금 어떤 파일이 바뀌었는지 확인합니다.
- 여기서는 보통 .specify/, .github/ 안의 constitution 관련 파일이 보이는지 확인하는 단계입니다.
- 이때 .venv/, .env, 캐시 파일 같은 로컬 전용 파일이 보이면 그대로 add 하면 안 됩니다.
git add .specify .github
- constitution 작업과 직접 관련된 폴더만 staging area에 올립니다.
- 왜 전체 git add .를 쓰지 않는가: 첫 실습에서는 로컬 환경 파일이나 의도하지 않은 변경이 섞이기 쉽기 때문입니다.
- 즉, 이 단계의 목적은 constitution 결과와 integration 관련 파일만 커밋 후보에 올리는 것입니다.
git commit -m "docs: establish constitution"
- staging area에 올린 변경을 하나의 이력으로 기록합니다.
- 이 커밋 메시지는 프로젝트 원칙 문서를 세웠다는 뜻입니다.
- 여기서 중요한 점은 코드 구현이 아니라 설계 원칙 합의가 커밋된다는 점입니다.
git checkout main
- 다시 기준 브랜치인 main으로 돌아옵니다.
- merge는 보통 합쳐질 대상 브랜치에서 실행하므로, main으로 돌아와서 합치는 흐름을 연습하는 것입니다.
git merge --no-ff docs/constitution -m "merge: constitution"
- docs/constitution 브랜치의 작업을 main에 합칩니다.
- --no-ff를 쓰는 이유는 merge commit을 남겨서, 나중에 로그를 봤을 때 여기서 constitution 합의가 main에 들어왔다는 사실이 분명히 보이게 하기 위해서입니다.
- 즉, 단순히 파일 내용만 반영하는 것이 아니라 합의의 시점을 이력에 남기는 명령입니다.
git push origin main
- 로컬 main의 변경을 원격 저장소에 올립니다.
- 이 단계가 끝나야 GitHub에서도 constitution 반영 결과를 볼 수 있습니다.
이 명령 블록에서 실제로 확인해야 하는 것
- git status에서 constitution과 관련된 파일만 보이는가?
- git add .specify .github 이후에도 staging에 이상한 파일이 섞이지 않았는가?
- merge 후 git log --oneline --graph를 봤을 때 constitution 관련 commit과 merge commit이 남는가?
자주 하는 실수
실수 1. main에서 바로 수정하고 커밋함
이 경우 합의용 변경과 기준 브랜치가 분리되지 않아, 교육 목표인 브랜치 기반 합의 흐름이 약해집니다.
실수 2. git add .로 전체를 올림
이 경우 .venv, 캐시 파일, 다른 문서 수정이 함께 올라갈 수 있습니다. 첫 실습에서는 선택적 add가 더 안전합니다.
실수 3. merge 전에 status를 보지 않음
내가 생각한 변경과 실제 staging 상태가 다를 수 있으므로, commit 전에 반드시 상태를 한 번 더 보는 습관이 필요합니다.
실수했을 때 최소 복구 방법
staging만 잘못했다면:
git restore --staged .
git status
아직 commit 전이라면 이 방법으로 staging을 비우고 다시 선택적으로 add 하면 됩니다.
왜 PR 대신 로컬 merge를 쓰는가
초심자는 여기서 자연스럽게 왜 GitHub Pull Request를 만들지 않고 바로 로컬에서 merge하나요?라는 질문을 하게 됩니다.
이 교육에서 PR 대신 로컬 merge를 먼저 쓰는 이유는 PR이 중요하지 않아서가 아니라, 지금 교시의 목표가 브랜치 기반 합의와 merge commit의 의미를 먼저 이해하는 데 있기 때문입니다.
이유 1. 지금 배우려는 핵심은 GitHub 웹 기능이 아니라 Git의 기본 흐름이기 때문
이 교시에서 정말 익혀야 하는 것은 다음 순서입니다.
- 원칙 문서를 만든다.
- 그 변경을 별도 브랜치로 분리한다.
- 검토 후 main에 합친다.
- merge commit으로 합의 시점을 남긴다.
이 핵심은 GitHub 웹의 PR 화면이 없어도 충분히 배울 수 있습니다.
이유 2. 초심자에게 도구 복잡도를 한 번에 늘리지 않기 위해서
PR까지 같이 쓰기 시작하면 학습자가 동시에 이해해야 하는 것이 많아집니다.
- 브랜치 생성
- push
- 원격 브랜치 추적
- PR 생성
- 리뷰 UI
- merge 버튼과 옵션
교육 설계상 1일차에서는 먼저 로컬 브랜치와 merge 자체의 의미를 익히고, 이후 2일차에서 리뷰와 merge 판단을 별도 주제로 다루는 편이 더 안정적입니다.
이유 3. 이 교시의 merge는 코드 통합보다 설계 합의 기록에 가깝기 때문
constitution 단계에서 merge는 단순히 파일을 합치는 작업이 아닙니다.
이 프로젝트의 상위 원칙에 합의했다는 사실을 이력으로 남기는 행위에 가깝습니다.
이 목적은 git merge --no-ff만으로도 충분히 드러납니다.
이유 4. 로컬 Git만으로도 SDD의 핵심 흐름을 체험할 수 있기 때문
이번 교육은 일부러 GitHub 웹 기능 의존도를 낮추고 있습니다.
그 이유는 다음과 같습니다.
- 실습 환경이 달라도 따라가기 쉽습니다.
- 회사망이나 권한 제약이 있어도 기본 흐름을 유지할 수 있습니다.
- 학습자가 플랫폼 기능보다 개발 프로세스를 먼저 이해하게 됩니다.
즉, PR은 협업 도구로서 매우 중요하지만, SDD의 핵심 흐름 자체는 로컬 Git만으로도 충분히 설명할 수 있습니다.
그럼 PR은 안 배워도 되는가
그 뜻은 아닙니다. 실제 협업에서는 PR이 매우 중요합니다.
다만 이 교육에서는 다음처럼 나눠서 접근합니다.
- 1일차: 브랜치, commit, merge commit의 의미를 로컬 Git으로 이해한다.
- 2일차: 로컬 리뷰, 품질 게이트, merge 판단을 통해 PR의 핵심 사고방식을 훈련한다.
즉, PR 화면을 먼저 배우는 것이 아니라, PR이 해결하려는 문제를 먼저 배우는 구조입니다.
한 문장으로 정리하면
이 교시에서 PR 대신 로컬 merge를 쓰는 이유는 웹 UI 사용법보다 브랜치 분리, 합의 기록, merge commit의 의미를 먼저 이해시키는 것이 교육 목표에 더 잘 맞기 때문입니다.
merge를 왜 하는가
여기서 merge는 단순한 파일 결합이 아닙니다. 이 프로젝트의 개발 원칙에 합의했다는 이력을 남기는 행위입니다.
스스로 점검
- constitution 문서만 읽어도 이 팀이 무엇을 중요하게 생각하는지 보이는가?
- 특정 기능 설명이 끼어 있지 않은가?
- 이후 spec.md를 볼 때 바로 비교할 수 있는 기준이 되는가?
'AI Native > GitHub Spec Kit으로 구현하는 SDD v2' 카테고리의 다른 글
| d01/07. clarify-workshop.md (0) | 2026.05.02 |
|---|---|
| d01/06. specify-workshop.md (2) | 2026.05.02 |
| d01/04. sdd-philosophy.md (0) | 2026.05.02 |
| d01/03. sdd-greenfield-workshop.md (0) | 2026.05.02 |
| d01/02. environment-setup.md (0) | 2026.05.02 |