학습 목표
ADR(Architecture Decision Record)가 무엇을·언제 남기는지 설명할 수 있다.
Context·Decision·Consequences·Alternatives 섹션을 실무 템플릿으로 작성할 수 있다.
Status(Proposed / Accepted / Superseded 등) 라이프사이클을 관리할 수 있다.
C4·API 계약과 ADR이 역할을 나누는 방식을 설명할 수 있다.
문제 상황
- 2년 전 Kafka 도입 — "왜 RabbitMQ가 아닌가?" 슬랙 400줄 스크롤 후 포기
- Outbox 테이블 추가 PR — 리뷰어 "이거 왜?" — ADR 없음, 작성자 퇴사
- Confluence "아키텍처 원칙" 12페이지 — 현재 코드와 불일치, 누가 갱신할지 모름
- MSA 전환 롤백 논의 — "당시 제약이 뭐였지?" — 회의록만 있고 결정 근거 없음
- Supersede 없이 세 번째 API Gateway 도입 — 이전 결정이 Accepted로 남아 혼란
- C4 Container는 있는데 왜 Payment가 동기 PG 호출인지 문서 없음
앞서 구조(C4)·계약(OpenAPI)을 정리했다. 왜 그렇게 했는지는 별도 기록이 필요하다.
1. ADR이란
ADR — 아키텍처에 영향 있는 결정을 짧은 문서로 영구 보존.
| C4 / API 계약 | ADR |
|---|---|
| 무엇이 연결되는가 | 왜 그렇게 연결했는가 |
| 현재 구조 스냅샷 | 당시 맥락·대안·트레이드오프 |
| 변경 시 다이어그램 diff | 결정 역사·폐기 이유 |
- Michael Nygard — "Documenting Architecture Decisions" (2011)에서 가볍게 한 장 권장
- Living documentation — ADR도 repo
docs/adr/에 버전 관리 - 코드 = 진실이지만 의도·거절한 길은 코드만으로 복구 어렵다
2. 언제 ADR을 쓰는가
| 쓴다 | 안 쓴다 (과도) |
|---|---|
| 서비스 분해 경계·통합 방식 (REST vs 이벤트) | 변수명·한 메서드 리팩터 |
| 데이터 소유·일관성 모델 (outbox, CDC) | 프레임워크 기본 convention |
| 브로커·캐시·DB 도입·교체 | 팀 이미 합의한 코딩 스타일 |
| 품질 속성 트레이드오프 (latency vs consistency) | 버그 fix 한 건 |
| 보안·규제 아키텍처 (PII 경계, 암호화) | |
| 되돌리기 어려운 결정 |
- 비용이 큰 결정일수록 먼저 — 구현 후 쓰면 맥락 소실
- 앞서 스타일 선택 플로우 마지막 단계 "Record ADR"와 동일 습관
3. ADR 구조 (Nygard / MADR)

| 섹션 | 내용 |
|---|---|
| Title | 결정 한 줄 — Use transactional outbox for order events |
| Status | Proposed → Accepted / Rejected / Deprecated / Superseded |
| Date | 결정일 (Accepted 기준) |
| Deciders | 책임 팀·역할 |
| Context | 문제·제약·품질 속성 — 가장 길게 |
| Decision | 선택한 것 — 명령형 한두 문단 |
| Consequences | 긍정 / 부정 / 중립 |
| Alternatives | 검토했으나 채택 안 한 옵션·이유 |
- Context 없이 Decision만 — "왜" 재현 불가
- Alternatives 없으면 — 나중에 "처음부터 이거밖에 없었나?" 의심
- MADR 등 메타 필드(
related,tags)는 팀 규모에 맞게 추가
4. Status 라이프사이클

| Status | 의미 |
|---|---|
| Proposed | 리뷰 중 — 아직 팀 합의 전 |
| Accepted | 적용·준수 기준 |
| Rejected | 검토 후 채택 안 함 — 이유 남김 |
| Deprecated | 더 이상 권장하지 않음 — 대체 없이 사라짐 |
| Superseded | 새 ADR이 대체 — Superseded by ADR-0007 링크 |
ADR-0003 Accepted (modular monolith)
│
▼ superseded
ADR-0012 Accepted (split Order service)
- 삭제하지 않는다 — 역사가 자산
- Supersede 시 양방향 링크 — 구 ADR에 by, 신 ADR에 replaces
- 분기마다 Accepted ADR 목록 점검 — 현재 구조와 불일치 찾기
5. 대안 비교를 ADR에 넣기
결정 전 표·다이어그램으로 대안을 짧게 비교한다.

| 기준 | Dual write | Outbox | CDC primary |
|---|---|---|---|
| DB·이벤트 원자성 | ✗ | ✓ | △ (지연) |
| 운영 복잡도 | 낮음 | 중간 | 높음 |
| 도메인 이벤트 표현 | ✓ | ✓ | △ (row change) |
| 앞서 incident 회피 | ✗ | ✓ | △ |
- 정답 표가 아니라 당시 우선순위 반영
- C4 Container에 Kafka가 있어도 outbox vs CDC는 ADR 없으면 재논의 반복
6. 예제 ADR
주문·이벤트 전파 — 앞서 dual write·outbox·CDC 맥락을 한 장으로.
# ADR-0001: Use transactional outbox for order events
- **Status:** Accepted
- **Date:** 2026-07-08
- **Deciders:** order-team, platform-architecture
- **Related:** C4 Container (Order API, Kafka), event catalog `order.events`
## Context
Order API must persist an order and publish `OrderPlaced` to Kafka so inventory and notification can react.
Dual write (DB commit then Kafka publish in application code) caused production incidents:
- DB committed, publish failed → inventory never reserved
- Publish succeeded, DB rolled back → ghost events in downstream projections
We need atomicity between the order row and the outbox row in the same PostgreSQL transaction. CDC from WAL is viable for read-model sync but does not replace an explicit domain event at write time for this flow.
Constraints:
- Team already runs PostgreSQL and Kafka
- Target at-least-once delivery with idempotent consumers
- No XA / 2PC across services
## Decision
Use the **transactional outbox** pattern in Order API:
1. In the same DB transaction: `INSERT orders` + `INSERT outbox_events`
2. A relay process polls `outbox_events` with `FOR UPDATE SKIP LOCKED` and publishes to Kafka
3. Mark outbox row `published` after broker ack (or rely on idempotent publish + status column)
Event schema and topic ownership stay in the provider contract (`order.events`, AsyncAPI).
## Consequences
### Positive
- Atomicity without distributed transactions
- Replay from outbox table on relay failure
- Aligns with DB-as-source-of-truth and eventual consistency SLO
### Negative
- Extra table, relay job, and monitoring
- Slight publish latency (poll interval, typically sub-second to few seconds)
- Consumers must be idempotent (at-least-once)
### Neutral
- CDC can still feed search/analytics from `orders` table later; not mutually exclusive
## Alternatives considered
| Option | Why not (for now) |
|--------|-------------------|
| Dual write in service | No atomicity; already caused incidents |
| Kafka EOS transaction only | Does not tie DB commit to event; scope limited |
| CDC as primary publish | Row-level change, not domain `OrderPlaced`; schema coupling to table |
| 2PC / XA | Ops and availability cost; avoided in MSA |
## Follow-up
- Add outbox relay dashboard (lag, failed rows)
- Document `OrderPlaced` in event catalog and ADR-0002 if we add CDC for catalog search
| 작성 팁 | |
|---|---|
| Context에 숫자 | "publish 실패 월 3건", "p99 800ms" |
| Decision은 짧게 | 구현 세부는 링크 (PR, RFC) |
| Follow-up | 미완 액션 — 다음 ADR·티켓 |
7. 저장소·워크플로
docs/
c4/
02-containers.md
adr/
0001-use-transactional-outbox.md
0002-cdc-for-catalog-search.md
README.md # index + status table
api/
openapi-order-snippet.yaml
| 습관 | 효과 |
|---|---|
| 번호 0001 순차 | 충돌·참조 쉬움 |
| 구조 변경 PR에 ADR 필수 | 왜 리뷰 |
docs/adr/README.md 표 |
Accepted 한눈 |
| Confluence 복사 금지 | repo가 single source |
PR 체크
| 질문 | |
|---|---|
| 새 통합·데이터 소유 변경? | ADR 필요 |
| 기존 ADR과 모순? | Supersede 또는 수정 |
| C4·계약 갱신? | ADR Related 링크 |
8. 안티패턴
| 안티패턴 | 대안 |
|---|---|
| Wiki 장문 한 편 | 결정당 짧은 ADR |
| Accepted 영원 | Superseded·Deprecated 관리 |
| 대안 생략 | 최소 2개 거절 이유 |
| 구현 후 기억 작성 | Proposed로 먼저 |
| ADR = 설계 승인 만 | Rejected도 남김 — 재논의 절약 |
| 영문/한글 혼재 무질서 | 팀 한 언어 고정 (예제는 영문 라벨 유지) |
9. C4·계약·ADR 함께 쓰기
| 산출물 | 답하는 질문 |
|---|---|
| C4 Context | 누가 무엇과 연동? |
| C4 Container | 어떤 배포 단위·데이터? |
| OpenAPI / AsyncAPI | 표면 스키마·버전? |
| ADR | 왜 이 경계·패턴·기술? |
- Container 다이어그램 캡션에
See ADR-0001— 탐색 용이 - 장애 RCA 후 "결정 가정이 틀렸다" → 신규 ADR 또는 Supersede
10. 실무 체크리스트
| 질문 | 좋은 답 |
|---|---|
| 몇 장? | 1~2 페이지, Context 밀도 높게 |
| 누가 씀? | 제안자 + 아키텍트 리뷰 |
| 언어? | 팀 합의 (코드·계약과 동일 권장) |
| 도구? | Markdown in git, adr-tools·Log4brains 선택 |
| 운영·합의 프로세스? | 본 편은 작성법 — 심화는 후속 주제 |
11. 정리
- ADR — 맥락·대안·결과로 설계 의도 보존
- Status — Proposed → Accepted → Superseded 역사 유지
- C4·계약과 분업 — 구조 vs 이유
- repo
docs/adr/— PR·리뷰와 연동 - Part 6 업무 — 다음은 아키텍처 평가
다음에 다룰 것
- 아키텍처 평가
- ATAM, 품질 시나리오, 기술 부채 관리
해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford) 및 Documenting Architecture Decisions (Michael Nygard) 의 내용을 기반으로 합니다.
'소프트웨어 아키텍처' 카테고리의 다른 글
| ADR 실전 (0) | 2026.07.10 |
|---|---|
| 아키텍처 평가 (0) | 2026.07.09 |
| 아키텍처 문서화 (0) | 2026.07.07 |
| 이벤트 스트리밍과 CDC (0) | 2026.07.06 |
| 캐싱 전략 (0) | 2026.07.05 |