PRD·ADR·코드를 왜 나눠야 할까 — 추상화 계층 하나만 읽고 판단하기
TL;DR
- 추상화 계층은 구현 디테일의 변화로부터 구현 의도를 보호한다.
- 요구사항을 ADR로 이관한 뒤에는 ADR을 구현 기준으로 삼는다.
- 구현 검토 보고서는 상세 근거에 앞서 계약과 위험을 보여준다.
시작하며
구현에서 선택한 라이브러리나 파일 구조가 바뀌었다고, 무엇을 왜 만들려는지까지 함께 바뀌어서는 안 된다고 생각한다. 그런데 그 세부사항을 기획과 설계 결정에 섞어두면, 코드 수정이 상위 문서의 수정으로 이어지고 어느 내용이 여전히 유효한지 다시 판단하게 된다.
ALPS Writer Plugins에서 추상화 계층을 나눈 목적은 구현 의도가 구현 디테일의 영향을 받지 않도록 하는 것이다. 요구사항이 구현을 이끌고, 구현 방법은 그 요구사항을 만족하는 범위에서 선택할 수 있게 하려는 것이다.
제품 요구사항 문서(PRD), 아키텍처 결정 기록(ADR), 코드를 같은 시스템을 서로 다른 해상도로 본 결과로 구분하는 방식이다. 저장소의 AGENTS.md에도 이 원칙을 적어뒀다.1
여기서 해상도는 얼마나 구체적으로 설명하느냐를 뜻한다. PRD에는 사용자가 얻을 결과를, ADR에는 구현이 지켜야 할 결정과 요구사항을, 코드에는 실제 동작을 담는다. 이 글에서 말하는 계약은 허용할 상태와 권한, 정확한 제한값처럼 구현을 바꿔도 지켜야 할 요구사항이다.
이 글에서 ALPS Writer Plugins는 두 독립 플러그인을 함께 가리킨다. ALPS Writer는 PRD 작성과 기획 인계를, ADR Writer는 ADR 작성과 구현·리뷰·동기화를 맡는다.
직접 적용해보니 Agent가 작업마다 모든 문서를 읽지 않아도 됐다. 코드 리팩터링이 상위 문서 수정으로 번지지 않았고, 구현 방법을 미리 고정하지 않으면서도 사람은 계약과 위험을 검토할 수 있었다.
이 글에서는 ALPS Writer Plugins의 설계를 기준으로, 구현 의도를 보호하는 경계와 그로부터 얻는 효과를 정리한다.
1. 같은 시스템을 세 가지 해상도로 본다
C4 모델은 시스템과 외부의 관계(Context), 내부 애플리케이션과 데이터 저장소(Container), 그 안의 구성 요소(Component)를 차례로 확대해 보여준다. PRD, ADR과 코드도 이와 비슷하게, 같은 시스템을 서로 다른 상세 수준에서 설명한다고 볼 수 있다.
이 그림은 각 문서에 담을 정보의 상세 수준을 Clean Architecture의 동심원 형태로 표현한 개념도다. 실제 호출 순서나 모든 단계에서 PRD를 다시 읽어야 한다는 뜻은 아니다.
가장 안쪽의 ALPS PRD에는 기획 단계의 사용자 문제, 제품 의도와 기능 계약을 담는다. 파일 경로나 기술 목록, 상세 구현 계획은 넣지 않는다.
그 바깥의 ADR은 선택 근거, 대안, 정확한 요구사항과 시스템 경계를 담는다. SDK, 함수 시그니처, 내부 호출 흐름과 튜닝값은 더 바깥의 코드와 테스트에 남긴다.
기획을 구현으로 넘길 때는 필요한 요구사항을 ADR에 빠짐없이 옮긴다. 이관 이후에는 ADR이 구현 기준이며, PRD는 당시의 기획을 남긴 기록이다. 코드는 ADR의 요구사항을 지켜야 하고, PRD를 나중에 고쳤다고 구현 기준이 자동으로 바뀌지는 않는다.
시스템과 외부의 관계를 묻는 그림에 클래스까지 넣으면 자세해지기는 하지만, 필요한 관계를 찾기는 어려워진다. 문서도 마찬가지다. 자기 수준의 질문에 답하는 데 필요 없는 내용은 아래 계층에 남겨두는 편이 낫다.
두 플러그인은 이를 문서 하나만 읽어보는 검사(single-level read test)로 확인한다.
이 계층 하나만 읽고 자신의 질문에 답할 수 있는가? 아래 계층의 내용이 섞이지 않았고, 다른 어느 곳에도 없는 계약이 빠지지 않았는가?
2. Agent가 문서와 코드를 오가며 작업한다
Clean Architecture의 Use Case나 Hexagonal Architecture의 Application Service는 요청을 받아 업무 규칙을 실행하고, 필요한 데이터를 읽거나 저장하는 작업을 조율한다.2 이때 특정 데이터베이스 제품에 직접 맞추기보다, 저장이나 조회에 필요한 동작을 약속한 인터페이스에 의존하게 만든다. 그러면 데이터베이스를 연결하는 코드를 바꿔도 업무 규칙은 유지할 수 있다.
ALPS Writer Plugins에서는 Agent가 비슷한 역할을 맡는다.
Agent는 기획을 넘기는 단계에서 PRD를 읽고, 오래 유지할 결정과 요구사항을 ADR에 옮기며 나머지는 구현자가 고를 수 있는 선택으로 구분한다. 이후 구현할 때는 ADR을 기준으로 현재 코드를 찾는다. 작업 지침인 Skill과 외부 도구를 연결하는 MCP, 명령줄 도구(CLI)를 사용해 코드를 수정하고 테스트한 뒤 검토할 결과를 남긴다.
타겟 그림에서 Agent를 원 안에 넣지 않은 이유도 여기에 있다. Agent는 세 계층을 가로질러 작업하지만 어느 계층의 내용을 영속적으로 소유하지 않는다.
작업을 조율한다는 점은 비슷하지만, Use Case나 Application Service는 코드로 남고 Agent가 만든 작업 계획은 그 실행을 위해 쓰인다. Agent의 계획을 PRD·ADR·코드와 별개의 구현 기준으로 계속 관리하지는 않는다.
끝난 작업의 계획, 검색 결과, 보조 Agent 구성과 중간 검토 자료를 다음 구현의 기준으로 삼지 않는다. 기획의 출발점은 PRD에, 이관한 제품 의도와 요구사항을 포함한 현재 결정은 ADR에, 실제 동작은 코드와 테스트에 남는다. 아직 진행 중인 작업을 이어가기 위한 기록은 그 용도가 끝날 때까지 필요할 수 있다.
다음 실행의 Agent는 현재 ADR과 코드를 읽고 작업 순서를 다시 구성한다. 이전 Agent의 내부 상태나 별도 등록 정보를 복구해야만 요구사항을 알 수 있는 구조는 피한다.
나는 이를 개발 과정에 의존성 역전 원칙을 적용한 것으로 볼 수 있다고 생각한다. 문서를 이해하기 위해 특정 Agent나 플러그인의 내부 상태가 필요한 것이 아니라, 교체 가능한 Agent가 문서에 적힌 요구사항을 읽고 일하기 때문이다.
그래서 plugin을 제거하거나 model을 바꿔도 PRD, ADR과 코드는 그대로 읽을 수 있다. Agent의 실행 방식은 바뀌어도 계약과 검증 결과를 지키면 된다.
Clean Architecture에서 추상화 계층이 복잡도를 추가하듯이 이 구조도 분류 비용이 생긴다. 작은 프로젝트에서는 PRD, ADR과 코드 전체를 나눠 얻는 이득이 크지 않을 수 있다.
3. 질문 하나에 문서 하나만 읽는다
사용자 가입을 왜 만들어야 하는지 알고 싶다면 PRD를 읽으면 된다.
예를 들어 로그인 상태를 이어가는 데 쓰는 갱신 토큰(refresh token)의 유효기간을 왜 7일로 정했는지 알고 싶다면 ADR을 읽는다. 토큰을 실제로 교체하는 로직이나 캐시에 저장할 때 쓰는 키가 궁금할 때만 코드로 내려간다.
각 문서가 자기 질문에 혼자 답하면 Agent도 작업에 필요한 계층만 읽고 멈출 수 있다.
ALPS Writer의 /feature-to-adr는 PRD의 구현 관련 요구사항을 ADR로 이관하는 작업이다. 이관이 끝난 뒤의 일반 구현과 리뷰는 PRD를 다시 읽지 않는다.
ADR 목록인 .mapping.json에는 각 ADR의 경로, 상태, 요약을 기록한다. 선행 관계인 dependsOn은 개별 ADR이 아니라 ADR을 묶은 카테고리 단위로 저장한다. 어떤 결정이 어떤 보장을 먼저 필요로 하는지는 ADR 본문과 검토 자료에서 설명한다.3
PRD 참조나 일반 구현 코드와 ADR을 연결하는 경로 매핑은 두지 않는다.
ADR 본문에도 PRD의 Section 번호, Feature ID, 함수와 파일 경로를 넣지 않는다. 관련 코드는 ADR을 읽은 Agent가 현재 저장소에서 다시 찾는다.
경로를 미리 저장하면 당장은 편하지만 rename과 refactoring 뒤에는 오래된 링크가 된다. 그때부터 Agent는 문서와 검색 결과 중 어느 쪽이 맞는지 다시 판단해야 한다.
반대로 필요한 순간에 검색하면 현재 코드를 기준으로 찾을 수 있다. 읽는 컨텍스트가 줄고, 오래된 하위 계층의 정보가 상위 판단에 끼어드는 일도 줄어든다.
4. 변경이 필요한 계층에서 멈춘다
세 계층의 변경 빈도는 같지 않다.
함수와 모듈은 자주 바뀌고, 아키텍처 결정은 가끔 바뀌며, 사용자 문제와 제품 목표는 상대적으로 오래간다. 이 프로젝트의 Code >> ADR >> PRD는 기획 단계에서 이렇게 변경 빈도가 다르다는 뜻이다. 이관 이후의 구현은 코드와 ADR을 기준으로 진행하며, PRD 수정은 명시적으로 다시 가져올 때만 검토한다.
계층이 잘 나뉘면 구현만 바꾼 일이 제품 문서 수정까지 번지지 않는다. 반대로 요구사항이 바뀌면 그에 맞춰 코드를 고쳐야 한다.
flowchart LR
PRD["기획 단계의 ALPS PRD"] --> H["요구사항을 빠짐없이 이관"]
H --> ADR["현재 구현 기준: ADR"]
P["이관 뒤 PRD 수정"] --> R["사용자가 재반영 요청<br/>현재 ADR과 비교"]
R --> A["요구사항·결정 변경 승인"]
A --> ADR
D["아키텍처 결정 변경"] --> ADR
ADR --> CODE["코드와 테스트"]
I["라이브러리·모듈·튜닝 변경"] --> CODE
Full ALPS의 아키텍처 설명에는 시스템과 외부의 관계, 내부 애플리케이션과 데이터 저장소, 재구현 뒤에도 유지할 제약을 남긴다. 내부 구성 요소와 프레임워크, 개발 도구 묶음(SDK), 데이터베이스 연결 라이브러리나 배포 도구는 코드에서 다시 찾을 수 있으므로 PRD에 올리지 않는다.
ADR로 남길 결정에도 기준을 둔다. 요구사항 계약, 데이터·보안 경계, 외부 서비스 제공자와 장애 시 대체 경로, 여러 구현을 계속 제약하는 선택의 장단점이 대상이다. 같은 계약을 유지한 채 바꿀 수 있는 라이브러리, 인증 정보를 연결하는 구현과 모듈 구조는 코드에 둔다.
이렇게 하면 SDK 교체나 파일 이동이 ADR 수정을 끌고 가지 않는다. 프레임워크를 바꿔도 제품의 시스템 경계가 그대로라면 PRD를 고칠 이유가 없다.
같은 결정의 대안이 바뀌었을 때도 새 ADR을 계속 만들지 않는다. ADR 본문은 현재 결정만 보여주고, 중요한 변화의 이력은 decision-log.md, 문장 단위의 전체 변경은 Git이 맡는다.
현재 상태, 중요한 전환과 전체 diff를 한 문서에 쌓지 않으므로 결정의 변경 횟수만큼 ADR이 늘어나지 않는다.
사용자가 수정한 PRD를 다시 반영해달라고 요청해도 문장 순서와 표현만 달라졌다면 아무것도 바꾸지 않는다. 실제 계약이나 경계가 달라졌을 때만 ADR 변경을 제안하며, 기존 요구사항을 없애는 변경도 자동으로 적용하지 않는다.
결과적으로 문서 변경량이 코드 변경량을 따라 폭증하지 않는다.
5. 계약은 완전하게, 구현은 열어둔다
추상화 계층을 나누면 Agent에게 구현 재량을 더 줄 수 있다.
ADR Writer에서는 ADR의 내용을 확인할 때, 코드를 전부 지우더라도 같은 요구사항과 경계를 지키는 구현을 다시 만들 수 있는지 묻는다. 이를 regeneration test라고 부른다. 파일이나 함수까지 예전과 똑같이 만들 필요는 없다.
예를 들어 갱신 토큰은 7일 동안 유효하다가 정해진 보안 정책이라면 정확한 7일과 그 근거를 ADR에 남긴다.
반면 그 정책을 어떤 SDK, 함수, 캐시 자료구조와 모듈로 구현할지는 코드의 선택이다. 다음 Agent가 현재 저장소의 관례와 도구에 맞는 방식을 고를 수 있다.
7일이라는 값은 ADR과 코드 양쪽에 있지만 맡은 역할은 다르다. ADR은 바꾸려면 다시 결정해야 하는 요구사항과 그 근거를 담고, 코드는 이를 실제로 적용한다.
코드만 보면 현재 값이 7일이라는 사실은 알 수 있어도, 개발자가 자유롭게 바꿀 수 있는 튜닝값인지 제품 계약인지는 알기 어렵다.
그래서 요구사항 값, 허용 상태와 권한, 반드시 지킬 순서와 실패 시 동작은 ADR에 남긴다. 구현 내부의 이름과 데이터를 저장하는 형태는 코드에 둔다.
이 상태를 계약은 완전하고 구현은 열려 있는 상태라고 볼 수 있다.
계약을 지키는 한 Agent는 리팩터링하고, 더 적합한 라이브러리를 고르고, 내부 구조를 바꿀 수 있다. 사람이 구현 계획을 미리 작성하지 않아도 자율성의 경계는 남는다.
6. 사람이 판단할 범위가 줄어든다
계층을 구별하면 리뷰도 코드 전체에서 시작하지 않아도 된다.
ADR Writer가 다루는 ADR은 요구사항을 독립적인 행으로 나누고, 특정 테스트 파일이나 함수 대신 구현과 무관하게 관찰할 수 있는 증거를 함께 적는다.
Agent는 이 계약을 기준으로 구현과 테스트를 진행한 뒤, 계약별 상태와 증거, 구현 중 선택한 내용과 남은 위험을 구현 검토 보고서(Evidence Package)로 만든다.
이 보고서는 새로운 권위 문서가 아니라 ADR과 코드에서 파생한 일시적인 검토 자료다. 다음 구현의 기준으로 쌓지 않는다.
긴 변경 내역과 검증 결과를 한꺼번에 읽으면, 어떤 근거가 어떤 판단을 뒷받침하는지 머릿속에서 다시 조합해야 한다. 사람이 검토할 때 이런 인지부하를 줄이려고 ADR Writer에 report-writer 스킬을 두었다.
보고서는 요청의 배경과 전체 판단을 먼저 보여주고, 업무 영역별 동작과 검증 결과, 상세 근거로 내려가도록 구성한다. 필요한 부분을 펼쳐 읽고, 관계를 이해하는 데 도움이 되는 다이어그램과 핵심 내용을 스스로 확인하는 질문을 제공한다. 구현 리뷰에서는 전체 의도와 계약에서 기능별 동작, 구성 요소, 필요한 코드 근거로 내려간다.
사람은 먼저 아래 내용을 본다.
- 승인한 계약이 모두 검증됐는가
- 계약을 지키는 범위에서 Agent가 고른 구현 방법은 무엇인가
- 새 계약이나 사람의 판단이 필요한 위험이 남았는가
증거가 부족하거나 보안, 결제와 데이터 변경처럼 구현 방식 자체가 위험한 부분만 코드로 내려가면 된다.
모든 코드를 읽지 않는다는 뜻은 아니다. 어디부터 읽고, 어느 부분까지 내려갈지를 계약과 위험으로 정할 수 있다는 뜻이다.4
이 경계가 없으면 Agent가 구현에서 줄인 시간을 사람이 전체 diff를 이해하는 데 다시 쓴다. 경계가 있으면 반복적인 구현 계획 승인을 줄이고, 사람의 판단을 계약 변경, 모순과 검증하지 못한 위험에 집중할 수 있다.
일반적인 설명과 리뷰에서는 이해에 필요한 복잡도에 따라 전달 방식을 고른다. 복잡한 내용은 보고서로 만들고, 짧고 단순한 내용은 채팅으로 전달하며, 보고서가 도움이 될지 애매하면 먼저 확인한다. 사용자가 명시한 보고서 요청과 전달 조건이 우선한다.
구현 검토 명령인 /adr-impl-review는 표준(standard)·전체(full) 검토 모두 HTML 보고서가 필수다. 보고서의 코드 판정과 읽는 사람이 구현을 이해했는지는 구분한다. 이해 확인 질문은 그 이해를 돕는 수단이며, 보고서가 생성됐다는 사실만으로 인지부하가 줄었다고 측정할 수는 없다.5
7. 계층을 나눌 때 쓰는 세 가지 질문
두 플러그인에서는 정보를 어느 계층에 둘지 아래 순서로 확인한다.
-
이 내용이 사라지면 다시 만든 코드가 요구사항을 위반할 수 있는가?
그렇다면 요구사항을 맡는 PRD나 ADR에 남긴다. 정확한 제한값, 허용 상태, 권한, 순서와 실패 보장이 여기에 해당한다.
-
요구사항이 아니라면 코드를 읽거나 도구를 실행해 다시 확인할 수 있는가?
그렇다면 코드와 테스트에 둔다. 라이브러리, SDK, 함수의 입력·출력 형식, 모듈 배치와 튜닝값은 보통 여기서 끝난다.
-
코드만으로는 선택 이유를 알 수 없고, 바꾸면 오래 유지할 결정이 달라지는가?
그렇다면 ADR에 선택 근거, 대안, 각 선택의 장단점과 경계를 남긴다.
같은 기술 이름도 답이 달라질 수 있다.
예를 들어 AI 모델을 제공받을 외부 서비스로 Amazon Bedrock을 선택하고, 장애 시 어느 서비스로 전환할지 정하는 일은 ADR 대상이 될 수 있다. 그 결정을 구현하는 SDK와 인증 정보를 읽거나 요청에 서명하는 코드는 같은 요구사항을 지키는 한 구현자가 고를 수 있다.
기술 이름이 들어갔는지가 아니라, 그 선택이 어떤 계약과 경계를 고정하는지를 봐야 한다.
마지막에는 다시 single-level read test를 적용한다. 문서 하나가 자기 질문에 답하지 못하거나, 아래 계층이 바뀔 때마다 함께 고쳐야 한다면 경계를 다시 봐야 한다.
마치며
지금은 코드 리팩터링이 ADR 수정을 요구하면 먼저 ADR의 해상도가 너무 낮은지 확인한다. 구현을 시작할 때 PRD를 다시 읽어야 한다면 handoff에서 계약이 빠졌는지 본다. 코드의 값이 계약인지 우연한 선택인지 구분할 수 없다면 ADR에 근거가 부족한지 확인한다.
이렇게 점검하니 한 번에 읽을 범위와 변경이 번질 범위가 줄었다. 문서를 나눌 때도 각 문서가 혼자 답해야 할 질문과, 구현 방법이 바뀌어도 지켜야 할 의도부터 정하는 편이 도움이 된다고 생각한다.
-
ALPS Writer Plugins의 설계 원칙은 AGENTS.md, ADR concepts, Dependency model에 정리되어 있다. ↩
-
쉽게 설명한 클린 / 헥사고날 아키텍쳐 — 추상화 계층으로 의존성을 줄이는 방식과 그에 따른 복잡도를 설명한 이전 글. ↩
-
구현 설명은 0.9.5를 기준으로 확인했다. 인덱스 스키마와 기존 프로젝트 가져오기 지침은 카테고리 단위의 선행 관계를 구분한다. 스키마·테이블 문서의 보조 참조인
tableDocs는 별도로 허용한다. ↩ -
AI로 코드는 빨리 만들었는데 왜 리뷰는 더 힘들까 — 계약과 검증 결과를 먼저 보고 위험한 부분만 코드로 내려가는 리뷰 방식을 다룬다. ↩
-
report-writer는 보고서의 읽기 구조와 적용 범위를 정한다. 구현 리뷰는 두 검토 방식 모두 HTML을 요구하며, 보고서 계약은 코드 판정과 사람의 이해를 구분한다. ↩