- 소프트웨어 엔지니어링에서 API는 핵심 도구이며, 좋은 API는 지루할 정도로 익숙하고 단순한 것이 바람직한 특징
- API는 한 번 공개되면 변경이 어렵기 때문에 사용자 환경을 깨지 않는 원칙(WE DO NOT BREAK USERSPACE) 이 중요함
- 불가피하게 변경할 경우 버전 관리(versioning) 가 필요하지만, 이는 복잡성과 유지보수 비용을 크게 증가시키는 필요악임
- API 품질은 결국 제품 자체의 가치에 의존하며, 잘못 설계된 제품은 좋은 API를 만들기 어렵게 함
- 안정성과 확장성을 위해 API 키 기반 인증, 멱등성(idempotency), 레이트 리미트, 커서 기반 페이지네이션 등을 고려해야 함
서론: API 설계의 중요성과 맥락
- 현대 소프트웨어 엔지니어의 주요 업무 중 하나는 API와 상호작용하는 것임
- 작성자 역시 REST, GraphQL, 명령줄 도구 등 다양한 형태의 공개 및 사내용 API를 설계/구현/활용한 경험을 보유함
- 현존하는 API 설계 조언들은 복잡한 개념(REST의 정의, HATEOAS 등)에 집착하는 경향이 있음
- 본 글은 실제 경험을 바탕으로 실용적인 API 설계 원칙을 정리한 것임
친숙함과 유연성의 균형: 좋은 API의 첫 번째 조건
- 좋은 API는 '평범하고 지루한' API, 즉 기존에 접해본 API들과 사용법이 비슷해야 함
- 사용자는 API 자체보다는 본인의 목적 달성에 집중하기 때문에 진입장벽이 낮은 설계가 필요함
- 한 번 공개된 API는 변경이 매우 어려워, 최초 설계단계에서 신중함이 요구됨
- 개발자는 최대한 간결한 API를 원하면서도, 장기적인 유연성을 남기기 위한 고민이 항상 따름
- 결과적으로, 친숙함과 장기 유연성 간의 균형이 핵심 과제임
사용자 공간을 절대 깨지 않는다 (WE DO NOT BREAK USERSPACE)
- 기존의 응답 구조에서 필드를 추가하는 변화는 대부분의 경우 문제없음
- 하지만 필드 제거, 타입이나 구조 변경은 모든 소비자 코드를 깨뜨리는 결과를 초래함
- API 유지자는 기존 사용자의 소프트웨어를 고의로 망가뜨리지 않을 책임이 있음
- HTTP의 "referer" 헤더 오타조차 고치지 않는 이유는 사용자 공간을 보존하는 문화 때문임
API를 깨뜨리지 않고 변경하기: 버전 관리 전략
- 필수적일 때만 API에 파괴적 변경을 허용하며, 이때는 버전 관리가 정답임
- 구버전과 신버전을 동시에 운영하면서 점진적 전환을 유도해야 함
- 버전 식별자는 URL(
/v1/), 헤더 등 다양한 방식 활용 가능하며, 사용자는 각자 속도에 맞게 전환 가능함 - 버전 관리에는 엄청난 유지보수 비용(엔드포인트 증가, 테스트, 지원)과 사용자 혼동이라는 단점이 존재함
- Stripe처럼 내부 트랜스레이션 계층을 두더라도, 근본적인 복잡성은 피할 수 없음
- API 버전 관리 도입은 최후의 수단이어야 함
API의 성공 요인은 전적으로 프로덕트 가치에 달려있음
- API는 본질적으로 실제 비즈니스 제품의 인터페이스에 불과함
- OpenAI, Twilio 등 API도 결국 사용자가 원한 것은 API가 제공하는 기능 그 자체임
- 가치 있는 프로덕트라면 API가 불편해도 사용하게 됨
- API 품질은 "마진" 특성: 본질적 경쟁력이 비슷할 때만 선택 요소가 됨
- 반면, 아예 API가 없는 제품은 기술 사용자에게 큰 장애물임
프로덕트 설계가 나쁘면 API도 좋아질 수 없음
- 기술적으로 완성도 높은 API가 있어도, 시장성이 없는 제품이면 의미 없음
- 더 중요한 것은, 기본 리소스 구조가 비논리적이거나 비효율적이라면 API에서도 드러남
- 예를 들어, 댓글을 링크드 리스트로 저장하는 시스템은 RESTful 설계조차 자연스럽게 나오기 어렵게 만듦
- UI에서는 숨겨질 수 있는 기술적 문제들이 API에서는 모두 노출되며, 사용자의 시스템 이해도를 불필요하게 강요함
인증(Authenticaton)과 사용자 다양성
- 긴 수명의 API 키 기반 인증을 반드시 지원해야 함
- OAuth 같은 보안성 높은 방식을 추가 지원하더라도, API 키의 진입장벽이 월등히 낮음
- API 소비자는 엔지니어뿐 아니라 비개발자(영업, 기획, 학생, 취미 개발자 등)도 많음
- 어렵거나 복잡한 인증 요구(OAuth 등)는 비전문 사용자에게 장벽이 됨
멱등성(Idempotency)과 재시도 처리
- 액션성 요청(예: 결제, 상태변경 등)은 실패 시 재시도(retry) 에 대한 안전성이 중요함
- 멱등성이란, 동일 요청을 여러 번 보내도 결과가 한 번만 처리됨을 보장하는 것임
- 표준 방법은 "멱등성 키"를 파라미터나 헤더로 전달하여 중복 처리 방지임
- 멱등성 키 저장은 Redis 등 단순 키/값 저장소로 충분하며, 대부분의 경우 주기적 만료를 적용해도 무방함
- 읽기/삭제 요청(REST 방식)에는 일반적으로 필요 없음
API 안전성과 속도 제한(Rate limiting)
- 코드를 통한 API 요청은 사용자의 조작보다 훨씬 빠른 속도로 발생 가능함
- 무심코 배포한 API 한 건이 의도치 않은 방식(예: 대규모 채팅 시스템)에 활용될 수 있음
- 속도 제한(ratelimit)은 반드시 필요하며, 비용이 높은 연산에는 더 엄격하게 적용되어야 함
- 특정 고객에 대한 일시적 API 비활성화(killswitch)도 선택지로 고려해야 함
- 응답 헤더(
X-Limit-Remaining,Retry-After등)로 속도 제한 정보를 안내해야 함
페이징(Pagination) 전략
- 대규모 데이터셋(예: 수백만 티켓)을 효율적으로 반환하려면 페이징이 필수임
- 오프셋 기반(Offset-based) 페이징은 간단하지만 대량 데이터에선 점차 느려짐
- 커서 기반(Cursor-based) 페이징은 쿼리 성능 저하 없이 아주 큰 데이터셋에도 효과적임
- 커서 기반은 구현과 활용이 다소 어렵지만, 장기적으로는 필수적 변화일 가능성 높음
- 응답에
next_page필드 등을 포함해, 다음 요청의 커서를 명확히 안내하는 것이 현명함
선택적 필드 및 GraphQL에 대한 견해
- 비용이 크거나 느린 필드는 기본 응답에서 제외하고 필요시만 선택적으로 추가해야 함
includes파라미터 등으로 연관 데이터 포함 가능- GraphQL은 데이터 구조 유연성 장점이 있으나, 비개발자 접근성 저하, 캐싱/엣지케이스 복잡화, 뒷단 구현 난이도 등의 문제점 있음
- 실무 경험상 GraphQL 도입은 꼭 필요한 경우에 한정하는 것이 적합함
내부용 API에 대한 특징
- 사내 API는 외부(API 공개형)와는 여러 조건이 다름
- 소비자는 대부분 전문 소프트웨어 엔지니어이므로, 더 복잡한 인증이나 파괴적 변경 가능함
- 그래도, 멱등성과 사고 예방, 운영 부담 최소화를 위한 설계 원칙은 유효함
요약 정리
- API는 변경이 어렵고 사용은 쉬워야 하는 특성을 가짐
- 사용자 공간을 깨지 않는 것이 API 유지자의 가장 중요한 의무임
- API 버전 관리는 비용이 크기 때문에 최후의 수단으로만 활용해야 함
- 최종적으로 API의 품질은 프로덕트의 본질적 가치가 좌우함
- 잘못 설계된 프로덕트는 API 수준에서 보완해도 한계가 큼
- 간단한 인증 방식 지원, 필수 액션 요청엔 반드시 멱등성, 그리고 속도 제한/페이징 등 안정성 대책 중요함
- 내부 API는 용도와 대상에 따라 전략이 다르지만, 설계 신중함은 여전히 요구됨
- REST, JSON 등 포맷이나 OpenAPI 등은 본질적 논점이 아님. 명확한 문서화가 더 중요함