Skill: Model Domain
목적
도메인의 유비쿼터스 언어를 정의하고 서술형 명세로 작성
철학: "ERD 그리기 말고, 말로 설명하기"
입력
- •도메인 이름 (예: "users")
- •Phase 2 결과 (SESSION.md의 완전한 도메인 정의)
출력
- •
ai-context/domain-books/{domain}/domain-model.md - •용어 정의 + 관계 규칙 + 제약 조건 + 생명주기
유비쿼터스 언어란?
해당 도메인 전용이면서 개발자가 쉽게 이해할 수 있는 전용 언어
❌ 잘못된 접근 (ERD)
code
┌─────────┐ ┌──────────────┐ │ User │───1:N──│ Translation │ └─────────┘ └──────────────┘
✅ 올바른 접근 (유비쿼터스 언어)
code
"사용자는 여러 개의 번역 기록을 가질 수 있다." "번역 기록은 반드시 한 명의 사용자에게 속한다."
사용 방법
1. 용어 추출
python
from skills.model_domain import extract_terms
terms = extract_terms(
session_data=read(".claude/SESSION.md"),
domain="users"
)
# 결과:
# [
# {"term": "사용자 (User)", "definition": "앱을 사용하는 개인", "example": "홍길동"},
# {"term": "식별자 (ID)", "definition": "사용자를 고유하게 구분하는 값", "example": "u_123abc"},
# {"term": "이메일 (Email)", "definition": "로그인 및 연락용 이메일 주소", "example": "user@example.com"},
# ...
# ]
2. 관계 규칙 생성
python
from skills.model_domain import generate_relationship_rules
rules = generate_relationship_rules(
domain="users",
session_data=read(".claude/SESSION.md")
)
# 결과:
# [
# {
# "rule_number": 1,
# "statement": "사용자는 여러 개의 번역 기록을 가질 수 있다.",
# "details": "한 사용자가 0개 이상의 번역을 요청할 수 있음",
# "example": "홍길동 사용자는 10개의 번역 기록을 가지고 있다"
# },
# ...
# ]
3. 제약 조건 추출
python
from skills.model_domain import extract_constraints
constraints = extract_constraints(
domain="users",
session_data=read(".claude/SESSION.md")
)
# 결과:
# [
# {
# "number": 1,
# "constraint": "식별자 (ID)는 시스템이 자동으로 생성한다.",
# "reason": "사용자가 직접 설정할 수 없음"
# },
# {
# "number": 2,
# "constraint": "이메일 (Email)은 중복될 수 없다.",
# "reason": "이미 존재하는 이메일로는 가입 불가",
# "example": "user@example.com으로 이미 가입되어 있으면 다시 가입 불가"
# },
# ...
# ]
4. 생명주기 정의
python
from skills.model_domain import define_lifecycle
lifecycle = define_lifecycle(
domain="users",
session_data=read(".claude/SESSION.md")
)
# 결과:
# {
# "create": {
# "steps": ["요청 접수", "중복 확인", "필수 정보 검증", "엔티티 생성", "자동 값 설정"],
# "rules": ["선호 언어 미제공 시 기본값은 '영어'"]
# },
# "update": {
# "steps": ["수정 요청", "권한 검사", "변경 가능 필드만 수정"],
# "editable_fields": ["표시 이름", "프로필 사진", "선호 언어"],
# "readonly_fields": ["식별자", "이메일", "가입 시각"],
# "rules": ["본인만 자신의 프로필을 수정할 수 있다"]
# },
# "delete": {
# "steps": ["탈퇴 요청", "본인 확인", "연결 데이터 확인", "모든 데이터 삭제", "엔티티 삭제"],
# "rules": ["탈퇴는 즉시 실행되며, 복구 불가능하다"]
# }
# }
domain-model.md 구조
markdown
# {domain} 도메인 모델
> 생성일: {오늘 날짜}
> Phase: 3 (Domain Modeler)
> 상태: ✅ 완료
---
## 📖 유비쿼터스 언어 (용어 정의)
> 이 도메인에서 사용하는 전용 용어들
### 핵심 용어
| 용어 | 정의 | 예시 |
|------|------|------|
| 사용자 (User) | 앱을 사용하는 개인 | "홍길동", "john@example.com" |
| 식별자 (ID) | 사용자를 고유하게 구분하는 값 | "u_123abc" |
| ... | ... | ... |
---
## 📐 관계 규칙
> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의
### 사용자와 번역 기록
**규칙 1**: 사용자는 여러 개의 번역 기록을 가질 수 있다.
- 한 사용자가 0개 이상의 번역을 요청할 수 있음
- 예: "홍길동 사용자는 10개의 번역 기록을 가지고 있다"
**규칙 2**: 번역 기록은 반드시 한 명의 사용자에게 속한다.
- 모든 번역은 누가 요청했는지 기록됨
- 예: "이 번역 기록은 john@example.com 사용자의 것이다"
---
## ✅ 제약 조건
> 엔티티가 지켜야 하는 규칙
### 필수 규칙
1. **식별자 (ID)**는 시스템이 자동으로 생성한다.
- 사용자가 직접 설정할 수 없음
2. **이메일 (Email)**은 중복될 수 없다.
- 이미 존재하는 이메일로는 가입 불가
- 예: "user@example.com"으로 이미 가입되어 있으면 다시 가입 불가
---
## 🔄 생명주기
> 엔티티가 어떻게 생성, 수정, 삭제되는지
### 생성 (가입)
\`\`\`
사용자가 가입을 요청한다
↓
이메일이 중복되지 않는지 확인한다
↓
모든 필수 정보가 제공되었는지 확인한다
↓
사용자 엔티티를 생성한다
↓
식별자(ID)와 가입 시각(CreatedAt)을 자동으로 설정한다
\`\`\`
**규칙**: 가입 시 선호 언어가 제공되지 않으면 기본값은 '영어'로 설정된다.
### 수정 (프로필 편집)
...
### 삭제 (탈퇴)
...
작성 원칙
✅ 좋은 명세 (서술형)
- •"사용자는 여러 개의 번역 기록을 가질 수 있다."
- •"번역 기록은 반드시 한 명의 사용자에게 속한다."
- •"이메일은 중복될 수 없다."
❌ 나쁜 명세 (기술적)
- •"User 테이블의 id 컬럼은 UUID 타입"
- •"Translation.user_id는 User.id를 참조하는 Foreign Key"
- •"email 컬럼에 UNIQUE 제약"
→ 말로 설명하기! 테이블/컬럼 언급 금지.
관계 표현 패턴
1:N 관계
code
"A는 여러 개의 B를 가질 수 있다." "B는 반드시 하나의 A에 속한다."
예시:
- •"사용자는 여러 개의 번역 기록을 가질 수 있다."
- •"번역 기록은 반드시 한 명의 사용자에게 속한다."
1:1 관계
code
"A는 하나의 B를 가진다." "B는 반드시 하나의 A에 속한다."
예시:
- •"사용자는 하나의 프로필을 가진다."
- •"프로필은 반드시 한 명의 사용자에게 속한다."
N:M 관계
code
"A는 여러 개의 B와 연결될 수 있다." "B는 여러 개의 A와 연결될 수 있다."
예시:
- •"사용자는 여러 개의 관심 태그를 가질 수 있다."
- •"태그는 여러 명의 사용자와 연결될 수 있다."
Cascade 관계
code
"A가 삭제되면 모든 B도 함께 삭제된다."
예시:
- •"사용자가 삭제되면 모든 번역 기록도 함께 삭제된다."
제약 조건 패턴
필수/선택
code
"X는 반드시 제공되어야 한다." "Y는 선택 사항이다."
유일성
code
"X는 중복될 수 없다."
형식 검증
code
"X는 반드시 Y 형식이어야 한다."
예시:
- •"이메일은 반드시 유효한 형식이어야 한다. (@ 기호 포함)"
값 범위
code
"X는 A, B, C 중 하나여야 한다." "Y는 최소 N 이상이어야 한다."
예시:
- •"선호 언어는 '한국어' 또는 '영어'만 가능하다."
- •"표시 이름은 최소 1자 이상이어야 한다."
자동 생성
code
"X는 시스템이 자동으로 생성한다."
생명주기 작성 가이드
생성 (Create)
code
요청자가 생성을 요청한다
↓
[검증 단계 1]
↓
[검증 단계 2]
↓
엔티티를 생성한다
↓
[자동 설정 단계]
수정 (Update)
code
요청자가 수정을 요청한다
↓
권한을 확인한다
↓
[검증 단계]
↓
변경 가능한 필드만 수정한다
수정 가능/불가능 필드 명시 필수
삭제 (Delete)
code
요청자가 삭제를 요청한다
↓
권한을 확인한다
↓
연결된 데이터를 확인한다
↓
[Cascade 처리]
↓
엔티티를 삭제한다
검증 체크리스트
domain-model.md 작성 완료 후:
- • 용어 테이블에 모든 핵심 용어 포함
- • 각 용어에 정의와 예시 있음
- • 관계 규칙이 서술형으로 작성됨
- • 모든 관계에 예시 포함
- • 제약 조건에 이유 설명
- • 생명주기 3단계 (생성/수정/삭제) 모두 포함
- • 기술 용어 (테이블, 컬럼, FK, UUID 등) 0개
- • 한글 용어 사용 (영어 병기는 괄호 안에)
기술 용어 필터링
금지 키워드:
python
TECH_KEYWORDS = [
# 데이터베이스
"테이블", "컬럼", "Foreign Key", "Primary Key", "Index",
"UUID", "VARCHAR", "INT", "TIMESTAMP",
# 프레임워크
"FastAPI", "SQLAlchemy", "Alembic", "Pydantic",
# 프로토콜
"HTTP", "REST", "GraphQL", "WebSocket",
# 기술 스택
"PostgreSQL", "MySQL", "Redis", "S3"
]
검증:
python
def validate_no_tech_terms(content: str) -> bool:
"""기술 용어가 없는지 확인"""
lower_content = content.lower()
for keyword in TECH_KEYWORDS:
if keyword.lower() in lower_content:
return False
return True