# 미팅 3: 최종 확정 + 반대 프레임
**일시**: 2026-03-04
**주제**: InfoKeyword Worker-Frontend 스키마 불일치 사고 — 최종 확정 + Devil's Advocate
**퍼실리테이터**: 개발2팀 Agent
**형식**: 역할 시뮬레이션 미팅 (토르 / 프레이야 / 헤임달 / 미미르 / 마아트)
**선행 미팅**: 미팅 1 현황 진단, 미팅 2 개선안 설계 (2026-03-04)

---

## 참가자

| 이름 | 역할 | 전문 영역 | 미팅 3 역할 |
|------|------|-----------|------------|
| 토르 | 백엔드 개발자 | Python Worker, Pydantic | 반대 프레임 + 보완안 제시 |
| 프레이야 | 프론트엔드 개발자 | Zod, TypeScript 타입 안전성 | 반대 프레임 + 보완안 제시 |
| 헤임달 | 테스터 | 계약 테스트, CI 전략 | 반대 프레임 + 보완안 제시 |
| 미미르 | 아키텍트 | 전체 설계 관점 | 반대 프레임 + 최종 아키텍처 확정 |
| 마아트 | QC 매니저 | qc_verify.py, QC-RULES.md | 반대 프레임 + QC 최종안 확정 |

---

## 개회 — 퍼실리테이터

**퍼실리테이터**: 미팅 2에서 훌륭한 개선안이 도출되었습니다. 오늘은 그것을 그대로 확정하는 것이 아니라, 의도적으로 약점을 찾아 보완합니다. 각자 자신의 영역에서 가장 날카로운 반대 의견을 제시해 주세요. 목표는 비판을 위한 비판이 아닌, 실제 실패 시나리오를 사전에 발견하는 것입니다.

---

## Round 1: 반대 프레임 (Devil's Advocate)

### 토르 (백엔드) — Pydantic 모델 설계에 대한 반론

**"Pydantic 모델을 Worker마다 만드는 것이 유지보수 부담이 되지 않을까? Agent가 코드를 생성하는 환경에서 Pydantic 모델을 매번 정확히 만들 수 있는가?"**

구체적 우려 사항:

1. **Worker 증가에 따른 모델 파일 폭발**: 현재 InfoKeyword Worker 하나지만, Worker가 10개, 20개로 늘어나면 `models.py` 파일이 Worker마다 생깁니다. Agent가 새 Worker를 만들 때 Pydantic 모델을 "작성해야 한다"는 것을 알고 있을까요? QC-RULES.md에 명시되지 않으면 빠뜨릴 가능성이 높습니다.

2. **Pydantic 모델과 실제 Worker 코드의 드리프트**: Agent가 Worker 로직을 수정할 때, `worker.py`는 바꾸지만 `models.py`는 바꾸지 않는 경우가 현실적입니다. "Pydantic 모델이 있다"는 사실을 Agent가 기억하지 못하거나 무시하면, 모델이 존재하지만 실제로는 검증을 우회하는 코드가 생길 수 있습니다. 예: `result = process()` 후 `KeywordFirestoreDoc(**result)` 검증 라인을 Agent가 삭제하면 도루묵입니다.

3. **Pydantic v1 vs v2 혼용**: Agent가 코드를 생성할 때 Pydantic v1 문법(`schema()`)과 v2 문법(`model_json_schema()`)을 혼용할 수 있습니다. 특히 `model.schema()` vs `model.model_json_schema()` 차이를 Agent가 일관되게 유지한다는 보장이 없습니다.

4. **"Pydantic 모델이 있으면 충분하다"는 착각**: Pydantic 모델이 존재해도, Worker가 `KeywordFirestoreDoc(**result).dict()` 대신 그냥 `result`를 Firestore에 저장하면 검증이 아예 동작하지 않습니다. 도구가 있어도 사용하지 않으면 무용지물입니다.

---

### 프레이야 (프론트엔드) — Zod + JSON Schema 이중 관리에 대한 반론

**"Zod 스키마와 JSON Schema를 이중으로 관리하면 결국 드리프트가 발생하지 않을까? 그리고 런타임 검증이 성능에 영향을 줄 수 있지 않은가?"**

구체적 우려 사항:

1. **Zod ↔ JSON Schema 수동 동기화 문제**: 미팅 2에서 "수동 작성 Zod + JSON Schema"로 결정했습니다. 실제로 Agent가 Zod 스키마(`keyword-data.schema.ts`)를 수정할 때 JSON Schema(`keyword-data.schema.json`)도 함께 수정할 것이라는 보장이 없습니다. 두 파일이 서로 다른 언어, 다른 포맷이므로 Agent가 "이 파일을 바꾸면 저 파일도 바꿔야 한다"는 연결 관계를 놓칠 가능성이 높습니다. 미팅 1에서 진단한 "두 파일의 동기화가 깨진다"는 문제를 우리 스스로 재현하는 셈입니다.

2. **런타임 Zod 파싱 비용**: Firestore에서 데이터를 읽을 때마다 Zod로 파싱하면, 특히 리스트 조회(수십~수백 개 도큐먼트)에서 성능 오버헤드가 발생할 수 있습니다. 계약 테스트 목적이라면 개발/스테이징에서만 검증하고 프로덕션에서는 검증을 끄는 방안도 고려해야 하지 않을까요?

3. **Zod transform의 유지보수 복잡성**: `KeywordDisplaySchema`의 `transform` 내부에서 snake_case → camelCase 매핑을 수동으로 작성합니다. 필드가 20개, 30개가 되면 transform 코드 자체가 에러 유발 지점이 됩니다. Agent가 새 필드를 Worker에 추가하면서 Zod transform에 해당 매핑을 추가하지 않으면 또 다른 묵음 실패가 발생합니다.

4. **"파싱 실패 = 앱 중단"의 리스크**: 프로덕션에서 Zod 파싱이 실패하면 어떻게 됩니까? 미팅 2 설계에서는 파싱 실패 시 에러를 던진다고 했는데, 실제 서비스에서 데이터 구조가 조금 달라졌다고 앱 전체가 에러 페이지를 보여주는 것이 최선인가요? 에러 경계(Error Boundary)와 fallback 전략이 같이 설계되어야 합니다.

---

### 헤임달 (테스터) — 계약 테스트의 실효성에 대한 반론

**"계약 테스트가 있어도 Agent가 스키마를 안 읽거나, 새 필드를 추가하면서 스키마를 안 업데이트하면 여전히 못 잡는 거 아닌가?"**

구체적 우려 사항:

1. **"테스트를 통과시키는" 방향으로의 수렴**: Agent가 코드를 수정하다가 계약 테스트가 FAIL이면, 가장 쉬운 해결책은 "테스트를 통과시키기 위해 sample.json을 수정"하는 것입니다. sample.json을 Worker 실제 출력에 맞춰 업데이트하면 테스트는 PASS됩니다. 하지만 이건 계약이 바뀐 것이고, 프론트엔드 Zod 스키마가 업데이트되지 않으면 여전히 불일치입니다. "테스트 통과"와 "계약 정합성"이 같지 않습니다.

2. **새 Worker에 계약 테스트가 없는 경우**: Agent가 새 Worker(`info_trend`, `info_competition` 등)를 만들 때 `test_contract.py`를 작성하지 않으면, 새 Worker는 계약 테스트 보호망 밖에 있습니다. CI 계약 게이트는 존재하는 계약 테스트를 실행할 뿐, "계약 테스트가 없는 Worker"의 존재는 검출하지 못합니다.

3. **sample.json의 현실성 문제**: sample.json이 Worker 실제 실행 결과라고 했지만, "테스트 데이터"를 입력으로 한 결과입니다. 실제 프로덕션 데이터에서만 나타나는 엣지 케이스(예: 특수문자 키워드, 빈 결과, 음수 카운트)가 sample.json에 반영되지 않으면, 그 케이스에서의 스키마 불일치는 여전히 검출되지 않습니다.

4. **계약 테스트 자체가 오래된 스냅샷이 될 위험**: 처음에 계약 테스트를 잘 작성해도, 6개월 후 Worker가 크게 리팩토링되면 계약 테스트가 실제 계약을 반영하지 않는 낡은 테스트가 될 수 있습니다. 테스트가 PASS되지만 실제로는 계약이 바뀐 상황.

---

### 미미르 (아키텍트) — JSON Schema 단일 진실 공급원의 취약성에 대한 반론

**"JSON Schema를 단일 진실 소스로 두면, 스키마 파일 자체가 잘못되었을 때 모든 게 무너진다. 스키마 파일의 정확성은 누가 보증하는가?"**

구체적 우려 사항:

1. **"진실의 단일 소스"가 거짓말을 하면**: `keyword-data.schema.json`이 잘못 작성되어 있어도, Pydantic 계약 테스트와 Zod 계약 테스트 모두 이 파일을 기준으로 PASS합니다. 즉, 두 테스트 모두 PASS인데 실제 시스템은 불일치 상태일 수 있습니다. "공통의 잘못된 기준"을 공유하는 상황입니다. 예: JSON Schema에 `keyword_count`가 `string`으로 잘못 정의되어 있으면, Pydantic 모델도 Zod 스키마도 거기에 맞춰 "string"으로 검증하고 둘 다 PASS합니다. 실제 Worker는 `int`를 저장하는데 말이죠.

2. **스키마 파일 변경 이력 관리 부재**: JSON Schema 파일이 git에 있다고 해도, 누가 왜 어떤 필드를 변경했는지 추적하기 어렵습니다. git blame으로 commit을 추적할 수 있지만, "이 변경이 Pydantic 모델 변경과 동기화된 것인가"를 확인하는 자동화된 방법이 없습니다.

3. **SSoT의 역설**: 단일 진실 공급원을 두는 이유는 불일치를 없애기 위해서입니다. 그런데 이 파일 자체의 정확성을 검증하는 상위 참조가 없으면, SSoT는 오히려 "모든 에러의 공통 원인"이 됩니다. Pydantic 모델을 SSoT로 두고 JSON Schema는 파생물(derived artifact)로 취급해야 하는 것 아닐까요?

4. **`shared/schemas/` 디렉토리 소유권 불명확**: 이 디렉토리를 누가 관리합니까? 토르(Worker 변경 시)와 프레이야(Frontend 변경 시)가 각자 수정하면, 충돌 해결 시 누구의 판단이 우선합니까? "공유" 파일이라 누구도 책임지지 않는 상황이 될 수 있습니다.

---

### 마아트 (QC) — QC 규칙 실효성에 대한 반론

**"QC-RULES.md에 항목을 추가해도, Agent(AI)가 실제로 그 항목을 꼼꼼히 수행한다는 보장이 있는가? 현재도 5항목을 건너뛰는 경우가 있다."**

구체적 우려 사항:

1. **체크리스트 증가 = 준수율 감소**: 셀프 QC를 5항목에서 6항목으로, 거기에 "인터페이스 계약 점검 체크리스트"까지 추가하면 Agent가 처리해야 할 항목이 상당히 늘어납니다. 체크리스트가 길어질수록 Agent(그리고 사람도)는 항목을 빠뜨리거나 형식적으로 체크하는 경향이 있습니다. 5개도 건너뛰는 상황에서 항목을 더 추가하는 것이 실질적 개선인가요?

2. **"해당 없음" 판단의 남용**: 마아트가 6번 항목에 "UI 컴포넌트 변경만 → 6번 해당 없음으로 체크 허용"이라고 했습니다. Agent가 모든 변경을 "UI 컴포넌트 변경"으로 분류하면 6번 항목은 사실상 무력화됩니다. "해당 없음" 판단을 Agent가 스스로 내리게 하는 것은 자기 면죄부를 주는 구조입니다.

3. **schema_contract.py가 SKIP되는 경우**: `--contract-schema` 플래그를 지정하지 않으면 schema_contract verifier가 SKIP됩니다. Agent가 qc_verify.py를 실행할 때 이 플래그를 "챙겨야 한다"는 것을 알고 있을까요? Workers 변경 작업인데도 플래그 없이 실행하면 계약 검증이 완전히 우회됩니다.

4. **마아트 독립 검증의 형식화**: critical 레벨에서 마아트(QC 역할)가 "계약 테스트를 직접 재실행하라"는 지시가 추가되었지만, 이것도 결국 Agent가 역할극을 하는 것입니다. Agent가 마아트 역할을 수행하면서 "형식적으로" 재실행 결과를 보고하고 PASS 처리할 수 있습니다. 실제 독립성이 보장되지 않습니다.

5. **qc_verify.py 자체의 버그 가능성**: schema_contract.py verifier를 새로 추가하면, 이 verifier 자체에 버그가 있을 수 있습니다. "파일이 존재하는가"를 체크하는데 경로 해석 오류로 항상 True를 반환한다거나, JSON Schema 검증 로직에 버그가 있어 잘못된 데이터도 PASS처리하면 오히려 잘못된 신뢰를 줍니다.

---

## Round 2: 약점 보완 토론

**퍼실리테이터**: 5가지 반론이 제시되었습니다. 각 반론에 대해 구체적이고 실현 가능한 보완책을 도출합니다. 한 사람의 반론에 여러 사람이 응답합니다.

---

### 토르의 반론 보완 — Pydantic 모델 유지보수 문제

**프레이야 → 토르**: "Pydantic 모델을 잊어버리는 문제는 QC-RULES.md가 아닌 코드 구조로 해결해야 합니다. Worker 베이스 클래스(BaseWorker)를 만들고, `output_model` 속성을 추상 속성으로 강제하면 됩니다."

```python
# workers/base_worker.py
from abc import ABC, abstractmethod
from pydantic import BaseModel

class BaseWorker(ABC):
    @property
    @abstractmethod
    def output_model(self) -> type[BaseModel]:
        """서브클래스는 반드시 Pydantic 출력 모델을 정의해야 합니다."""
        ...

    def validated_output(self, result: dict) -> dict:
        """출력 dict를 Pydantic 모델로 검증 후 반환합니다."""
        return self.output_model(**result).model_dump()
```

Agent가 새 Worker를 만들 때 BaseWorker를 상속하면, `output_model`을 정의하지 않으면 인스턴스화 자체가 실패합니다. "잊어버릴 수 없는 구조"입니다.

**헤임달 → 토르**: "Pydantic 모델과 실제 Worker 코드 드리프트 문제는 Worker 단위 테스트에서 검출해야 합니다. 단위 테스트에 `test_output_uses_validated_model()`을 의무화하면, Agent가 검증 라인을 삭제하면 단위 테스트가 FAIL됩니다."

**미미르 → 토르**: "Pydantic v1/v2 혼용 문제는 팀 표준을 명시하는 것으로 해결합니다. QC-RULES.md에 '본 프로젝트는 Pydantic v2 사용. `model_json_schema()` 사용, `schema()` 사용 금지'를 한 줄 추가하고, schema_contract.py verifier에서 `schema()` 호출 코드를 발견하면 경고를 줍니다."

**마아트 → 토르**: "Worker 증가에 따른 모델 파일 관리는 디렉토리 관례로 강제합니다. `workers/{worker_name}/models.py` 파일 부재를 schema_contract.py가 검출합니다. 즉, Worker 디렉토리에 `models.py`가 없으면 FAIL."

**보완책 합의**:
- [B-1] `BaseWorker` 추상 클래스 도입, `output_model` 추상 속성 강제
- [B-2] Worker 디렉토리에 `models.py` 존재 여부를 schema_contract.py에서 검증
- [B-3] QC-RULES.md에 Pydantic v2 표준 명시 (`model_json_schema()` 사용 명시)
- [B-4] Worker 단위 테스트에 `validated_output()` 사용 검증 케이스 의무화

---

### 프레이야의 반론 보완 — Zod+JSON Schema 이중 관리 문제

**토르 → 프레이야**: "Zod ↔ JSON Schema 드리프트를 없애는 가장 확실한 방법은 JSON Schema를 진실 공급원에서 파생물로 격하시키는 것입니다. Pydantic 모델을 진정한 SSoT로 두고, JSON Schema는 `make export-schema` 명령으로 생성된 아티팩트로 취급합니다."

```makefile
# Makefile
export-schema:
    cd workers/info_keyword && python -c \
    "from models import KeywordFirestoreDoc; \
    import json; \
    print(json.dumps(KeywordFirestoreDoc.model_json_schema(), indent=2))" \
    > shared/schemas/keyword-data.schema.json
    @echo "schema.json 갱신 완료. Zod 스키마와 동기화하세요."
```

Zod 스키마는 여전히 사람이(또는 Agent가) JSON Schema를 보고 수동으로 작성하지만, JSON Schema 자체는 자동 생성 아티팩트이므로 "JSON Schema가 틀릴 가능성"이 제거됩니다.

**헤임달 → 프레이야**: "Zod transform 누락 문제는 계약 테스트의 '모든 필드 undefined 아님' 케이스로 잡을 수 있습니다. 미팅 2에서 이미 이 테스트가 설계되었는데, 이것이 transform 누락의 자동 검출기 역할을 합니다."

**미미르 → 프레이야**: "런타임 성능 문제는 실제로 측정해야 합니다. Zod의 safeParse는 매우 빠르고(나노초 수준), 수백 개 도큐먼트도 1ms 미만입니다. 성능이 실제로 문제가 되는지 프로파일링 없이 '성능 우려' 때문에 검증을 끄는 것은 더 위험합니다. **검증은 항상 ON. 성능 문제가 실측되면 그때 최적화**."

**마아트 → 프레이야**: "파싱 실패 시 앱 중단 문제는 설계 결정입니다. 데이터 불일치를 묵음으로 삼켜서 잘못된 UI를 보여주는 것과, 명확한 에러 화면을 보여주는 것 중 어느 것이 더 나쁩니까? 우리가 이번 사고에서 교훈을 얻었듯이, 묵음 실패가 더 위험합니다. 프로덕션 파싱 실패는 Sentry 등 에러 모니터링으로 즉시 알람을 받는 것이 맞습니다. React Error Boundary는 UX를 위한 것이고, 근본 해결은 스키마 불일치를 사전에 막는 것."

**보완책 합의**:
- [B-5] JSON Schema는 Pydantic `model_json_schema()`에서 자동 생성되는 파생 아티팩트로 확정. `Makefile`에 `export-schema` 타겟 추가.
- [B-6] Zod 스키마는 JSON Schema 기반으로 작성하되, 계약 테스트(undefined 필드 없음)가 transform 누락 검출기 역할 수행.
- [B-7] 런타임 Zod 파싱은 프로덕션 포함 **항상 ON**. 성능은 실측 후 결정.
- [B-8] 파싱 실패 처리: React Error Boundary + Sentry 에러 알람. "잘못된 데이터 묵음 표시"보다 "명확한 에러"가 원칙.

---

### 헤임달의 반론 보완 — 계약 테스트 실효성 문제

**토르 → 헤임달**: "Agent가 sample.json을 수정해서 테스트를 통과시키는 문제는, sample.json 변경이 자동으로 감지되면 해결됩니다. schema_contract.py verifier에서 sample.json의 git diff를 확인하여 'sample.json이 변경되었는데 Zod 스키마도 변경되지 않았다면 WARN'을 출력하게 합니다."

**프레이야 → 헤임달**: "새 Worker에 계약 테스트가 없는 문제는 토르의 B-2 보완책(models.py 존재 여부 검증)의 확장입니다. schema_contract.py가 Worker 디렉토리에 `tests/test_contract.py`도 존재하는지 함께 체크합니다."

**미미르 → 헤임달**: "sample.json의 엣지 케이스 부재 문제는 sample.json을 하나가 아닌 여러 개로 확장하는 것으로 해결합니다."

```
shared/schemas/
    keyword-data.schema.json
    keyword-data.sample.normal.json    # 정상 케이스
    keyword-data.sample.edge.json      # 엣지 케이스 (빈 결과, 특수문자 등)
```

계약 테스트는 모든 sample 파일에 대해 실행합니다. 에뮬레이터 E2E(Phase 4)까지 가기 전에 edge case를 수동으로 커버하는 현실적 방법입니다.

**마아트 → 헤임달**: "계약 테스트가 낡은 스냅샷이 되는 문제는 주기적 갱신 강제보다, Worker 코드가 변경되면 sample.json 재생성을 강제하는 구조가 더 확실합니다. `workers/{name}/worker.py`가 변경된 PR에서 `shared/schemas/{name}.sample.*.json`이 함께 변경되지 않으면 CI에서 WARN을 냅니다."

**보완책 합의**:
- [B-9] sample.json 변경 시 Zod 스키마 미변경이면 WARN 출력 (schema_contract.py에 구현)
- [B-10] Worker 디렉토리에 `tests/test_contract.py` 존재 여부 검증 추가
- [B-11] sample.json 분리: `sample.normal.json` + `sample.edge.json` (최소 2개)
- [B-12] worker.py 변경 PR에서 sample.json 미변경 시 CI WARN

---

### 미미르의 반론 보완 — JSON Schema SSoT 취약성 문제

**토르 → 미미르**: "JSON Schema가 거짓말을 하면 모든 게 무너진다는 문제는, JSON Schema를 Pydantic이 생성하도록 하면 해결됩니다. 이미 B-5에서 결정했습니다. Pydantic 모델이 SSoT이고, JSON Schema는 자동 파생이므로 JSON Schema 자체가 틀릴 가능성이 없습니다. Pydantic 모델과 JSON Schema가 불일치하는 경우는 `make export-schema`를 실행하지 않은 경우뿐이고, 이것은 schema_contract.py에서 파일 수정 시각 비교로 감지합니다."

**프레이야 → 미미르**: "schema_contract.py에서 Pydantic 모델과 JSON Schema의 필드 목록을 Python에서 직접 비교 검증합니다. 파일 수정 시각보다 더 확실합니다."

```python
# schema_contract.py 내부
def verify_pydantic_json_schema_sync(model_path: str, schema_path: str) -> bool:
    """Pydantic 모델과 JSON Schema 파일의 필드 목록이 일치하는지 확인."""
    # Pydantic 모델 임포트 + model_json_schema() 호출
    # JSON Schema 파일 읽기
    # properties 키 목록 비교
    pydantic_fields = set(pydantic_schema["properties"].keys())
    json_schema_fields = set(file_schema["properties"].keys())
    return pydantic_fields == json_schema_fields
```

**헤임달 → 미미르**: "`shared/schemas/` 소유권 문제는 명시적 CODEOWNERS 파일로 해결합니다. GitHub의 `.github/CODEOWNERS`에 `shared/schemas/ @마아트`를 지정하면, 이 디렉토리 변경 PR에 마아트의 리뷰가 자동으로 요청됩니다. QC 매니저가 스키마 변경을 최종 승인하는 구조입니다."

**마아트 → 미미르**: "소유권이 마아트(QC)라는 것에 동의합니다. 단, 마아트가 변경 내용을 이해할 수 있어야 합니다. PR에 'schema_change_summary'를 작성하는 템플릿을 추가하겠습니다. '변경된 필드', '변경 이유', 'Worker/Frontend 동기화 완료 여부'를 명시합니다."

**보완책 합의**:
- [B-13] JSON Schema는 Pydantic `model_json_schema()`의 자동 파생 아티팩트 (B-5 재확인). Pydantic이 진정한 SSoT.
- [B-14] schema_contract.py에서 Pydantic 모델 ↔ JSON Schema 필드 목록 비교 자동 검증
- [B-15] `.github/CODEOWNERS`에 `shared/schemas/ @maart` 추가 (QC 매니저 최종 승인)
- [B-16] shared/schemas 변경 PR에 `schema_change_summary` 필수 작성 (변경 필드, 이유, 동기화 완료 여부)

---

### 마아트의 반론 보완 — QC 규칙 실효성 문제

**토르 → 마아트**: "체크리스트 항목 수 증가 문제는 '조건부 체크리스트' 구조로 해결합니다. 모든 작업에 모든 항목이 적용되는 것이 아니라, 작업 유형에 따라 해당 항목만 활성화됩니다."

```markdown
## 셀프 QC 체크리스트

### [항상 적용]
- [ ] 1. 이 변경이 다른 파일에 영향을 미치는가?
- [ ] 2. 이 로직의 엣지 케이스는 무엇인가?
- [ ] 3. 이 구현이 작업 지시와 정확히 일치하는가?
- [ ] 4. 에러 처리와 보안은 확인했는가?
- [ ] 5. 테스트가 모든 경로를 커버하는가?

### [데이터 모델 변경 시 추가 적용]
(해당 조건: workers/ 또는 src/types/ 또는 shared/schemas/ 변경 시)
- [ ] 6. shared/schemas/ 계약 파일을 갱신하고 계약 테스트를 재실행했는가?
```

항목을 추가하는 것이 아니라, 해당 조건에서만 추가 항목이 나타나는 구조입니다.

**프레이야 → 마아트**: "'해당 없음' 남용 문제는 판단 기준을 코드 경로로 대체합니다. '이 변경에 workers/ 파일이 포함되었는가?'는 Agent가 주관적으로 판단하는 것이 아니라 git diff 결과로 객관적으로 확인 가능합니다. schema_contract.py verifier가 스스로 이 조건을 체크하여 '이 작업에 계약 검증이 필요합니다'를 알려줍니다."

**헤임달 → 마아트**: "`--contract-schema` 플래그 미지정으로 SKIP되는 문제는, qc_verify.py의 기본 동작을 바꾸는 것으로 해결합니다. `workers/` 디렉토리에 `models.py`가 존재하면 자동으로 schema_contract verifier를 활성화합니다. 플래그가 필요 없게 만듭니다."

```python
# qc_verify.py 내 자동 감지 로직
def auto_detect_contract_schema():
    """Worker 디렉토리에 models.py가 있으면 자동으로 계약 스키마 경로를 반환합니다."""
    worker_models = list(Path("workers").glob("*/models.py"))
    if worker_models:
        # 첫 번째 모델에서 스키마 경로 추론
        ...
```

**미미르 → 마아트**: "schema_contract.py 자체의 버그 가능성은 verifier도 테스트해야 한다는 의미입니다. `tests/test_schema_contract_verifier.py`를 작성하여, 의도적으로 잘못된 sample.json을 입력했을 때 verifier가 FAIL을 반환하는지 검증합니다. 도구의 정확성을 테스트하는 것은 표준 엔지니어링 관행입니다."

**보완책 합의**:
- [B-17] 셀프 QC를 "항상 적용(5항목)" + "데이터 모델 변경 시 추가(1항목)" 구조로 분리
- [B-18] qc_verify.py가 workers/ 디렉토리에서 models.py를 자동 감지하여 schema_contract verifier 자동 활성화 (플래그 불필요)
- [B-19] `tests/test_schema_contract_verifier.py` 작성 (verifier 자체의 정확성 검증)
- [B-20] `--contract-schema` 플래그 없이 실행 시에도 자동 감지로 계약 검증 수행

---

## Round 3: 최종 확정 계획

### 확정된 개선안 목록

미팅 2에서 도출한 개선안에 Round 2 보완책을 통합한 최종 확정 목록:

| ID | 개선안 | 미팅 2 기반 | 추가 보완 |
|----|--------|------------|----------|
| F1 | **BaseWorker 추상 클래스** — `output_model` 강제 | 신규 | B-1 |
| F2 | **Worker Pydantic 모델** — `models.py` 필수화 | A2 | B-2, B-3 |
| F3 | **Pydantic → JSON Schema 자동 생성** — Makefile `export-schema` | A1 일부 | B-5, B-13 |
| F4 | **공유 스키마 디렉토리** — `shared/schemas/` 구조 확립 | A1 | B-11 (sample 분리) |
| F5 | **Frontend Zod 스키마 + camelCase transform** | A3 | B-6, B-7, B-8 |
| F6 | **Python 계약 테스트** — `test_contract.py` 의무화 | B1 | B-10 |
| F7 | **TypeScript 계약 테스트** — `keyword-contract.test.ts` 의무화 | B2 | B-9 |
| F8 | **schema_contract.py verifier** — 자동 감지 + 필드 비교 | C1 | B-14, B-18 |
| F9 | **schema_contract.py verifier 자체 테스트** | 신규 | B-19 |
| F10 | **QC-RULES.md 개정** — 조건부 체크리스트 구조 | A4 | B-17 |
| F11 | **CODEOWNERS 설정** — shared/schemas 소유권 명시 | 신규 | B-15 |
| F12 | **CI WARN 규칙** — worker.py 변경 시 sample.json 미변경 감지 | C2 | B-12 |
| F13 | **에러 처리 전략** — React Error Boundary + Sentry | 신규 | B-8 |
| F14 | **Firestore 에뮬레이터 E2E** (장기) | D1 | - |

---

### 구현 계획 (파일명, 담당, 순서)

#### Phase 1 — 즉시 실행 (이번 주)

**목표**: 스키마 계약의 뼈대 구축. 이번 주 안에 "잘못된 키로는 저장 불가" + "잘못된 키는 즉시 에러" 달성.

| 순서 | 파일 | 작업 내용 | 담당 | 완료 기준 |
|------|------|-----------|------|----------|
| 1-1 | `workers/base_worker.py` (신규) | BaseWorker 추상 클래스, `output_model` 추상 속성, `validated_output()` 메서드 | 미미르 | 추상 클래스 인스턴스화 시 TypeError 발생 |
| 1-2 | `workers/info_keyword/models.py` (신규) | `KeywordFirestoreDoc` Pydantic v2 모델, 필드 정의 + description + validator | 토르 | `KeywordFirestoreDoc.model_json_schema()` 정상 출력 |
| 1-3 | `workers/info_keyword/worker.py` (수정) | BaseWorker 상속, `validated_output()` 호출로 Firestore 저장 | 토르 | 잘못된 필드명 입력 시 ValidationError 발생 |
| 1-4 | `Makefile` (신규 또는 수정) | `export-schema` 타겟: Pydantic → `shared/schemas/keyword-data.schema.json` 자동 생성 | 토르 | `make export-schema` 실행 시 JSON Schema 파일 갱신 |
| 1-5 | `shared/schemas/keyword-data.schema.json` (신규) | `make export-schema` 실행으로 생성 (수동 작성 금지) | 토르 (생성), 마아트 (검토) | 파일 존재 + Pydantic 모델 필드 목록과 일치 |
| 1-6 | `shared/schemas/keyword-data.sample.normal.json` (신규) | Worker 실제 실행 결과 (정상 케이스) | 토르 | schema.json 검증 통과 |
| 1-7 | `shared/schemas/keyword-data.sample.edge.json` (신규) | 엣지 케이스 샘플 (빈 결과, 특수문자, 경계값) | 헤임달 | schema.json 검증 통과 |
| 1-8 | `shared/schemas/keyword-data.schema.ts` (신규) | Zod 스키마: `KeywordFirestoreDocSchema` + `KeywordDisplaySchema` (camelCase transform 포함) | 프레이야 | TypeScript 컴파일 성공 |
| 1-9 | `frontend/src/services/keyword-service.ts` (수정) | 기존 `as KeywordData` 타입 단언 → Zod `safeParse` 교체, Error Boundary 연동 | 프레이야 | 잘못된 키 데이터 → 즉시 ZodError 발생 |
| 1-10 | `QC-RULES.md` (수정) | 셀프 QC 조건부 구조 + 인터페이스 계약 점검 체크리스트 + 마아트 독립검증 계약 재실행 | 마아트 | 팀 전체 합의 후 커밋 |

#### Phase 2 — 단기 실행 (다음 주)

**목표**: 계약 테스트 자동화. PR 시 계약 위반 자동 검출 달성.

| 순서 | 파일 | 작업 내용 | 담당 | 완료 기준 |
|------|------|-----------|------|----------|
| 2-1 | `workers/info_keyword/tests/test_contract.py` (신규) | Worker 출력 → JSON Schema 검증, sample.normal/edge → JSON Schema, Pydantic ↔ JSON Schema 필드 일치, `validated_output()` 사용 검증 | 헤임달 + 토르 | `pytest test_contract.py` 4개 테스트 PASS |
| 2-2 | `frontend/tests/integration/keyword-contract.test.ts` (신규) | sample.normal/edge → Zod 파싱, camelCase 변환 검증, undefined 필드 없음 | 헤임달 + 프레이야 | `vitest run tests/integration/` PASS |
| 2-3 | `.github/CODEOWNERS` (신규 또는 수정) | `shared/schemas/ @maart` 추가 | 미미르 | shared/schemas 변경 PR에 마아트 자동 리뷰 요청 |

#### Phase 3 — 중기 실행 (2~3주 내)

**목표**: QC 파이프라인 자동화. qc_verify.py가 계약 검증을 자동 수행.

| 순서 | 파일 | 작업 내용 | 담당 | 완료 기준 |
|------|------|-----------|------|----------|
| 3-1 | `verifiers/schema_contract.py` (신규) | (1) Worker 디렉토리 models.py 존재 검증, (2) test_contract.py 존재 검증, (3) JSON Schema 파일 존재, (4) sample.*.json → JSON Schema 검증 (`jsonschema` 라이브러리), (5) Pydantic 모델 ↔ JSON Schema 필드 비교, (6) sample.json 변경 + Zod 스키마 미변경 시 WARN | 마아트 + 미미르 | 모든 검증 항목 동작 확인 |
| 3-2 | `qc_verify.py` (수정) | workers/ 디렉토리 auto-detect로 schema_contract 자동 활성화, ALL_CHECKS에 schema_contract 추가 | 마아트 | 플래그 없이 `python3 qc_verify.py` 실행 시 schema_contract 자동 실행 |
| 3-3 | `tests/test_schema_contract_verifier.py` (신규) | verifier 자체 테스트: 올바른 sample → PASS, 잘못된 sample → FAIL, models.py 없음 → FAIL | 헤임달 | 3개 테스트 케이스 PASS |
| 3-4 | CI 설정 파일 (수정) | PR CI에 계약 테스트 게이트 추가, worker.py 변경 + sample.json 미변경 시 WARN | 미미르 + 헤임달 | 계약 위반 PR 자동 WARN 동작 확인 |

#### Phase 4 — 장기 검토 (1개월 내)

| 파일 | 작업 내용 | 담당 | 완료 기준 |
|------|-----------|------|----------|
| Firebase Emulator CI 환경 | Firebase CLI 기반 에뮬레이터 설정, Worker 실행 → 저장 → fetch → 렌더링 E2E | 헤임달 | 스테이징 배포 전 E2E 자동 실행 |

---

### QC-RULES.md 최종 개정안 (전문)

> 아래는 미팅 2 개정안을 Round 2 보완책으로 강화한 최종 전문입니다.

```markdown
# QC-RULES.md — 개발2팀 Quality Control 규칙
**버전**: 2.0 (2026-03-04, 미팅 3 최종 확정)
**이전 버전**: 1.0 (셀프 QC 5항목, schema verifier 없음)

---

## 1. 셀프 QC (작업 완료 전 필수)

### 1-A. 기본 체크리스트 (모든 작업에 항상 적용)

- [ ] 1. 이 변경이 다른 파일에 영향을 미치는가? (영향 파일 목록 명시)
- [ ] 2. 이 로직의 엣지 케이스는 무엇인가? (빈 값, 경계값, 예외 경로)
- [ ] 3. 이 구현이 작업 지시와 정확히 일치하는가?
- [ ] 4. 에러 처리와 보안은 확인했는가?
- [ ] 5. 테스트가 모든 경로를 커버하는가?

### 1-B. 데이터 계약 체크리스트 (아래 조건 해당 시 추가 적용)

**적용 조건** (해당 경로의 파일이 이번 변경에 포함된 경우):
- `workers/` 하위 파일 변경
- `src/types/`, `src/services/` 하위 파일 변경
- `shared/schemas/` 하위 파일 변경

위 조건 중 하나라도 해당하면:
- [ ] 6-1. `shared/schemas/{name}.schema.json`이 Pydantic 모델과 동기화되어 있는가?
         (미동기화 시: `make export-schema` 실행)
- [ ] 6-2. `shared/schemas/{name}.sample.normal.json`이 Worker 실제 출력 기반으로 최신 상태인가?
- [ ] 6-3. Python 계약 테스트 재실행: `pytest workers/{name}/tests/test_contract.py`
- [ ] 6-4. TypeScript 계약 테스트 재실행: `vitest run tests/integration/`
- [ ] 6-5. camelCase ↔ snake_case 변환이 Zod transform에 반영되어 있는가?

**중요**: "해당 없음"은 위 조건에 해당하는 파일이 이번 변경에 없을 때만 허용.
         workers/ 파일 변경 시 이 체크리스트를 우회하는 것은 계약 위반입니다.

---

## 2. 인터페이스 계약 관리 (참조 사항)

### 계약 파일 위치 및 소유권

| 파일 | 역할 | 생성 방법 | 소유자 |
|------|------|-----------|--------|
| `shared/schemas/{name}.schema.json` | 언어 독립적 계약 정의 (파생 아티팩트) | `make export-schema` 자동 생성 | 마아트 (QC) |
| `shared/schemas/{name}.sample.normal.json` | 정상 케이스 Worker 출력 샘플 | Worker 실제 실행 결과 | 토르 (Worker 담당) |
| `shared/schemas/{name}.sample.edge.json` | 엣지 케이스 샘플 | 수동 작성 | 헤임달 (테스터) |
| `workers/{name}/models.py` | Pydantic 출력 모델 (진정한 SSoT) | 수동 작성 (필수) | 해당 Worker 개발자 |
| `shared/schemas/{name}.schema.ts` | Zod 스키마 + camelCase transform | schema.json 기반 수동 작성 | 프레이야 (Frontend) |

### 기술 표준
- Python Worker 스키마: **Pydantic v2** (`model_json_schema()` 사용. `schema()` 사용 금지)
- TypeScript Frontend 스키마: **Zod** (safeParse 사용, as 타입 단언 사용 금지)
- 공유 계약 파일: **JSON Schema** (`.schema.json`)
- Python 계약 검증 라이브러리: **jsonschema**
- camelCase 변환 책임: **Zod transform 레이어** (humps 등 별도 라이브러리 사용 금지)

### Worker 추가 시 의무 사항
새 Worker `{name}`을 추가할 때 반드시 생성해야 하는 파일:
1. `workers/{name}/models.py` — Pydantic 출력 모델 (BaseWorker 상속 시 강제됨)
2. `workers/{name}/tests/test_contract.py` — Python 계약 테스트
3. `shared/schemas/{name}.schema.json` — `make export-schema`로 생성
4. `shared/schemas/{name}.sample.normal.json` — 정상 케이스 샘플
5. `shared/schemas/{name}.sample.edge.json` — 엣지 케이스 샘플
6. `shared/schemas/{name}.schema.ts` — Zod 스키마

이 중 하나라도 누락 시 schema_contract.py verifier FAIL.

---

## 3. 자동 검증 (qc_verify.py)

### 실행 방법
```bash
python3 qc_verify.py --task-id {task-id}
```
`workers/` 하위에 `models.py`가 존재하면 schema_contract verifier 자동 활성화.

### verifier 목록 (v2.0)

| verifier | 검증 내용 | FAIL 조건 | 도입 시기 |
|----------|-----------|-----------|----------|
| api_health | HTTP 200 응답 여부 | 비정상 응답 | v1.0 |
| file_check | 보고서 파일 존재 + 크기 | 파일 없거나 0 bytes | v1.0 |
| data_integrity | task-timers.json ↔ .done 파일 교차 검증 | 상태 불일치 | v1.0 |
| test_runner | pytest exit code | 0이 아닌 경우 | v1.0 |
| schema_contract | 스키마 계약 검증 (상세 아래) | 아래 참조 | **v2.0** |

### schema_contract verifier 검증 항목

| # | 검증 항목 | FAIL/WARN |
|---|-----------|-----------|
| SC-1 | Worker 디렉토리에 `models.py` 존재 | FAIL |
| SC-2 | Worker 디렉토리에 `tests/test_contract.py` 존재 | FAIL |
| SC-3 | `shared/schemas/{name}.schema.json` 존재 | FAIL |
| SC-4 | `sample.normal.json` → JSON Schema 검증 통과 (`jsonschema`) | FAIL |
| SC-5 | `sample.edge.json` → JSON Schema 검증 통과 (`jsonschema`) | FAIL |
| SC-6 | Pydantic 모델 필드 목록 ↔ JSON Schema properties 일치 | FAIL |
| SC-7 | sample.json 최근 변경 + Zod 스키마 미변경 감지 | WARN |
| SC-8 | Pydantic v1 문법 (`schema()`) 사용 탐지 | WARN |

---

## 4. 마아트 독립 검증 (critical 레벨)

critical 레벨 작업에서 마아트는 다음을 수행합니다:

1. `python3 qc_verify.py --task-id {id}` 직접 실행 및 결과 검토
2. `.done` 파일 및 보고서 파일 내용 직접 확인
3. API 응답 body 구조 샘플링 확인
4. 보안 취약점 코드 패턴 검토
5. 변경된 파일이 다른 컴포넌트에 미치는 영향 검토
6. 요구사항 대비 구현 완성도 평가
7. **[v2.0 신규]** 데이터 모델 변경이 포함된 경우:
   - `pytest workers/{name}/tests/test_contract.py` 직접 재실행
   - `vitest run tests/integration/` 직접 재실행
   - `shared/schemas/` 파일 변경 이력 검토 (CODEOWNERS 리뷰)
   - schema_contract verifier의 WARN 항목 직접 확인

---

## 5. 로키 보안 감사 (security 레벨)

critical 레벨 전체 + 다음 추가:

- 인증/인가 로직 변경 여부 확인
- 민감 데이터 로깅/노출 가능성 검토
- **[v2.0 신규]** `shared/schemas/*.schema.json` 무결성 해시 확인
  (스키마 파일의 무단 변경 가능성 배제)
- 의존성 패키지 취약점 스캔

---

## 6. 에러 처리 원칙 (v2.0 신규)

### 데이터 파싱 실패 처리

| 상황 | 처리 방법 | 근거 |
|------|-----------|------|
| Worker 출력 Pydantic 검증 실패 | 예외 발생, Firestore 저장 중단 | 잘못된 데이터 저장 차단 |
| Frontend Zod 파싱 실패 | ZodError 발생 → React Error Boundary 캐치 | 묵음 실패 제거 |
| Error Boundary 트리거 | 사용자에게 에러 메시지 표시 | 투명한 실패 |
| 파싱 실패 이벤트 | Sentry 에러 알람 전송 | 즉시 감지 + 대응 |

**원칙**: "잘못된 데이터의 묵음 표시"는 허용하지 않습니다.
         파싱 실패는 명시적으로 실패해야 합니다.

---

## 변경 이력

| 버전 | 일시 | 변경 내용 |
|------|------|-----------|
| 1.0 | (이전) | 셀프 QC 5항목, qc_verify.py 4개 verifier |
| 2.0 | 2026-03-04 | 셀프 QC 조건부 구조화, schema_contract verifier 추가, 인터페이스 계약 관리 섹션 신설, 에러 처리 원칙 명시 |
```

---

### 리스크 및 완화 방안

| 리스크 | 발생 가능성 | 영향도 | 완화 방안 |
|--------|------------|--------|----------|
| Agent가 BaseWorker를 상속하지 않고 Worker를 만듦 | 중 | 중 | schema_contract.py SC-1(models.py 존재 검증)으로 탐지 |
| Pydantic 모델과 실제 저장 로직이 분리되어 검증 우회 | 중 | 높음 | test_contract.py의 `validated_output()` 사용 케이스 + BaseWorker 강제 |
| JSON Schema와 Zod 스키마 드리프트 | 중 | 높음 | schema_contract.py SC-7(sample 변경 + Zod 미변경 WARN), 계약 테스트 FAIL |
| schema_contract.py 자체 버그로 잘못된 PASS | 낮음 | 높음 | test_schema_contract_verifier.py (verifier 자체 테스트) |
| "해당 없음" 남용으로 6번 항목 우회 | 중 | 중 | git diff 기반 자동 판단 + qc_verify.py 자동 감지 |
| shared/schemas/ 소유권 분쟁 | 낮음 | 중 | CODEOWNERS + schema_change_summary PR 템플릿 |
| Firestore 에뮬레이터 E2E 무한 지연 | 높음 | 낮음 | Phase 1~3의 계약 테스트로 핵심 가치 확보, E2E는 부가 가치 |
| 새 Worker에 계약 파일 누락 | 높음 | 높음 | SC-1~SC-3 FAIL + BaseWorker 추상 속성 강제 |
| 런타임 Zod 파싱 성능 문제 | 낮음 | 낮음 | 실측 후 결정. 사전 비활성화 불가 원칙 |

---

## 전체 미팅 시리즈 결론

### 3회 미팅이 도달한 핵심 결론

**미팅 1 (현황 진단)**: 근본 원인은 개인의 주의 부족이 아닌, QC 파이프라인이 "컴포넌트 완성도"만 검증하고 "컴포넌트 간 인터페이스 계약"을 검증하지 않는 구조적 공백이었다.

**미팅 2 (개선안 설계)**: JSON Schema를 SSoT로 하는 Pydantic + Zod 이중 검증 체계, 경량 계약 테스트 패턴, 4단계 구현 로드맵 확정.

**미팅 3 (최종 확정)**: Devil's Advocate 검토를 통해 5가지 치명적 약점 발견 및 보완:
1. Pydantic 모델 강제화 → BaseWorker 추상 클래스로 구조적 강제
2. Zod+JSON Schema 드리프트 → Pydantic을 진정한 SSoT로, JSON Schema를 자동 파생 아티팩트로 격하
3. 계약 테스트 우회 가능성 → sample.json 분리(normal/edge) + CODEOWNERS + verifier 자동 감지
4. SSoT 자체의 취약성 → Pydantic ↔ JSON Schema 필드 비교 자동 검증
5. QC 규칙 실효성 → 조건부 체크리스트 구조 + qc_verify.py 자동 감지

### 최종 아키텍처 (확정)

```
[진정한 SSoT]
workers/{name}/models.py  (Pydantic v2 모델)
        │
        │ make export-schema (자동 생성)
        ▼
shared/schemas/{name}.schema.json  (파생 아티팩트, CODEOWNERS: 마아트)
        │                     │
        │                     │ 기반으로 수동 작성
        │                     ▼
        │            shared/schemas/{name}.schema.ts  (Zod 스키마 + camelCase transform)
        │
        │ jsonschema 검증
        ▼
shared/schemas/{name}.sample.normal.json  (정상 케이스)
shared/schemas/{name}.sample.edge.json   (엣지 케이스)
        │
        ├── pytest test_contract.py  (Python 계약 테스트)
        └── vitest keyword-contract.test.ts  (TypeScript 계약 테스트)
                │
                └── CI 게이트: FAIL → PR merge 블락
                             WARN → 리뷰 필요

[QC 자동화]
qc_verify.py
    → api_health, file_check, data_integrity, test_runner (기존)
    → schema_contract (신규, workers/ 자동 감지)
            → SC-1 ~ SC-8 항목 검증
            → FAIL: 계약 파일 누락, 필드 불일치, sample 검증 실패
            → WARN: 드리프트 의심, v1 문법 사용
```

### Phase별 기대 효과

| Phase | 완료 시 달성 효과 |
|-------|-----------------|
| Phase 1 (이번 주) | Worker 잘못된 키 저장 즉시 차단 + Frontend 잘못된 키 즉시 에러 (이번 사고 재발 방지) |
| Phase 2 (다음 주) | PR 시 계약 위반 자동 감지 (사전 예방) |
| Phase 3 (2~3주) | QC 파이프라인 완전 자동화 (qc_verify.py 계약 검증 포함) |
| Phase 4 (1개월) | Firestore 실제 저장 경로까지 커버하는 완전한 E2E |

### 미미르의 최종 아키텍처 평가

> "미팅 3의 Devil's Advocate 검토가 미팅 2 설계의 가장 중요한 약점을 잡아냈습니다. JSON Schema를 SSoT로 두는 것과 Pydantic을 SSoT로 두는 것은 표면적으로 비슷해 보이지만, 실제로는 근본적 차이가 있습니다. Pydantic이 SSoT가 되면 '진실'이 코드(검증 가능, 테스트 가능)에 있고, JSON Schema가 SSoT가 되면 '진실'이 데이터 파일(검증 필요, 오류 가능)에 있습니다. 코드로서의 진실이 더 강합니다. 최종 설계는 Agent 환경에서 실제로 동작할 수 있는 최소한의 복잡도로 최대한의 안전성을 확보합니다."

### 마아트의 최종 QC 평가

> "이번 사고의 교훈을 한 문장으로 정리하면: '도구가 통과시켜 주면 팀 전체가 믿는다.' qc_verify.py가 PASS를 주면 마아트도, 로키도, Agent도 믿습니다. 따라서 qc_verify.py의 schema_contract verifier를 올바르게 구현하고, 그 verifier 자체를 테스트하는 것이 이번 개선의 핵심입니다. 도구를 신뢰하려면 도구를 검증해야 합니다."

### 헤임달의 최종 테스트 전략 평가

> "계약 테스트는 쉽게 우회할 수 있습니다. sample.json을 수정하거나, 테스트 파일을 빈 상태로 두거나. 하지만 BaseWorker가 output_model을 강제하고, schema_contract.py가 test_contract.py 존재 자체를 검증하면, 우회할 수 없는 구조가 됩니다. '테스트를 통과시키는 것'이 아닌 '계약을 유지하는 것'이 진짜 목표임을 구조가 강제합니다."
