본문 바로가기
BackEnd

API Versioning

by solanarey 2024. 9. 2.

자동목차

API Versioning 의 필요성

  • 하위 호환성 유지
    • API를 사용하는 사용자는 특정 API 인터페이스에 의존하게 됩니다. 만약 API가 변경되거나 새로운 기능이 추가되면서 기존의 인터페이스가 변경된다면 하위 호환성을 유지하지 않으면 기존 클라이언트에서 오류가 발생할 수 있습니다.
    • 버전 관리를 통해 이전 버전의 API를 유지하면서 새로운 기능을 추가한 버전을 제공할 수 있습니다.
  • 점진적인 기능 개선
    • API는 시간이 지남에 따라 개선되고 확장됩니다. 새로운 기능을 추가하거나 성능을 개선하거나 보안을 강화하는 등의 작업이 필요할 때 버전 관리를 통해 단계적으로 새로운 버전을 릴리스할 수 있습니다. 이를 통해 클라이언트는 자신에게 필요한 시점에 새 버전을 선택적으로 사용할 수 있습니다.
  • 기존 클라이언트 지원
    • 다양한 사용자가 API를 사용하고 있으며 이들 중 일부는 최신 버전으로 쉽게 업그레이드할 수 없는 경우가 많습니다. API 버전 관리를 통해 기존 클라이언트가 계속해서 안정적으로 동작할 수 있도록 지원할 수 있습니다.
  • API 수명 주기 관리
    • 모든 API는 수명 주기가 있으며, 버전 관리를 통해 API의 수명 주기를 체계적으로 관리할 수 있습니다. 이는 API의 기능이 오래된 경우 폐기하고, 새로운 기능으로 전환할 수 있도록 도와줍니다.
  • 비즈니스 요구사항 반영
    • 비즈니스 환경이 변화하면서 새로운 요구사항이 발생할 수 있습니다. API 버전 관리를 통해 이러한 요구사항을 반영한 새로운 버전을 릴리스하면서도 기존 비즈니스 논리를 보호할 수 있습니다.

API Versioning 종류와 그 방법

API 버저닝 방법에는 API URL을 활용하거나 params, 요청 헤더, accept 헤더 4가지 방법이 있습니다.

먼저 시작하기 전에 응답 데이터를 정의하기 위해 각각 버전1, 버전2의 응답 바디를 아래와 같이 정의하도록 하겠습니다.

{ // version 1
    "lastName": "Son"
    "firstName": "Seunggi"
}
{ // version 2
    "name" : {
        "lastName": "Son"
        "firstName": "Seunggi"
    }
}

위 json 형식과 매핑되는 클래스를 아래 코드와 같이 생성해줍시다.

@Getter
public class PersonV1 {
    private String firstName;
    private String lastName;

    public PersonV1(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
@Getter
public class PersonV2 {
    private Map<String, String> name;

    public PersonV2(String firstName, String lastName) {
            this.name = new HashMap<>();
        name.put("firstName", firstName);
        name.put("lastName", lastName);
    }
}

그럼 이제 api versioning 실습을 위한 준비가 되었습니다.

먼저 api url 을 통한 방법입니다.

API URL

말 그대로 api url 패턴을 통해 버저닝을 관리하는 방법입니다. url에 v1, v2, v3… 와 같이 버전을 포함시키는 방법입니다.

@RestController
public class VesioningPersonController {

    // url을 통해 버저닝
    @GetMapping("/v1/person")
    public PersonV1 getFirstVersionPerson() {
        return new PersonV1("Seung gi", "Son");
    }

    @GetMapping("/v2/person")
    public PersonV2 getSecondVersionPerson() {
        return new PersonV2("Seung gi", "Son");
    }

위의 코드를 보시면 /v1/person , /v2/person 과 같이 버전기록을 url에 포함 시키고있습니다.
/v1/person 으로 접근해보면

v1에 해당하는 응답 데이터인

{
    firstName: "Seung gi",
    lastName: "Son"
}

위 형식과 같이 출력이 되고

/v2/person 으로 접근을 하면

{
    name : {
        firstName: "Seunggi"
        lastName: "Son"
    }
}

위 형식과 같이 출력 되는 것을 볼 수 있을 것입니다.

RequestParam

다음은 매개변수를 통해 버저닝 하는 방법입니다.

// param을 통해 버저닝
@GetMapping(value = "/person", params = "version=1")
public PersonV1 getFirstVersionPersonWithRequestParam() {
    return new PersonV1("Seung gi", "Son");
}

@GetMapping(value = "/person", params = "version=2")
public PersonV2 getSecondVersionPersonWithRequestParam() {
    return new PersonV2("Seung gi", "Son");
}

위 코드 역시 간단합니다. params 속성에 version의 버전을 명시해주면 됩니다.

http://localhost:8080/person?version=1 혹은

http://localhost:8080/person?version=2 에 접근 하면 각 버전에 맞는 응답 데이터 형식을 볼 수 있을 것입니다.

Request Header

요청 헤더를 통한 방법입니다.

// 요청헤더를 통해 버저닝
@GetMapping(value = "/person/header", headers = "X-API-VERSION=1")
public PersonV1 getFistVersionPersonWithRequestHeader() {
    return new PersonV1("Seung gi", "Son");
}

@GetMapping(value = "/person/header", headers = "X-API-VERSION=2")
public PersonV2 getSecondVersionPersonWithRequestHeader() {
    return new PersonV2("Seung gi", "Son");
}

headers 속성에 X-API-VERSION=1 과 같이 명시해 줍니다. header의 key 값은 원하시는대로 명시해주셔도 됩니다.

위와 같이 header에 key value를 지정 해주고 요청을 해보면 해당 response를 받는 것을 확인할 수 있습니다.

Media Type

마지막으로 MediaType을 통해 버저닝 하는 방법입니다.

// MediaType을 통한 버저닝
@GetMapping(value = "/person/accept", produces = "application/vnd.company.app-v1+json")
public PersonV1 getFistVersionPersonWithAcceptHeader() {
    return new PersonV1("Seung gi", "Son");
}

@GetMapping(value = "/person/accept", produces = "application/vnd.company.app-v2+json")
public PersonV2 getSecondVersionPersonWithAcceptHeader() {
    return new PersonV2("Seung gi", "Son");
}

produces 라는 attribute 값에 application/vnd.company.app-v1+json 와 같은 값을 명시해줍니다.

application/vnd.company.app-v1+json 와 같은 형식은 정해져 있는 것은 아니지만 관례상 이와 같이 명시한다고 합니다.

 

위와 같이 accept header에 v2 버전으로 요청을 보냈을때 정상적으로 응답을 받을 수 있는 것을 확인할 수 있습니다.

API 버저닝 시 고려사항

  • api url 방식은 버전이 url에 직접적으로 명시되기 때문에 api url이 깔끔하지 않습니다.
  • 요청 헤더는 원래 버저닝을 위한 요소가 아닙니다. 또한, 클라이언트 사이드에서 헤더를 직접 설정해야 합니다.
  • 헤더에 기반한 api 생성을 지원 하지 않을 수도 있습니다. header 방식 이용 시 이에 대한 고려도 필요합니다.

이렇게 API 버전 관리 방법에 대하여 알아보았습니다. 실제로 x(구 트위터)에서는 api url 방식을 사용중이고, request param은 아마존, header는 마이크로소프트, media type은 깃허브에서 사용 중이라고 합니다.

api versioning이 왜 필요한지, 왜 해야하는지 그리고 개발 중인 팀과 프로젝트에 잘 어우러지게 고려하여 어떤 방식을 채택하면 좋을 지 잘 생각해보시면 좋을 것 같습니다.