개발자는 프로젝트를 진행하면서 컴퓨터 자원, 기술 스택, 데이터 저장 방식, 시스템 구성, 컴포넌트 간 통신 방식 등 다양한 결정을 마주한다.
시스템 구조와 장기적인 개발 방향에 중요한 영향을 주는 결정을 아키텍처 결정(Architecture Decision)이라고 한다.
이러한 결정을 내리게 된 배경과 근거, 그 결정으로 인해 발생하는 결과를 문서로 기록하는데, 이 문서를 ADR(Architecture Decision Record)이라고 한다.
모든 사소한 구현 결정을 ADR로 기록할 필요는 없다. 주로 나중에 변경하기 어렵거나, 여러 개발자 및 시스템 구성요소에 영향을 주거나, 비용과 일정에 큰 영향을 미치는 중요한 결정을 ADR로 기록하면 된다.
AD는 ‘결정 그 자체’, ADR은 ‘그 결정을 기록한 문서’, ADL은 ‘ADR을 모아놓은 기록’으로 설명할 수 있겠다.
ADR의 핵심은 '해당 선택을 왜 했는가'에 있다.
선택의 근거를 명시하면 의사결정 과정의 투명성과 추적 가능성을 높일 수 있다. 다만 근거를 기록한다고 해서 결정의 합리성이나 정확성이 자동으로 보장되는 것은 아니다.
대신 당시 어떤 상황과 제약조건에서 결정이 내려졌는지를 확인할 수 있기 때문에, 이후 그 결정이 적절했는지를 검토하기가 쉬워질 수 있다.
예를 들어 프로젝트에서 특정 데이터베이스를 선택했다고 하자. 시간이 지나면 팀원들은 해당 데이터베이스를 사용하고 있다는 사실은 알고 있어도, 왜 다른 데이터베이스가 아닌 그것을 선택했는지는 기억하지 못할 수 있다.
ADR에 다음과 같은 내용이 남아 있다면 결정의 맥락을 쉽게 이해할 수 있다.
더 나아가, 새로운 팀원이나 미래의 유지보수 담당자가 ADR을 읽게 된다면 현재 시스템의 아키텍처가 어떤 이유로 구성되었는지 쉽게 파악할 수 있을 것이다.
즉, 이미 논의했던 문제를 새로운 팀원이 다시 처음부터 검토하는 일을 줄일 수 있고, 과거의 결정을 무의식적으로 되돌리는 것도 방지할 수 있다는 장점이 있다.
프로젝트 저장소에 ADR 폴더를 만들고, 그 안에 Markdown 형식의 파일을 만들어 Git에 함께 커밋할 수 있다.
일반적으로 docs/adr와 같은 경로를 사용한다.
project/
├── src/
├── tests/
└── docs/
└── adr/
├── 0001-use-ros2.md
├── 0002-use-px4.md
└── 0003-use-state-machine.md
ADR에 번호를 붙이는 방식은 필수는 아니지만, 문서를 작성된 순서대로 관리하고 관련 ADR을 서로 참조할 때 유용하다.
번호를 붙이지 않는다면 다음처럼 결정의 내용을 나타내는 파일명을 사용할 수도 있다.
choose-database.md
use-message-queue.md
manage-passwords.md
handle-exceptions.md
파일명은 문서의 내용을 쉽게 알 수 있도록 간결하게 작성하는 것이 좋다. 소문자와 하이픈을 사용하면 운영체제나 개발 환경에 따른 파일명 차이도 줄일 수 있다.
ADR을 Git에 함께 관리하면 다음과 같은 장점이 있다.
Michael Nygard의 ADR 템플릿은 구조가 간단하여 소규모 프로젝트나 ADR을 처음 도입하는 팀에서 사용하기 좋다.
# 제목
## Status
## Context
## Decision
## Consequences
어떤 결정을 다루는 문서인지 명확하게 나타낸다.
제목은 단순한 주제보다 결정의 내용을 드러내는 문장으로 작성하는 것이 좋다.
데이터베이스 선택(X) --> 사용자 데이터 저장소로 PostgreSQL을 사용한다(O)
현재 ADR의 상태를 나타낸다.
일반적으로 다음과 같은 상태를 사용한다.
조직이나 프로젝트의 운영 방식에 따라 Draft, Under Review, Approved 등의 상태를 추가해서 사용하기도 한다.
예:
## Status
Accepted — 2026-08-05
어떤 문제와 상황 때문에 이 결정을 내려야 하는지를 작성한다.
문제의 배경과 더불어, 결정에 영향을 미치는 기술적·조직적 제약조건과 요구사항을 함께 기록한다.
Context에서는 다음 질문에 답할 수 있어야 한다.
예:
## Context
드론의 임무는 Takeoff, Search, ConfirmTarget,
ApproachTarget, Hold, RTL의 여러 단계로 구성된다.
각 단계의 완료 조건과 실패 조건이 서로 다르기 때문에
현재 임무 상태를 명확하게 관리할 수 있는 구조가 필요하다.
검토한 대안은 다음과 같다.
- 단순한 if-else 조건문
- 유한 상태 머신
- Behavior Tree
최종적으로 어떤 설계 방식이나 변경 사항을 선택했는지 명확하게 작성한다. Decision의 핵심은 ‘무엇을 선택했는가’를 선언하는 것이다.
검토한 모든 대안의 상세한 장단점을 Decision에 전부 작성하기보다는, Context 또는 별도의 대안 비교 항목에 정리하도록 한다.
예:
## Decision
초기 버전의 드론 임무 제어 구조로
유한 상태 머신을 사용한다.
현재 임무 단계가 명확하게 정의되어 있고,
상태별 실패 조건과 안전 동작을 분리해 테스트할 수 있기 때문이다.
Decision은 모호한 표현보다 현재형의 단정적인 문장으로 작성하는 것이 좋다.
유한 상태 머신을 사용할 예정이다.
보다는
임무 제어에 유한 상태 머신을 사용한다.
와 같이 작성한다.
이 선택으로 인해 발생하는 영향을 분석한다.
결정이 도입되었을 때 무엇이 쉬워지고 무엇이 어려워지는지, 긍정적인 영향과 부정적인 영향은 무엇인지를 포함한다.
다음과 같은 내용을 함께 기록할 수 있다.
예:
## Consequences
긍정적인 영향:
- 현재 임무 상태를 명확하게 확인할 수 있다.
- 상태별 성공 및 실패 조건을 분리할 수 있다.
- 상태 전환에 대한 테스트를 작성하기 쉽다.
부정적인 영향:
- 상태가 많아지면 전환 조건이 복잡해질 수 있다.
- 임무를 동적으로 조합하는 데에는 한계가 있다.
후속 작업:
- 각 상태의 진입 조건과 종료 조건을 정의해야 한다.
- 상태별 timeout을 정의해야 한다.
- 비정상 상황에서 Hold와 RTL 중 어떤 상태로 전환할지 결정해야 한다.
너무 큰 주제를 하나의 문서에 모두 넣지 않는 것이 좋다.
예를 들어 드론 전체 시스템 구조 결정이라는 하나의 ADR에 모든 내용을 넣기보다는 다음과 같이 서로 독립적으로 검토할 수 있는 개별 결정으로 나누어 문서화한다.
하나의 결정이 다른 결정과 밀접하게 관련되어 있다면, 하나의 문서에 모두 합치는 대신 별도의 ADR로 작성한 뒤 서로 링크하는 것이 좋다.
최종 선택을 선택한 이유뿐만 아니라, 현실적으로 검토했던 다른 대안과 각 대안을 제외한 이유도 기록한다.
예:
대안 A: 단순 if-else 구조
- 구현은 빠르지만 상태가 증가할수록 조건문이 복잡해진다.
대안 B: 유한 상태 머신
- 현재 정의된 임무 단계를 명확하게 표현할 수 있다.
대안 C: Behavior Tree
- 확장성은 높지만 현재 프로젝트 규모에 비해 학습 및 구현 비용이 크다.
모든 가능한 기술을 조사할 필요는 없고, 당시 현실적으로 채택 가능했던 주요 대안을 기록하면 된다.
최종 선택으로 얻게 되는 장점뿐만 아니라, 그 선택 때문에 감수해야 하는 단점과 위험도 함께 작성한다. 즉, 결정으로 인해 발생하는 trade-off를 솔직하게 기록해야 한다는 것.
예:
장점:
- 상태 전환 과정을 명확하게 추적할 수 있다.
- 상태별 안전 조건을 분리할 수 있다.
단점:
- 상태의 수가 증가하면 전환 규칙이 복잡해질 수 있다.
- 동적으로 임무를 조합하기는 어렵다.
ADR은 당시의 상황과 판단을 기록한 문서다. 시간이 지나 결정이 잘못되었다고 판단되거나 상황이 바뀌더라도, 과거의 ADR 내용을 현재의 기준에 맞게 조용히 덮어쓰면 안된다.
결정이 변경되었다면 다음과 같은 방법을 사용할 수 있다.
예:
## Status
Superseded by ADR-0012 — 2026-10-20
이렇게 관리하면 단순히 현재의 결정만 알 수 있는 것이 아니라, 시간이 지나면서 아키텍처가 어떤 이유로 변화했는지까지 추적할 수 있다.
모든 구현 세부사항을 ADR로 작성하면 문서의 수가 지나치게 많아지고, 정작 중요한 결정을 찾기 어려워질 수 있다.
다음과 같은 결정은 ADR 작성 대상으로 고려할 수 있다.
반면,
과 같은 세세한 내용은 ADR로 작성하지 않아도 된다.
ADR은 작성자 자신만 보는 개인 메모가 아니다. 미래의 팀원과 유지보수 담당자도 읽을 수 있도록 작성해야 한다. 프로젝트 내부에서만 사용하는 약어와 맥락을 지나치게 생략하지 않는 것이 좋다. 필요한 경우 관련 요구사항, 이슈, 코드, 다이어그램의 링크를 추가한다.
예:
## Related Artifacts
- 요구사항: docs/requirements.md
- 상태 전환도: docs/mission-state-diagram.drawio
- 구현 코드: src/mission_manager.py
- 테스트: tests/test_state_transition.py
문서를 길게 작성하는 것보다 다음 질문에 명확하게 답하는 것이 중요하다.
# ADR-0003: 임무 제어에 유한 상태 머신을 사용한다
## Status
Accepted — 2026-08-05
## Context
드론은 Takeoff, Search, ConfirmTarget,
ApproachTarget, Hold, RTL의 여러 단계를 수행한다.
각 단계의 성공 조건과 실패 조건이 다르기 때문에
현재 임무 상태와 상태 전환 조건을 명확하게 관리해야 한다.
검토한 대안은 다음과 같다.
- 단순 if-else 구조
- 유한 상태 머신
- Behavior Tree
단순 if-else 구조는 초기 구현은 쉽지만,
상태가 증가할수록 조건문이 복잡해질 가능성이 높다.
Behavior Tree는 임무 확장에 유리하지만,
현재 프로젝트 규모에 비해 구현 및 학습 비용이 크다.
## Decision
초기 버전의 임무 제어 구조로 유한 상태 머신을 사용한다.
각 임무 단계를 독립적인 상태로 정의하고,
성공 조건, 실패 조건 및 안전 전환 조건을 명시한다.
## Consequences
긍정적인 영향:
- 현재 임무 상태를 명확하게 추적할 수 있다.
- 상태별 성공 및 실패 조건을 테스트할 수 있다.
- 센서 오류나 통신 장애 발생 시 안전 전환을 명시할 수 있다.
부정적인 영향:
- 상태의 수가 증가하면 상태 전환 규칙이 복잡해질 수 있다.
- 동적으로 임무를 조합하는 기능에는 한계가 있을 수 있다.
후속 작업:
- 상태별 timeout을 정의한다.
- Hold와 RTL 전환 조건을 정의한다.
- 상태 전환 로그를 구현한다.
ADR은 당시 어떤 상황에서 어떤 대안을 검토했고, 왜 해당 결정을 내렸으며, 그 결정으로 인해 무엇이 달라지는지를 기록하는 문서다.
코드에는 의사결정의 결과만 남지만, ADR은 코드가 놓치는 의사결정의 맥락을 보존하는 역할을 한다.
요약: ADR은 중요한 아키텍처 결정의 내용과 이유, 그리고 그 결과를 미래의 개발자에게 전달하기 위한 결정 기록 문서
https://github.com/architecture-decision-record/architecture-decision-record