GN⁺: No Abstractions: 우리의 API 설계 원칙
(increase.com)- API 리소스 네이밍과 모델링 결정은 API 설계에서 가장 어렵고 중요한 부분임. 리소스는 사용자의 제품 작동 방식과 기능에 대한 멘탈 모델을 구성함.
- Increase 팀은 "추상화 없음" 원칙을 사용하여 API 설계를 지원함.
Stripe의 API 설계 접근 방식
- Stripe 팀의 상당수는 이전에 Stripe에서 근무했으며, Stripe의 성공적인 API 설계 가치를 고려함.
- Stripe은 복잡한 도메인의 핵심 기능을 사용자가 쉽게 이해하고 사용할 수 있는 추상화로 추출하는 데 탁월함. (예: Visa와 Mastercard의 차이를 추상화한 PaymentIntent)
- Stripe의 사용자 대부분은 결제와 무관한 제품을 개발하는 초기 스타트업으로, 신용카드의 세부 사항을 알 필요가 없음. 이들은 Stripe를 빠르게 통합하고 제품 개발에 집중하기를 원함.
Increase 사용자와 API 설계 원칙
- 반면에 Increase의 사용자는 결제 네트워크에 대한 깊은 지식이 있으며, Increase를 선택한 이유가 직접적인 네트워크 연결과 통합 깊이 때문임.
- 이들은 FedACH 창구가 닫히는 시점과 이체 시점을 정확히 알기를 원하며, ACH 이체에서 다른 SEC 코드 설정이 반환 타이밍에 영향을 줄 수 있다는 것을 이해함.
- 이러한 네트워크의 근본적인 복잡성을 숨기려는 시도는 사용자를 짜증나게 할 뿐 삶을 단순화하지 않음.
- 초기 사용자와의 대화를 통해 "추상화 없음" 원칙을 도출하고 API 설계에 적용함.
"추상화 없음" 원칙이 API 설계에 미친 영향
- 실제 사용되는 용어 사용: API 리소스와 속성에 자체 이름을 만드는 대신 기본 네트워크의 어휘를 사용함. (예: ACH 이체 파라미터는 Nacha 사양의 필드명을 사용)
- 불변성: 실제 이벤트를 모델로 사용하여 더 많은 API 리소스가 불변하도록 함. 불변 리소스 클러스터를 상태 머신 "수명 주기 객체"로 그룹화하는 것이 효과적임. (예: ach_transfer 객체의 status 필드와 전송 수명 주기에 따라 생성되는 몇 가지 불변 하위 객체)
- 사용 사례별 리소스 분리: 리소스 인스턴스에 따라 사용자가 취할 수 있는 작업 집합이 크게 다른 경우 여러 리소스로 분할함. (예: 발신 ACH 이체와 수신 ACH 이체는 별도의 리소스로 분리)
엔지니어링 팀의 접근 방식 준수
- 엔지니어링 팀이 이 접근 방식을 준수하기로 약속함.
- 복잡한 API를 몇 년 동안 설계할 때 작은 증분 결정을 항상 내려야 하는데, 사전에 기본 원칙을 준수하기로 약속하면 이러한 결정에 대한 인지 부하가 줄어듦.
- 예를 들어 연방준비제도로 송금할 때 필요한 Input Message Accountability Data 필드의 경우 추상화가 많은 API에서는 이 필드의 이름을 "사용자 친화적"으로 지정하는 방법을 고민해야 하지만, Increase에서는 엔지니어가 필드 이름을 input_message_accountability_data로 지정하고 넘어감.
GN⁺의 의견
- API의 추상화 수준은 해당 제품 도메인에 대한 개발자의 경험 수준과 통합에 투입할 에너지 등에 따라 달라질 수 있음. 따라서 API를 설계할 때 통합할 개발자에게 적절한 추상화 수준을 고려하는 것이 중요함.
- 추상화 수준이 높은 API를 구축한다면 새로운 기능을 추가하기 전에 신중하게 생각해야 함. 반면 추상화 수준이 낮은 API를 구축한다면 이를 준수하고 추상화를 추가하고 싶은 유혹에 저항해야 함.
- 기본 네트워크나 프로토콜의 용어를 그대로 사용하는 것은 개발자들이 underlying system을 이해하는데 도움이 될 수 있지만, 반대로 처음 접하는 개발자에게는 진입장벽이 될 수도 있음. 따라서 주석이나 문서화를 잘 해두는 것이 중요할 것 같음.
- API 설계 시 immutable 객체를 활용하는 것은 데이터의 정합성 유지와 side-effect 방지에 효과적일 수 있음. 하지만 반대로 데이터 갱신이 필요한 경우 불편할 수도 있으므로 trade-off를 잘 고려해야 함.
- 사용 사례별로 리소스를 분리하는 것은 API의 복잡도를 높일 수 있지만, 장기적으로는 예측 가능성을 높일 수 있음. 하지만 너무 세분화되면 사용성이 떨어질 수 있으므로 적절한 수준을 찾는 것이 중요함.
Hacker News 의견
-
낮은 수준의 추상화가 적용된 API와 높은 수준의 추상화가 적용된 API를 모두 제공하는 것이 좋음
- 낮은 수준의 API는 세밀한 제어가 가능하지만 전문 지식이 필요함
- 높은 수준의 API는 일반적인 사용 사례에 맞춰 단순화된 작업을 제공함
- 두 API 사이에 명확한 구분을 유지하면, 각 API에 추상화나 특수 사례를 추가하려는 압박이 줄어듦
- 클라이언트가 한 API에서 다른 API로 이동하는 방법을 배울 수 있도록 자료를 제공하는 것이 좋음
-
Increase가 다른 접근 방식을 선택한 이유를 설명한 부분이 마음에 듦
- 근본적인 것을 설계할 때 상황이 많이 중요하지만, 사람들은 보통 이를 충분히 인식하지 못함
-
Stripe의 진정한 능력은 고객을 알고 고객이 원하는 단순함을 제공하는 것임
- Increase도 고객이 필요로 하는 것을 잘 파악하고 있으며, 훌륭한 제품을 만들기 위한 설계 지침을 만드는 데 유사한 집중력을 발휘함
-
도메인 전문가가 사용하는 실제 용어와 동일한 용어를 구현에 사용하는 Domain-Driven Design의 "보편 언어(Ubiquitous Language)" 설계 패턴과 유사함
-
도메인 전문가가 이해할 수 있는 언어를 사용해야 함
- 사용자가 NACHA 파일에 대해 알고 있다면, 다른 용어를 사용하면 머릿속에서 매핑을 유지해야 함
- Stripe의 경우 사용자가 도메인 전문가가 아니므로, 이해 가능하면서도 불필요한 세부 사항을 숨기는 추상화를 만드는 것이 가치 있음
-
POSIX와 같은 추상화가 없다면, 애플리케이션은 지원되는 모든 파일 시스템에 대해 어댑터를 작성해야 함
-
외부에서 제어되는 사양을 기반으로 API 구조의 일부가 1:1로 구축된다고 함
- 이러한 사양이 발전하거나 변경되면 어떻게 되는가? 새로운 API인가?
-
지불 API에서 깔끔하게 모델링하기 어려운 한 가지는 지불 체계가 지불 반환 시 지급인과 수취인의 역할을 다른 방식으로 나타낸다는 것임
- 예를 들어, 특정 체계에서는 지급인과 수취인이 초기 지불과 동일한 위치에 유지될 수 있음
- 반면 다른 체계에서는 전환됨