새로운 프로젝트를 시작하면서 구현만을 하던 API서비스를 직접 설계하게 되는 기회가 생겼다.
지속적으로 유지 관리가 되어야 하며 확장의 계획이 있는 프로젝트이기에 확실한 기반과 구조를 가져가야 했고, 이번 기회에 API설계에 대한 패턴을 탐색해 보았다.
API 설계 패턴은 빠른 확장을 가능하게 하고 설계 및 구동에 있어 일반적인 문제를 해결하는데 청사진이 될 수 있다.
하기 내용에는 API 디자인 패턴의 사용 이점과 일반적인 패턴, 구현 모범 사례를 함께 다루어 보려고 한다.
API 디자인 패턴을 사용하면 좋은 점
디자인 패턴을 사용하는 원초적인 이유는 협업의 용이성과 안정적인 서비스 운용 등 다양하게 있다.
이에 따라서 API 디자인 패턴을 사용하면 함께 협업하는 개발자와 API를 더 쉽게 이해하고 확장 및 유지보수가 쉬워지게 된다. 또한 이러한 패턴은 문제를 야기할 수 있는 부분을 줄일 수 있으며 대응에도 용이하도록 할 수 있다.
API 디자인 패턴은 API의 무단 액세스로부터 시스템 보호가 가능하고 이외의 보안적인 측면에서도 이점이 있다. 자원 비용 측면에서도 시스템의 부하를 줄이며 캐싱 메커니즘 등을 활용하여 자원 점유에 대한 면에서도 챙길 수 있다.
일반적인 API 디자인 패턴
버전 관리
API의 버전 관리 디자인 패턴은 시간 경과, 기능 추가에 따른 API의 변경 사항을 관리하는 데 사용된다. API가 발전 및 확장함에 따라 새로운 기능은 추가될 것이며, 기존 기능이 수정될 수 있고, 제거도 될 수 있다.
API는 업데이트를 하게 되면 클라이언트 측의 업데이트가 뒤따라야 하게 때문에 이러한 공수를 한 번에 감수하기에는 큰 무리가 있다. 버전 관리를 통하여 클라이언트 측은 기존 API 기능을 중단 없이 사용이 가능하며, 점진적인 신 버전 API로 마이그레이션 할 수 있도록 시간을 줄 수 있다.
API 버전 관리를 구현하는 데는 다음과 같은 여러 가지 접근 방식이 있다.
- URL 기반 버전 관리
이 버전 관리법은 API 엔드포인트의 URL에 버전 정보를 표기한다. 예를 들어 API 버전 2에 요청을 하려면 클라이언트는 다음과 같은 URL을 사용한다.
https://api.example.com/v2/hello
- Query 매개변수 기반 버전 관리
이 버전 관리법은 버전 명이 API 요청 URL 쿼리 매개변수로 포함을 한다. 예를 들어 위와 같이 API 버전 2에 액세스를 하려면 클라이언트는 다음과 같이 URL을 사용해야 된다.
https://api.example.com/hello?version=2
- header 기반 버전 관리
이 버전 관리법은 버전 정보가 API 요청 내부의 사용자 정의 HTTP header에 포함한다. 예를 들어 클라이언트는 요청 header 내부에 "X-API-Version: 2"를 포함할 수 있다.
캐싱
캐싱은 API의 성능을 향상시키는 중요한 디자인 패턴이다. 클라이언트가 API에 요청을 보낼 때마다 요청을 처리하고 응답을 반환해 준다. 이러한 동작은 만약 복잡한 DB 쿼리를 수행하거나 자원 집약적인 작업을 수행하는 경우 상당한 latency 발생할 수 있다.
캐싱은 액세스가 자주 일어나는 리소스를 캐시에 저장하여 이 프로레스 작업 속도를 높이는데 많은 도움이 된다.
API 캐싱은 클라이언트 측, 서버 측, 분산 환경에서 모두 활용할 수 있다. 클라이언트 측에 지속적으로 중복되는 API의 응답을 자체 캐시에 저장한다. 이러한 기능을 활용하면 서버 측에 요청이 도달하지 않아도 클라이언트는 자체 캐시를 이용하여 데이터를 다룰 수 있다. 반대로, 서버 측 캐싱에서는 서버가 응답을 저장하고 요청을 백엔드 시스템으로 전달하는 대신 요청이 다시 이루어질 때 사용할 수 있도록 할 수 있다. 분산 캐싱은 서버 측 캐싱과 유사하며 응답만 여러 서버에서 공유되는 분산 캐시에 저장한다. 이렇게 하면 모든 서버가 캐시 된 응답을 반환하여 백엔드 시스템의 로드를 줄일 수 있다.
GET /api/example
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600, public
ETag: "adfasdf3faf"
{"email":"example@example.com"}
위 예시에서는 API 응답에는 최대 1시간(max-age=3600) 동안 응답을 캐싱할 수 있음을 지정하는 Cache-Control header에 포함되어 있다. ETag의 경우 클라이언트가 캐시 된 응답이 여전히 유효한 것인지 확인할 수 있는 방법을 제공한다. 클라이언트가 캐싱 기간 내에 다시 동일한 요청을 하고 ETag값이 동일한 경우, 동일한 요청을 또 하는 것이 아닌 캐시 된 응답을 반환할 수 있다. 이 외에도 다양한 옵션들이 존재한다.
Pagenation
Pagenation은 방대한 양의 데이터 결과를 작게 분할하여 사용하도록 가능하게 한다. 이 쪽 수를 활용하면 클라이언트가 데이터의 이어지는 데이터도 요청하면 되므로 전송되는 데이터의 양을 줄이고 API의 전반적인 성능을 향상할 수 있다. Pagenation은 보통 API 요청 Query 매개변수를 사용하여 구현한다. 예를 들어, 클라이언트는 원하는 페이지와 페이지당 항목 수를 표현하기 위해 API 요청에 "page"와 "pageSize"와 같은 매개 변수를 기입할 수 있다. 따라서 아래와 같이 표현 가능하다.
GET /api/posts?page=3&pageSize=10
속도 제한
속도 제한 즉, Rate limmiting은 클라이언트가 API에 요청할 수 있는 속도를 제어하는 데 사용된다. 만약 특정 클라이언트가 과다한 요청으로 API를 오버로드하여 성능의 저하를 발생시키거나 심지어 충돌이 발생하는 것이 발생하는 것을 방지하는 일반적인 방법이다. 요청은 초 당, 분 또는 시간당 특정 요청 수로 제한된다. 클라이언트는 한도를 초과하면 API가 관련 에러를 반환하여 서비스의 남용을 방지한다.
이를 사용하기 위해 클라이언트가 생성할 수 있는 요청 수를 나타내는 토큰을 저장하는 데 토큰 버킷 알고리즘을 사용한다. 클라이언트가 요청하면 버킷에서 코튼이 제거된다. 버킷이 비어있으면 클라이언트는 토큰이 버킷에 다시 추가될 때까지 기다려야 하며, 이는 특정 주기로 동작을 하게 된다.
다음은 HTTP 헤더를 사용하는 API 속도 제한 예시이다.
// Request to an API that supports rate limiting
GET /api/resource
// Example of HTTP headers used for rate limiting
Authorization: Bearer <access_token>
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1630694271
API 응답에는 속도 제한과 관련된 3개의 header가 포함되어 있다.
X-RateLimit-Limit: 특정 기간 동안 클라이언트가 만들 수 있는 최대 요청 수를 지정
X-RateLimit-Remaining: 클라이언트가 제한에 도달하기 전에 남은 요청 수를 지정
X-RateLimit-Reset: 속도 제한이 재설정되는 시간을 Unix 타임스탬프 형식으로 저
회로 차단기
회로 차단기 즉, Circuit-breaker는 분산 시스템의 이슈를 처리하는 데 사용되는 설계 패턴이다. 이는 하나의 서비스가 실패하면 해당 서비스에 의존하는 다른 서비스도 실패할 때 발생할 수 있는 계단식 오류에 대한 보호 메커니즘을 한다.
Publish-subscribe
발행과 구독 즉, Publish-subscribe는 이벤트 기반 아키텍처 및 메시징 시스템과 관련된 API에서 일반적으로 사용된다. 단순히 메시지의 발행자는 목적지에 메시지를 보내고 구독자는 해당 메시지를 받는다. 이 패턴은 동일한 주제에 관심이 있는 구독자가 여러 명 있고 발생자가 구독자의 신원을 알 필요가 없는 시나리오에서 특히 유용하다. 발행자와 구독자의 관계는 서비스가 느슨하게 결합노딘 방식으로 서로 통신해야 하는 분산 시스템에서 패턴을 특히 유용하게 만든다.
인증 authentication과 인가 authorization
인증 authentication 은 사용자나 시스템의 신원을 확인하는 프로세스이다. API 설계에서는 인증된 사용자나 시스템만 API에 액세스 할 수 있도록 인증이 사용된다. 일반적으로 사용자에게 사용자 id 및 비밀번호, 액세스 토큰 또는 디지털 인증서와 같은 특정 형태의 자격 증명을 제공한다. 사용자의 신원이 확인되면 API는 요청된 리소스에 대해 액세스 권한을 부여할 수 있다.
인가 authorization는 사용자나 시스템이 특정 리소스에 대해 어느 수준의 액세스 권한을 가져야 하는지 결정하는 프로세스이다. API설계에서는 사용자가 액세스 권한이 있는 리소스에만 액세스 할 수 있도록 하기 위해 인가가 사용된다. 이는 일반적으로 사용자가 특정 리소스에 대해 수행할 수 있는 작업을 지정하는 역할과 권한을 적절히 정의하여 수행한다.
인증과 인가는 REST API, GraphQL API, SOAP API를 비롯한 다양한 유형의 API에서 사용된다. 이 패턴의 구체적인 구현은 API의 기술 및 요구 사항에 따라 달라질 수 있지만 대부분의 경우 따르는 몇 가지 일반적인 사례가 있다.
예를 들어, 일반적인 접근 방식 중 하나는 OAuth 2.0 인증 방식을 사용하는 것이다. OAuth 2.0은 최신 API에서 널리 사용되는 토큰 기반 인증/인가를 위한 개방형 표준이다. 또 다른 접근 방식은 클라이언트와 서버 간의 데이터 전송 사이에서 주고받는 JWT방식을 사용한다.
API 개발 모범 사례
API는 애플리케이션과 기타 클라이언트 혹은 시스템과 연결하는 데 널리 사용된다. 따라서 API를 쉽게 사용하고 유지 관리할 수 있도록 모범 사례를 확인해 보는 것이 좋을 것 같다.
일관된 리소스 이름 지정
- 일관된 에러 응답
- 일관된 응답 코드
- API 문서 제작
- 요수사항과 피드백에 따라 API를 지속적 개선 및 발전
- 확실한 API 테스
- 등등등
마무리
API 디자인 패턴은 확장 가능하며 안전한 API를 만들기 위해서는 무조건 적으로 필요한 요소이다. API가 동작하는 시스템을 방어하며 리소스의 사용을 효율적으로 운용하여 클라이언트가 서비스 이용에 무리 없이 만족할 수 있도록 고품질 API를 만드는데 도움이 될 수 있을 것 같다. 또한 본 내용은 API를 떠나 데이터 이동 관점에서 보았을 때도 기본이 되는 내용이 많은 것 같다.
'Backend > 개발 방법론 & 디자인 패턴' 카테고리의 다른 글
[Rate Limiting] Token Bucket 알고리즘 (2) | 2024.10.27 |
---|---|
DTO (Data Transfer Object)의 설계법 (0) | 2023.12.12 |
[Design Pattern] 싱글톤 패턴이란? (Singleton Pattern) (0) | 2023.03.11 |
[Methodologies] Monorepo(모노레포)란? (0) | 2023.02.24 |
[Design Pattern] Factory Pattern (0) | 2022.08.05 |