계약 형태
개요
API가 우회를 줄이려면
entry point뿐 아니라 합법 경로를 드러내는 계약의 형태도 중요하다.
나쁜 계약은 호출자에게 내부 구조를 추측하게 만들고, 좋은 계약은 호출자가 내부 구현을 몰라도 되게 만든다.
1. command는 행위를, DTO는 경계를 드러낸다
하네스 친화적 계약은 대개 다음 특징을 가진다.
- command는 허용된 행위를 명시한다
- DTO는 경계 밖으로 나갈 수 있는 정보를 제한한다
- facade는 내부 그래프를 직접 만지지 않게 한다
이 형태는 호출자에게 무엇을 할 수 있는지는 명확히 주되,
어떻게 구현되는지는 감춘다.
2. raw object 노출은 우회 가능성을 키운다
raw entity, mutable state, 내부 service reference를 직접 노출하면, 호출자는 공식 경로 대신 내부 구조를 따라가기 시작한다.
그 결과:
- 직접 mutation
- 내부 의존성 호출
- 테스트에서만 쓰던 편법의 확산
- validation 없는 조작
계약은 정보를 많이 주는 것이 아니라, 잘못된 자유도를 줄이는 방향으로 설계해야 한다.
3. result shape도 중요하다
호출 결과가 너무 풍부하면, 호출자는 그 안에서 다시 비공식 경로를 만들기 쉽다.
예:
- 필요한 값 하나만 있으면 되는데 내부 객체 전체를 반환
- status만 필요하면 되는데 mutable reference까지 함께 전달
- 실패를 명시해야 하는데 default success처럼 보이는 값을 반환
좋은 result shape는 다음을 만족한다.
- 필요한 정보만 준다
- 실패를 숨기지 않는다
- 후속 행동을 공식 경로로 유도한다
4. 계약은 에러도 포함해야 한다
에러가 계약 밖에 있으면 호출자는 에러를 무시하거나 fallback을 만든다.
따라서 계약은 다음을 분명히 해야 한다.
- 어떤 실패가 가능한가
- 어떤 입력이 허용되지 않는가
- 호출자는 실패 시 무엇을 해야 하는가
좋은 contract shape는 happy path만이 아니라, 실패 path도 설계 안에 포함한다.
5. 좋은 계약의 조건
- 역할이 분명하다
- 입력과 출력이 안정적이다
- 내부 세부를 감춘다
- 잘못된 자유도를 주지 않는다
- 실패를 조용히 숨기지 않는다
이 기준은 API 가독성뿐 아니라 강제 준비도에도 직결된다.
6. 실무에서 보는 위험 신호
- DTO 대신 내부 entity를 바로 넘긴다
- facade가 얇고 실제로는 내부 service가 더 자주 호출된다
- result에 mutable reference가 섞여 있다
- command 없이 setter와 direct mutation이 넓게 열려 있다
- 예외와 실패가 결과 타입 밖에서 처리된다
이 신호는 contract shape가 우회를 허용하고 있다는 뜻이다.
요약
좋은 contract shape는 다음을 만든다.
- command로 허용된 행위를 드러내고
- DTO로 경계를 좁히고
- facade로 내부 그래프를 감추고
- result shape로 후속 행동을 공식 경로 안에 묶는다
좋은 계약은 호출자에게 많은 자유를 주는 계약이 아니라, 필요한 일만 정확히 하게 만드는 계약이다.