Werner Vogels – 좋은 API 디자인을 위한 6가지 배운 점
제가 API 구현 및 배포에 대해 관심도 많았고, 10년전에 최악의 오픈 API를 면하려면이라는 글도 쓴 적이 있는데, 버너 보겔스 박사님도 AWS가 초기 API 설계에서 어떤 실수를 했었는지 이야기하고, 그로 부터 배운 점을 설명해 주셨습니다.
초기 API 설계에서 실수
어떤 서비스라도 처음 API 설계를 할 때 많이 간과하는 부분이 있습니다. API를 만드는 팀이나 개인에 따라, 고객의 요구에 구현하다 보면 초기 설계에 주안점을 두지 않는 것입니다. 민첩하게 구현하다 보면, 결과적으로 각 팀의 API 형식이 약간 다를 수 있습니다. 예를 들어, 보겔스 박사님은 AWS Lambda 함수와 Amazon Kinesis에서 유사한 API 호출이 다르게 표현되는 예를 들어 주셨습니다.
$ aws lambda get-function --function-name WernersFunction
$ aws kinesis describe-stream --stream-name WernersStream
이 때, 일관성 없이 만들어진 API 구조를 기억하고 호출하는 외부 개발자의 생산성 손실을 만들 수 있습니다. 이러한 문제를 해결하기 위해 작년 9월에 AWS Cloud Control API 를 출시했습니다. AWS Cloud Control API는 클라우드 리소스를 생성, 읽기, 업데이트, 삭제 같은 작업을 표준화된 메소드로 호출할 수 있습니다.
$ aws cloudcontrol get-resource
--type-name AWS::Lambda::Function --identifier WernersFunction
$ aws cloudcontrol get-resource
--type-name AWS::Kinesis::Stream --identifier WernersStream
새로운 Cloud Control API를 사용하면, get-resource
라는 표준 방식을 통해 서비스마다 다른 API를 규격화하고 향후에도 하위 호환성을 유지할 수 있을 것입니다. 뿐만 아니라, HashiCorp나 Pulumi와 같은 AWS 파트너와 AWS 서비스 API에 의존하는 솔루션을 만드시는 분들에게도 앞으로 새로 만들어지는 AWS API에 대한 규격이 제공되기 때문에 좀 더 쉽게 고객 지원이 가능해 질 것입니다. (HarshCorp는 Terraform을 위한 AWS Cloud Control Provider를 출시했습니다.)
자! 그럼 이제 AWS가 API 설계에 대해 15년간 배운점을 하나씩 설명해 드리겠습니다.
1. API는 영원하다!
일단 API를 만들어 공개하면, 사용자가 생기게 됩니다. 혹시라도 나중에 원치 않는 기능을 지원 중단하는 것은 가능하지만, 서비스 팀과 고객에게 매우 번거롭고 고통스러운 과정일 수 있습니다. 영향을 받는 고객의 수에 따라 해당 API 기능을 더 이상 사용하지 않더라도, 제거하는 것 자체가 사실상 불가능할 수 있습니다. 따라서, API를 만들 때는 이 API가 삭제되지 않을 것이라고 가정하는 게 좋습니다.
Vogels 박사님은 “일단 API를 공개하면, 비즈니스는 그 위에 구축될 것”이기 때문에 API를 변경하면 기본적으로 비즈니스가 큰 영향을 준다는 점을 지적하셨습니다. 따라서, API 설계 단계에서 상당히 많은 고려를 하는 것이 좋습니다. (비지니스가 성공한다면, 향후 개발 생산성에 큰 이익이 될 것이고, 성공하지 않더라도 설계 과정에서 많은 배움이 있을 것입니다.)
2. 하위 호환성을 지켜주세요.
일반적인 사용자 경험(UX) 디자인과 API 디자인의 한 가지 중요한 차이점은 API는 이전 버전과의 호환성을 잘 지켜야 한다는 점입니다. AWS는 일단 API가 공개된 후에는 이전 버전과의 하위 호환성을 강력하게 보장하고 있습니다. Amazon S3 서비스는 2006년 출시 이후 다양한 방식으로 개선했지만, 1세대 API는 아직도 지원하고 있습니다.
하위 호환성을 유지하는 것이 API를 수정하거나 개선할 수 없다는 것이 아닙니다. 새로운 기능이 이전 버전으로 들어오는 호출이 영향을 받지 않도록 하는 것입니다. API를 발전시키는 안전한 방법은 기존 기능은 그대로 두고, 계속 새로운 기능을 추가하는 것입니다. (기존 API가 좋든 나쁘든) 변경하거나 제거하는 것은 안전하지 않습니다. 가끔 낮은 버전을 장기적으로 중단(Deprecation)하는 경우가 있는데, 이것은 고객(특히 개발자)과의 신뢰에도 큰 영향을 줍니다. 따라서, 하위 호환성은 필수라고 생각하시면서 운영하시기 바랍니다.
3. 고객 사용 사례에서 거꾸로 만드세요.
대개 API를 처음 설계할 때 하는 실수가 필요하다고 생각하는 모든 API를 구현하는 것입니다. 사용자가 필요한 게 무엇인지 파악한 다음, “실제 사용 사례에서 거꾸로 만드는 것”을 권장합니다. 이는 고객의 입장에서 피드백을 바탕으로 서비스를 구현하는 Amazon의 Working Backward 사상과도 일치하는 것입니다. 우리는 대개 “서비스 기획”이라는 단계를 거치는데, 이 기획 단계에서 만드는 사람들의 입김이 들어가는 것을 자주 보게 됩니다.
예를 들어, 여러 캠페인에 사용할 수 있는 광고 시스템 API의 경우, 새로운 캠페인이 필요할 때 마다 핵심 API를 추가 수정하지 말아야 합니다. 대신 플래그로 만들어서 핵심 캠페인 API 호출을 유지한 다음, 각 캠페인 요구 사항에 따라 다른 호환 가능한 API를 추가로 만드는 게 좋습니다.
4. 오류가 명시적인 API를 만드세요.
많은 분들이 API 기능 자체에만 신경쓰는 나머지 오류 모드에 대해 대충 구현하는 경향이 있습니다. 왜 오류가 났는지에 대해 응답 결과에서 명시적으로 알려주는 게 좋습니다. 이게 명확하지 않기 때문에 많은 개발자들이 고객 센터나 회사 게시판에 문의를 하거나, 스택오버플로우에서 헤매게 됩니다. 검색 결과에 따라 몇 시간 혹은 며칠식 문제 해결에 시간이 걸리기도 하죠.
API 문서 만큼이나 철저하게 직관적인 오류 모드 (잘못된 입력 매개변수, 권한 오류 등 보안 고려 사항)에 대해 잘 설명하도록 만들어 주시기 바랍니다. 개발자들은 오류를 많이 보더라도, API 기능이 적다고 생각하는 게 아니라 친절하다고 느끼게 될 것입니다.
5. 바로 목적과 사용법을 이해할 수 있는 API를 만드세요.
좋은 API는 문서화를 잘 하는 거라고 생각하는 오해도 있는데요. 문서를 읽지 않더라도 API 그 자체가 기능을 설명할 수 있도록 설계 해야 합니다. 그리고, 호출 결과에 그 다음에 사용할 만한 해당 API 기능에 대한 명시적인 설명이 있어도 좋습니다. REST API 원칙 중 ‘자기 서술적 메시지(Self-descriptive message)’라고 부르는 것이구요. 아래 API는 호출 만으로도 이 API가 두번째 글을 호출하는 것이고, 다른 글은 어떻게 호출하는지 바로 보여 줄 수 있습니다.
HTTP/1.1 200 OK
Content-Type: application/json
{
“Title” : “This is the second article”,
“content” : “This API is to return 2nd blog article."
href :
{
previous : /article/1
next : article/3
list: /list/all&max=20&page=1
}
}
좀 더 자세한 것은, What RESTful actually means 문서나 REST API로 괜찮은가라는 영상을 참고하시면 좋겠습니다.
6. 구현 세부 정보는 누출되지 않게 신경을 쓰세요.
마지막으로 API를 구현하는 세부 기술 정보는 가급적 누출되지 않아야 합니다. 구현 관련 정보는 API 자체의 일부가 되는 것입니다. Vogels 박사님은 “고객이 이러한 비필수적인 세부 사항에 의존하게 되면, 더 이상 해당 구현을 변경할 수 없다”라고 이야기하셨습니다. 여기서 구현 정보라는 게 어디까지 이야기하는가 하는 점에서 노란이 있을 수는 있지만, API는 내부 구조에 대한 단서를 알게 되면 보안 이슈를 만들어 낼 수도 있습니다.
많은 기업들이 API 설계에 대한 아키텍처 (AWS 서비스 포함)와 오픈 소스 구조를 외부에 발표를 하는 경우가 있는데, 가급적 지양해야 합니다. 회사 내부 기술을 홍보한다는 측면에서 모범 사례나 배운 점들을 알려주는 것은 좋지만, 어떤 소프트웨어를 어떻게 연결해서 API를 만들었는지 너무 상세하게 설명하지 않아야 합니다.
이 부분에 대한 좀 더 자세한 것은 버너 보겔스 박사님의 기조 연설을 참고해 주세요.
Smithy를 통한 확장성 높은 API 만들기
이러한 API 구현 윈칙에 따라 Amazon과 AWS에서는 개발자가 여러 프로그래밍 언어로 클라이언트와 서버를 위한 표준형 API를 구축할 수 있도록 하는 인터페이스 정의 언어(IDL)를 사용해 왔습니다. 이를 토대로 수만개의 API 서비스를 구축해왔는데, 이를 Smithy라는 오픈 소스로 공개했습니다.
Smithy는 맞춤형 API 표준을 만들고 유효성 검사 규칙을 공유할 수 있구요. 리소스 모델로 서비스를 정의하면, 더 나은 API를 만드는 데 도움이 되됩니다. Smithy는 여러 프로그래밍 언어에 대한 SDK 코드를 자동으로 생성할 목적으로 설계되었고, 어떤 프로토콜에도 구애받지 않고 데이터 전송 계층을 서비스의 데이터 구조 및 기능과 분리하여 독립적으로 발전할 수 있도록 합니다. Smithy 기반 API 설계 방식을 이용해서, Ruby SDK를 자동으로 생성하는 방법을 보시면 좀 더 쉽게 이해하실 수 있습니다. 더 자세한 것은 Smithy 구현 방법 문서를 참고하세요.
– Channy(윤석찬);
Update. Smithy에 대해 좀 더 자세한 것은 AWS SDK에서 일하고 있는 Michael Dowling의 강연 영상을 추천합니다. 이 강연에서는 새로운 오픈 소스 모델링 언어인 Smithy를 사용하여 AWS SDK를 구축하는 방법과 이를 적용하여 자체 서비스 및 사용자 지정 SDK를 구축하는 방법을 공유합니다.