결정 기록 (ADR)
ADR(Architecture Decision Record) 은 되돌리기 어려운 설계 결정과 그 근거를 한 장씩 남기는 기록입니다. 시스템 구조 자체는 아키텍처 개요에, 구현 방법은 각 가이드 문서에 있습니다. 여기는 “왜 이렇게 됐는가” 전용입니다.
왜 필요한가
근거가 코드·Jira·PR 설명에 흩어져 있으면 폐기된 결정이 살아남습니다. 실제로 겪은 사례:
- native PDF 렌더러 관련 “이 라이브러리 금지” 판단이 PR #5409에서 뒤집혔지만, 뒤집혔다는 사실이 어디에도 남지 않아 이후 작업의 전제가 틀어짐
- OTA 업데이트 정책이 Notify → Immediate 로 바뀌었는데, 옛 전제(“도달이 느리다”)로 장애를 오진할 뻔함
- 카페 첨부 장애의 CloudFront CORS 가설이 기각됐는데 기각 사실이 코드에 없어 같은 가설을 다시 세울 위험
ADR의 Superseded 상태가 정확히 이 문제를 해결합니다. 결정을 지우지 않고, 뒤집혔다는 사실과 함께 남깁니다.
목록
| # | 제목 | 상태 |
|---|---|---|
| 0001 | API 백엔드를 코어 API와 front-api BFF 둘로 나눈다 | Accepted |
| 0002 | snake_case → camelCase 변환을 fetcher 경계에서 한 번만 한다 | Accepted |
| 0003 | 관측·기능플래그 SDK는 공유 패키지 facade로만 쓴다 | Accepted |
| 0004 | native 간편인증을 PUSH 대신 App2App 직접 호출로 전환한다 | Accepted (Amended by ADR-0005) |
| 0005 | eziok App2App은 userAgent와 deviceBrowser를 함께 보내야 켜진다 | Accepted |
| 0006 | 정비 뉴스 수집을 스케줄 배치 파이프라인으로 두고 N→M 토픽 클러스터로 요약해 admin/news-brief에 적재한다 | Accepted |
| 0007 | devtools 빌드 코드를 공개 번들에서 물리적으로 제외한다 (DCE + Metro resolver) | Accepted |
| 0008 | 간편인증 표준창 자동 채우기는 BFF에서만 조립하고, 평문은 라우트를 벗어나지 않는다 | Accepted (Amended by ADR-0012) |
| 0009 | 뉴스 브리프 후속 링크를 엔티티 인덱스 + precision-first LLM 판정으로 붙인다 | Accepted |
| 0010 | OS 앱 아이콘 배지를 앱에서 알림함 미읽음 + 채팅 미읽음으로 합산 계산한다 | Accepted |
| 0011 | Expo SDK 56 → 57 로 업그레이드해 Xcode 27(Swift 6.4) 빌드를 지원한다 | Accepted |
| 0012 | 간편인증 결과 수신을 표준창에서 간편인증-API(서버 주도)로 이전한다 | Accepted |
언제 쓰는가
모든 PR마다 쓰지 않습니다. 아래 중 하나라도 해당하면 한 장 남기세요.
- 되돌리는 데 하루 이상 걸리는 결정 (외부 의존성 도입·교체, 데이터 소유 위치, 인증 흐름 변경)
- 대안이 실제로 있었고 그중 하나를 골랐을 때
- 나중에 누군가 “왜 이렇게 안 했지?” 라고 물을 게 뻔한 선택 (= 직관에 반하는 선택)
- 기존 ADR을 뒤집을 때 (새 ADR을 쓰고 옛 것을
Superseded로 표시)
반대로 이런 건 쓰지 마세요 — 컨벤션·스타일 선택, 자명한 버그 수정, 되돌리기 쉬운 것, 이미 다른 문서가 설명하는 구현 방법.
쓰는 법
template을 복사해content/decisions/NNNN-짧은-영문-슬러그.mdx로 만든다 (번호는 다음 순번)Status/Context/Decision/Consequences를 채운다- 근거로 실제 티켓 번호와 측정값을 인용한다. 인용 없는 ADR은 의견이지 기록이 아니다
- ADR은 PR을 만들기 전에 코드와 같은 커밋으로 들어가므로, 그 시점에 이번 작업의 PR 번호는 아직 없습니다. 인용하지 마세요 — 티켓 번호로 충분하고 PR↔Jira는 자동 링크됩니다
- 반면 이미 존재하는 다른 PR(선행 작업, 이 결정을 반증한 PR 등)은 번호로 인용합니다
- 위 목록 표에 한 줄 추가한다
상태
| 상태 | 뜻 |
|---|---|
Proposed | 제안됨, 아직 합의 전 |
Accepted | 채택됨, 현재 유효 |
Accepted (Amended by ADR-NNNN) | 결정은 그대로 유효하지만, 근거·범위 일부가 이후 사실로 정정됨 |
Superseded by ADR-NNNN | 결정 자체가 다른 결정으로 대체됨. 문서는 지우지 않는다 |
Deprecated | 더 이상 유효하지 않으나 대체 결정은 없음 |
기존 ADR을 뒤집을 때는 옛 문서를 수정하지 말고, 상태 줄만 바꾸고 새 문서에서 무엇이 왜 바뀌었는지 설명하세요. 옛 결정의 근거가 남아 있어야 같은 논의를 반복하지 않습니다.
Amended와 Superseded 구분
이 둘을 섞으면 기록이 쓸모없어집니다. 기준은 하나입니다 — 결정을 지금도 따르고 있는가.
Amended | Superseded | |
|---|---|---|
| 결정을 지금도 따르는가 | 예 | 아니오 |
| 무엇이 틀렸나 | 근거·전제·범위의 일부 | 결정 자체 |
| 옛 문서를 읽을 사람에게 | ”이대로 하되, 이 부분은 ADR-NNNN을 함께 보라" | "이대로 하지 마라, ADR-NNNN을 보라” |
Amended를 쓸 때는 반증된 문장을 고쳐 쓰지 마세요. 원문을 그대로 두고 그 자리에 포인터만 덧붙입니다. 틀린 판단이 왜 그럴듯했는지가 남아 있어야 같은 함정을 다시 밟지 않습니다.
왜 이 상태가 필요한가: ADR-0004가 도입 당일 이 상황에 걸렸습니다. App2App 전환이라는 결정은 유효한데, “대안으로 검토한
deviceBrowser변경은 효과가 없다”는 근거를 프로덕션이 부정했습니다(HMH-8978).Superseded로 쓰면 아직 유효한 결정을 폐기된 것처럼 보이게 하고, 그냥 두면 틀린 주장이 남습니다. 그래서 세 번째 칸이 필요합니다.