드래프트 표준: DraftIR ↔ DraftVariant 불변성 아키텍처
🗓️ 9/20/2025 👤 Role: Backend Developer
FastAPIContextPropagationImmutableDesign
Problem & Condition
Definition of Problem Context
마케팅 팀이 캠페인을 진행하면서 게시하는 여러 콘텐츠는 같은 시간대에 같은 토픽을 공유한다.
하지만 플랫폼 정책 제약, 각 플랫폼의 톤앤매너, 표현 방식의 차이 때문에 완전히 동일한 콘텐츠를 그대로 쓸 수는 없다.
그럼에도 불구하고 이들 콘텐츠는 분명히 같은 맥락을 공유한다. 따라서 단일 진실 공급원(Single Source of Truth, SoT)을 정의하고, 이를 표준화하는 구조가 필요하다.
만약 이러한 경계가 없다면,
- 사용자 입력(Draft)이 시스템 내부 처리 과정에서 변조되거나,
- 플랫폼별 변환 과정이 추적 불가능해져,
- 누가 무엇을 수정했는지 책임소재가 불분명해진다.
이는 운영 안정성과 회귀(traceability)를 크게 해치게 된다.
Definition of Solution DTOs and Relationships
이를 해결하기 위해, PostgreSQL 스키마 설계와 RESTful API, AsyncIO, Celery 워커 등 모든 경로에서 In-Out Schema 불변성을 유지해야 한다.
따라서 필요한 요건은 다음과 같다:
- Celery와 AsyncIO 간 Context 전파
(X-Request-ID,X-Idempotency-Key,X-Trace-Parent등) - IR(DTO) 스키마 정의
사용자 입력과 시스템 산출물을 구분 - Injector/Policy 정의
다양한 컨텍스트(페르소나, 트렌드, 플레이북 등)를 안전하게 주입
Solution
DTOs and Responsibilities
-
DraftIR
- 범 플랫폼적 DTO
- 오직 사용자 Action에 의해 Write되며, 내부 시스템은 수정 불가
-
DraftVariant
- Adapter에 의해 생성되는 결과물
- 플랫폼별 Rendered Blocks와 불변 필드를 포함
- 사용자는 직접 수정할 수 없음
즉,
- 사용자 책임: DraftIR 작성 및 CRUD
- 시스템 책임: DraftVariant 컴파일 및 영속화
이렇게 경계를 분리하면,
- User Action은 DraftIR CRUD 기록에서 항상 확인 가능
- 최종 렌더링 결과는 DraftVariant에서 추적 가능
→ 결과적으로 책임소재와 불변성이 명확히 보장된다.
Responsibilities of Injectors and Adapters
-
Adapter
- 추상 클래스
- 각 플랫폼별 어댑터는 고유한 규칙에 따라 DraftVariant를 생성
- 컴파일러로서 최소한의 변환 책임만 수행
- Service Layer에서 결과는 영속화
-
Injector
InjectorContext내에서 연쇄적으로 적용finalize()시점에InjectedResult스키마 생성- Adapter로 전달할 컨텍스트를 준비하되, 최종 결과에는 직접 관여하지 않음
시각적으로 표현하면:
InjectorContext(Injector1 -> Injector2 -> ...) -> Adapter -> DraftVariant(RenderedBlocks)
Context Propagation 경로는 다음과 같다:
Service Layer -> Capture Context -> Celery Worker -> Apply Context -> Adapter -> Persist
Effects
-
Context Propagation
- Injector와 Adapter는 모두
ContextVars기반 캡처/복원 방식을 사용 X-Request-ID,X-Trace-Parent,X-Idempotency-Key등이 end-to-end로 보존
- Injector와 Adapter는 모두
-
독립적 주입 (Isolation)
- 각 Injector는 독립적으로 작동 → 부작용 최소화
- Context 주입 충돌을 예방
-
책임소재 분리 (Accountability)
- DraftIR = 사용자 입력의 SoT
- DraftVariant = 시스템 산출물의 SoT
- 두 레이어 사이 불변성 보장
-
안정성 & 운영성
- Adapter.compile은 항상 Celery Worker에서 실행
- 멱등키 기반 재시도/회로 차단기 적용 가능
- 추후 장애 추적과 롤백 용이
Conclusion
DraftIR ↔ DraftVariant의 경계를 명확히 하면,
- 사용자 입력은 시스템이 손대지 않는다
- 시스템 결과물은 사용자 손을 타지 않는다
이 단순한 원칙 덕분에 콘텐츠 파이프라인 전반이 추적 가능하고 안정적이며, 운영 비용이 낮은 구조로 유지된다.