파트너 수수료 계층 구조와 외부 API 레퍼런스 문서 정비
목차
문서 작업은 항상 후순위로 밀린다. 기능 구현이 급하고, 배포가 급하고, 버그 수정이 급하다 보면 문서는 언제나 "나중에"가 된다. 근데 그 나중이 쌓이면 외부 API 하나 연동할 때마다 이미 3개월 전에 다 파악해뒀던 스펙을 처음부터 다시 뒤져야 한다. 그게 더 비싸다는 걸 알면서도 계속 그러게 된다.
이번에 .claude/docs/ 아래를 정리하면서 이 루프를 끊으려 했다.
문서 구조를 왜 이렇게 쪼갰나
.claude/docs/
├── api-reference.md # 외부 API 엔드포인트 정리
├── architecture.md # 시스템 구조도
├── java.md # 코딩 컨벤션
├── sql.md # SQL 패턴/규칙
└── rbac.md # 권한 체계
처음엔 하나의 큰 README.md로 관리했었는데, 파일이 커질수록 목적별로 찾아 들어가기가 힘들었다. 코딩 컨벤션 보려다 SQL 패턴 읽고, 권한 체계 확인하러 들어갔다가 API 스펙까지 스크롤하는 일이 반복됐다. 관심사를 분리하는 건 코드에서만 하는 게 아니다.
api-reference.md는 특히 공들였다. 외부 서비스와 연동하는 엔드포인트는 우리가 통제할 수 없는 영역이라, 언제든 스펙이 바뀔 수 있다. 그래서 "현재 우리가 사용하는 버전 기준"이라는 걸 명시하고, 마지막으로 검증한 날짜를 같이 적어뒀다. 한 달 넘은 항목은 의심하고 다시 확인하는 게 맞다.
좋은 API 레퍼런스가 갖춰야 할 요소를 정리하면 이렇다.
| 항목 | 내용 |
|---|---|
| 엔드포인트 | URL, Method, 인증 방식 |
| 요청 파라미터 | 타입, 필수 여부, 예시 |
| 응답 형식 | 성공/실패 각 샘플 |
| 에러 코드 | 코드 + 원인 + 대응 방법 |
에러 코드 항목이 빠져 있는 문서가 생각보다 많다. 성공 응답만 잘 정리해놓고 에러는 "알아서 처리"처럼 남겨두는 경우. 근데 실제 운영에서 문제가 생기면 에러 코드 해석이 제일 먼저 필요하다. 코드가 무엇을 의미하고 어떻게 대응해야 하는지까지 적어야 비로소 쓸 수 있는 문서가 된다.
문서 관리 원칙 하나만 꼽으라면 코드와 문서를 같은 PR에 묶는 것이다. 분리되는 순간 시차가 생기고, 시차가 생기는 순간 문서를 못 믿게 된다. 못 믿는 문서는 없는 것과 다를 게 없다.
파트너 수수료 계층 구조 구현
이번 작업의 다른 축은 파트너 계층별 수수료 검증 로직이었다. 유통 단계가 깊어지면 수수료 계산 자체는 단순하지만, 계층 간 순서가 뒤집히면 마진 역전이 발생한다.
최상위 운영사 (0%)
└── 총판 A (0.5%) → 수익 0.5%
└── 판매점 B (0.8%) → 수익 0.3%
└── 최하위 C (1.0%) → 수익 0.2%
각 단계의 수익은 자신의 요율에서 상위 파트너 요율을 뺀 값이다. 단순하지만 이게 깨지면 어떤 단계가 돈을 버는지 알 수 없게 된다. 예를 들어 판매점 B의 요율이 0.3%로 설정됐다면, 총판 A보다 낮아서 B의 수익이 음수가 된다. 시스템이 막아주지 않으면 데이터 입력 실수 하나가 정산 전체를 틀어버린다.
그래서 파트너 등록·수정 시 상위 파트너 요율을 초과할 수 없도록 검증 로직을 넣었다. Java 기준으로 대략 이런 형태다.
public void validateCommissionRate(Partner partner, BigDecimal newRate) {
if (partner.getParentPartner() == null) return; // 최상위는 제한 없음
BigDecimal parentRate = partner.getParentPartner().getCommissionRate();
if (newRate.compareTo(parentRate) <= 0) {
throw new InvalidCommissionRateException(
"요율은 상위 파트너(" + parentRate + "%) 초과 불가"
);
}
}
compareTo로 비교하는 이유는 BigDecimal에서 equals가 스케일까지 비교하기 때문이다. 1.0과 1.00을 다르게 취급하는 버그를 한 번 겪고 나서 금액/요율 관련 비교는 전부 compareTo로 통일했다.
한 가지 고민했던 부분은 요율이 "정확히 동일"한 경우를 허용할지 여부였다. 위 코드에서 compareTo(parentRate) <= 0이면 예외가 발생하는데, 즉 상위와 같은 요율도 막는다. 같은 요율이면 해당 단계의 수익이 0%가 되는데, 이게 비즈니스 상 의미 있는 케이스인지 도메인 담당자와 확인했다. 결론은 0% 마진 파트너도 존재할 수 있다는 쪽이었고, 그래서 조건을 < parentRate로 완화할지 검토했으나, 일단 현 정책 기준으로는 동일 요율도 허용하지 않는 쪽으로 결정됐다. 이런 결정 사항은 코드 주석이나 java.md에 남겨두는 게 맞다. 나중에 조건 바꾸려는 사람이 왜 이렇게 됐는지 알 수 있게.
오늘 작업에서 느낀 것
문서 정비와 비즈니스 로직 구현이 같은 날 묶인 건 우연이 아니었다. 파트너 계층 구조를 제대로 구현하려면 먼저 도메인 개념을 정확히 이해해야 했고, 그 이해 과정 자체가 문서화로 이어졌다. 코드 먼저 짜고 나중에 문서 쓰는 방식보다 이게 훨씬 낫다. 설계 의도가 코드에 남고 예외 처리 판단 근거가 문서에 남는다.
.gitignore 업데이트도 같이 했다. 로컬에만 있어야 하는 파일들이 문서 디렉토리 안에 섞여 있었는데, 정리하면서 패턴을 명시적으로 추가해뒀다. 당연히 무시될 거라 생각하고 넘어가는 파일이 실수로 커밋되는 경우가 종종 있어서, 커밋 전에 git status 한 번 더 보는 습관이랑 명시적 .gitignore 항목을 함께 가져가는 게 낫다고 본다.
댓글 0
첫 댓글 달아줘.