"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > Monite의 API 버전 관리

Monite의 API 버전 관리

2024-11-08에 게시됨
검색:219

우리 모두는 멋진 새 도구를 갖고 싶어하지만 지속적으로 업데이트하는 일을 싫어합니다. 이는 운영 체제, 앱, API, Linux 패키지 등 모든 것에 적용됩니다. 업데이트로 인해 코드 작동이 중단되면 고통스럽고 업데이트가 시작되지도 않았을 때 고통은 두 배로 커집니다.

웹 API 개발에서는 새로운 업데이트가 있을 때마다 사용자 코드가 손상될 위험이 끊임없이 존재합니다. 귀하의 제품이 API라면 이러한 업데이트는 매번 끔찍할 것입니다. Monite의 주요 제품은 API와 화이트 라벨 SDK입니다. 우리는 API 우선 회사이므로 API를 안정적이고 사용하기 쉽게 유지하는 데 세심한 주의를 기울입니다. 따라서 변경 사항을 깨뜨리는 문제는 우리의 우선순위 목록의 최상위에 가깝습니다.

일반적인 해결 방법은 클라이언트에 지원 중단 경고를 표시하고 주요 변경 사항을 거의 릴리스하지 않는 것입니다. 갑자기 릴리스가 완료되는 데 몇 달이 걸릴 수 있으며 일부 기능은 다음 릴리스가 나올 때까지 숨겨지거나 병합되지 않은 상태로 유지되어야 합니다. 이로 인해 개발 속도가 느려지고 사용자가 몇 달에 한 번씩 통합을 업데이트해야 합니다.

릴리스를 더 빠르게 만들면 사용자가 통합을 너무 자주 업데이트해야 합니다. 릴리스 간격을 늘리면 회사의 움직임이 느려집니다. 사용자에게 불편함을 더 많이 줄수록 사용자에게는 더 편리해지며, 그 반대도 마찬가지입니다. 이는 확실히 최적의 시나리오는 아닙니다. 우리는 정기적인 지원 중단 접근 방식으로는 불가능할 기존 클라이언트의 어떤 것도 손상시키지 않고 우리 자신의 속도로 움직이고 싶었습니다. 이것이 우리가 대체 솔루션을 선택한 이유입니다: API 버전 관리.

아주 간단한 아이디어입니다. 주요 변경 사항을 언제든지 릴리스하고 새 API 버전에서 숨기면 됩니다. 이는 두 세계의 장점을 모두 제공합니다. 사용자는 통합이 일상적으로 중단되지 않으며 원하는 속도로 이동할 수 있습니다. 사용자는 원할 때 언제든지 부담 없이 마이그레이션할 수 있습니다.

아이디어의 단순함을 고려하면 어떤 회사에도 딱 맞는 느낌입니다. 이것이 일반적인 엔지니어링 블로그에서 읽을 것으로 예상되는 내용입니다. 안타깝게도 그렇게 간단하지는 않습니다.

가격을 조심하세요

API 버전 관리는 매우 어렵습니다. 환상적 단순성은 일단 구현을 시작하면 빠르게 사라집니다. 안타깝게도 해당 주제에 대한 리소스가 놀라울 정도로 적기 때문에 인터넷에서는 실제로 경고하지 않습니다. 그들 중 절대 다수는 API 버전을 어디에 둘 것인지에 대해 논쟁을 벌이고 있지만 "어떻게 구현합니까?"라는 질문에 대답하려는 기사는 거의 없습니다. 가장 일반적인 것들은 다음과 같습니다:

  • 동일한 웹 애플리케이션의 여러 버전을 별도의 배포에 배치
  • 버전 간에 변경된 단일 경로 복사
  • 각 버전에 대해 버전이 지정된 전체 애플리케이션 복사

별도의 배포는 비용이 많이 들고 지원하기 어려울 수 있습니다. 단일 경로를 복사하는 것은 큰 변경 사항에 맞게 확장되지 않으며, 전체 애플리케이션을 복사하면 너무 많은 추가 코드가 생성되어 몇 가지 버전만 사용해도 빠져들게 됩니다.

가장 저렴한 것을 고르려고 해도 버전 관리에 대한 부담은 금방 따라잡을 것입니다. 처음에는 간단하게 느껴질 것입니다. 여기에 또 다른 스키마를 추가하고, 거기에 비즈니스 로직의 또 다른 분기를 추가하고, 마지막에 몇 개의 경로를 복제합니다. 그러나 버전이 충분하면 비즈니스 로직을 관리할 수 없게 되고, 많은 개발자가 애플리케이션 버전과 API 버전을 혼동하고, 데이터베이스 내의 데이터 버전 관리를 시작하게 되며, 애플리케이션을 유지 관리할 수 없게 됩니다.

동시에 2~3개 이상의 API 버전을 사용하지 않기를 바랄 수도 있습니다. 몇 달에 한 번씩 이전 버전을 삭제할 수 있습니다. 소수의 내부 소비자만 지원하는 경우에는 그렇습니다. 그러나 조직 외부의 클라이언트는 몇 달마다 강제로 업그레이드해야 하는 경험을 즐기지 못할 것입니다.

API 버전 관리는 인프라에서 가장 비용이 많이 드는 부분 중 하나가 될 수 있으므로 사전에 부지런히 조사하는 것이 중요합니다. 내부 소비자만 지원한다면 GraphQL과 같은 것을 사용하는 것이 더 간단할 수 있지만 버전 관리만큼 비용이 빨리 들 수 있습니다.

스타트업이라면 API 버전 관리를 제대로 수행할 수 있는 리소스가 있을 때 개발 후반 단계까지 API 버전 관리를 연기하는 것이 현명할 것입니다. 그때까지는 지원 중단 및 추가 변경 전략으로 충분할 수 있습니다. API가 항상 좋아 보이지는 않지만 적어도 명시적인 버전 관리를 피함으로써 상당한 비용을 절약할 수 있습니다.

API 버전 관리를 어떻게 구현합니까?

몇 번의 시행착오와 많은 오류 끝에 우리는 갈림길에 섰습니다. 위에서 언급한 이전 버전 관리 접근 방식은 유지 관리 비용이 너무 많이 들었습니다. 우리의 노력의 결과로 저는 완벽한 버전 관리 프레임워크에 필요한 다음 요구 사항 목록을 고안했습니다.

  1. "많은 수의 버전을 유지하는 것은 쉽습니다" 버전 관리로 인해 기능 개발 속도가 느려지지 않도록 합니다.
  2. "이전 버전을 삭제하는 것은 쉽습니다" 노력 없이 코드 베이스를 정리할 수 있습니다.
  3. "새 버전을 만드는 것은 그리 쉬운 일이 아닙니다" 개발자가 버전 없이 문제를 해결하려고 노력할 동기를 부여받을 수 있도록 하기 위한 것입니다.
  4. "버전 간 변경 로그를 유지하는 것은 쉽습니다" 우리와 고객 모두 버전 간의 실제 차이점을 항상 확신할 수 있습니다.

안타깝게도 기존 접근 방식에 대한 대안은 거의 또는 전혀 없었습니다. 이때 미친 아이디어가 떠올랐습니다. Stripe의 API 버전 관리와 같이 정교하고 작업에 완벽한 것을 구축하면 어떨까?

수많은 실험의 결과로 우리는 이제 Stripe의 접근 방식을 구현할 뿐만 아니라 이를 기반으로 구축하는 오픈 소스 API 버전 관리 프레임워크인 Cadwyn을 갖게 되었습니다. Fastapi 및 Pydantic 구현에 대해 이야기할 예정이지만 핵심 원칙은 언어 및 프레임워크에 구애받지 않습니다.

캐드윈의 작동 방식

버전 변경

다른 모든 버전 관리 접근 방식의 문제는 너무 많이 복제된다는 것입니다. 계약의 아주 작은 부분만 깨졌는데 왜 전체 경로, 컨트롤러, 심지어 애플리케이션까지 복제하겠습니까?

Cadwyn을 사용하면 API 관리자가 새 버전을 생성해야 할 때마다 최신 스키마, 모델 및 비즈니스 로직에 주요 변경 사항을 적용할 수 있습니다. 그런 다음 새 버전과 이전 버전 간의 모든 차이점을 캡슐화하는 클래스인 버전 변경을 만듭니다.

예를 들어 이전에는 클라이언트가 주소를 사용하여 사용자를 생성할 수 있었지만 이제는 단일 주소 대신 여러 주소를 지정할 수 있도록 허용하고 싶다고 가정해 보겠습니다. 버전 변경은 다음과 같습니다:

class ChangeUserAddressToAList(VersionChange):
    description = (
        "Renamed `User.address` to `User.addresses` and "
        "changed its type to an array of strings"
    )
    instructions_to_migrate_to_previous_version = (
        schema(User).field("addresses").didnt_exist,
        schema(User).field("address").existed_as(type=str),
    )

    @convert_request_to_next_version_for(UserCreateRequest)
    def change_address_to_multiple_items(request):
        request.body["addresses"] = [request.body.pop("address")]

    @convert_response_to_previous_version_for(UserResource)
    def change_addresses_to_single_item(response):
        response.body["address"] = response.body.pop("addresses")[0]

instructions_to_ migration_to_previous_version은 Cadwyn에서 이전 API 버전의 스키마에 대한 코드를 생성하는 데 사용되며 두 개의 변환기 기능은 우리가 원하는 만큼 많은 버전을 유지할 수 있게 해주는 트릭입니다. 프로세스는 다음과 같습니다.

  1. Cadwyn은 Change_address_to_multiple_items 변환기를 사용하여 이전 API 버전의 모든 사용자 요청을 최신 API 버전으로 변환하고 이를 비즈니스 로직으로 파이프합니다.
  2. 비즈니스 로직, API 응답 및 데이터베이스 모델은 항상 최신 API 버전에 맞게 조정됩니다. (물론 새 버전에서 삭제된 경우에도 이전 기능을 계속 지원해야 합니다.)
  3. 비즈니스 로직이 응답을 생성한 후 Cadwyn은 Change_addresses_to_single_item 변환기를 사용하여 이를 클라이언트 요청자가 현재 사용 중인 이전 API 버전으로 변환합니다.

API 관리자가 버전 변경을 생성한 후 이를 VersionBundle에 추가하여 이 VersionChange가 일부 버전에 포함될 것임을 Cadwyn에 알려야 합니다.

VersionBundle(
    Version(
        date(2023, 4, 27),
        ChangeUserAddressToAList
    ),
    Version(
        date(2023, 4, 12),
        CollapseUserAvatarInfoIntoAnID,
        MakeUserSurnameRequired,
    ),
    Version(date(2023, 3, 15)),
)

그렇습니다. 획기적인 변경 사항을 추가했지만 비즈니스 로직은 단일 버전, 즉 최신 버전만 처리합니다. 수십 개의 API 버전을 추가한 후에도 비즈니스 로직에는 버전 관리 로직, 지속적인 이름 변경, if 및 데이터 변환기가 없습니다.

버전 체인화

버전 변경은 API의 공개 인터페이스에 따라 달라지며 기존 API 버전에 주요 변경 사항을 추가하는 경우는 거의 없습니다. 이는 일단 버전을 출시하면 버전이 깨지지 않는다는 것을 의미합니다.

버전 변경은 버전 내의 주요 변경 사항을 설명하고 이전 버전 내에는 주요 변경 사항이 없기 때문에 버전 변경이 완전히 불변임을 확신할 수 있습니다. 즉, 변경할 이유가 전혀 없습니다. 불변 엔터티는 항상 진화하기 때문에 비즈니스 로직의 일부인 경우보다 유지 관리가 훨씬 쉽습니다. 버전 변경 사항도 차례로 적용됩니다. 즉, 모든 요청을 최신 버전으로 마이그레이션하고 모든 응답을 이전 버전으로 마이그레이션할 수 있는 버전 간에 변환기 체인을 형성합니다.

API Versioning at Monite

부작용

API 계약은 스키마와 필드보다 훨씬 더 복잡합니다. 이는 모든 엔드포인트, 상태 코드, 오류, 오류메시지, 심지어 비즈니스 로직 동작까지 구성됩니다. Cadwyn은 위에서 설명한 것과 동일한 DSL을 사용하여 끝점과 상태 코드를 처리하지만 오류와 비즈니스 로직 동작은 다른 이야기입니다. DSL을 사용하여 설명하는 것은 불가능하며 비즈니스 로직에 포함되어야 합니다.

이러한 버전 변경은 비즈니스 로직에 영향을 주기 때문에 다른 모든 변경보다 유지 관리 비용이 훨씬 더 많이 듭니다. 우리는 이 속성을 "부작용"이라고 부르며 유지 관리 부담 때문에 어떤 희생을 치르더라도 이를 피하려고 노력합니다. 비즈니스 로직을 수정하려는 모든 버전 변경은 부작용이 있는 것으로 표시되어야 합니다. 어떤 버전 변경이 "위험"한지 알 수 있는 방법이 됩니다.

class RequireCompanyAttachedForPayment(VersionChangeWithSideEffects):
    description = (
        "User must now have a company_id in their account "
        "if they want to make new payments"
    )

또한 API 유지관리자가 클라이언트 요청이 다음 부작용을 포함하는 API 버전을 사용하는지 확인할 수 있습니다.

if RequireCompanyToBeAttachedForPayment.is_applied:
    validate_company_id_is_attached(user)

은탄환은 없어

Cadwyn에는 많은 이점이 있습니다. 개발자의 부담을 크게 줄이고 인프라에 통합하여 변경 로그를 자동으로 생성하고 API 문서를 개선할 수 있습니다.

그러나 버전 관리에 대한 부담은 여전히 ​​존재하며 정교한 프레임워크라도 만능은 아닙니다. 우리는 꼭 필요한 경우에만 API 버전 관리를 사용하기 위해 최선을 다합니다. 또한 특별한 "API 위원회"를 구성하여 첫 번째 시도에서 API를 올바르게 만들려고 노력합니다. 모든 중요한 API 변경 사항은 구현이 시작되기 전에 최고의 개발자, 테스터 및 기술 작가가 검토합니다.

Stripe의 API 버전 관리 기사와 Cadwyn 구현 시 나에게 도움을 준 Brandur Leach에게 특별히 감사드립니다. 그의 도움 없이는 불가능했을 것입니다.

릴리스 선언문 이 글은 https://dev.to/monite/api-versioning-at-monite-3ba3?1 에서 복제하였습니다. 침해 내용이 있는 경우, [email protected]으로 연락하여 삭제하시기 바랍니다.
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3