API 서버 버전 업 이후 회고

강민석·2023년 3월 28일
0

1. 개요

오늘은 회사에서 운영중인 API의 버전들을 올리면서 느꼈던 점들에 대해서 회고해보려고 한다.

먼저 API서버의 특성에 대해 알아보자

  1. 운영중인 API는 사내 웹서비스에 기능을 제공하는 서버로, 하나의 프로젝트에 3가지의 서브모듈이 구성되어 있는 형태로 구성되어 있다.(프로젝트 - A 기능 API 모듈, B 기능 API 모듈, C 기능 API 모듈)
  2. 기능을 제공받는 클라이언트(웹서비스)는 다섯가지로, 유사하지만 제공해야하는 기능에서 약간의 차이를 보이는 경우가 있다.
  3. API서버는 클라이언트에서 사용하는 DB를 직접 바라보고 있다.
  4. 운영상으로 active-active로 이중화되어 있으며, L4 구성되어 있다.

이번 포스팅에서는 위의 특성들을 API내 버전을 올리는 관점에서 바라보고 느꼈던 장단점에 대해서 생각해 볼 계획이다.

2. 기능 변경

세가지의 API모듈들의 변경사항은 다 다르지만, 각 API의 전체적인 구성은 유사하므로 한번에 다뤄보면 좋을 것 같다. 해당 목차에서는 빠르게 변경사항에 대해서 알아보고 3장에서 변경사항에 대한 회고를 다뤄보도록 하겠다.

2.1 공통된 API 구성

세가지 API들은 모두 endpoints에 v1이라는 버전을 알리는 단어를 포함하고 있다. 이번 버전 업을 진행하면서 endpoints내에 버전을 포함하는 것이 필요한가?에 대한 고민을 하게 되었는데 3장에서 함께 다뤄보도록 하자.

2.2 A, B모듈의 변경사항

다음으로 가장 많은 변화를 겪은 A모듈에 대해 살펴보자. 변경사항은 아래와 같다.

  • endpoints url의 변경(v1 -> v2 외 기타 등등)
  • 특정 endpoints들의 request 파라미터 변경
  • 기존의 서비스와 기능에서의 차이를 보이는 신규 서비스의 추가
  • 관련 DB의 변경에 따른 로직 변경

2.3 C모듈의 변경사항

C모듈은 request의 특정 파라미터의 종류가 추가되었다.(자바의 입장에서 enum필드가 추가됨)
이전의 commit과 큰 차이점을 보이지 않아 따로 버저닝을 하지 않았다.

3. 변경 사항의 적용

전체적인 변경의 이해를 위해, 변경사항에 대한 내용은 2. 기능 변경에 작성해두었으므로 함께 읽어주시기 바랍니다.

3.1 버저닝

먼저 가장 큰 문제를 일으킨 개념은 버전이라는 개념이었다. v1 -> v2로의 변경은 내가 해당 프로젝트를 맡기전 전임자가 적용해두고 간 내용이였는데, v1과 v2가 각각 다른 서비스들(앞에 소개한바와 같이 5개의 서비스들에 기능을 제공하고 있다.)을 지원하게 끔 개발이 되어있었다.

이렇게 개발이 되다보니, API모듈의 관점에서는 v1, v2의 코드를 모두 가진채 디렉토리로 구분하고 있었고 유지보수에 어려움을 겪고있었다.(상당히 유사한 클래스명, 방대한 모듈 사이즈 등)

이런 구조를 보니 가장 괴리감을 느꼈던 부분은 형상 관리 툴의 활용도가 떨어진다는 점이였다.
우리는 프로젝트의 버전을 관리하고 개발하기 위해 git을 사용하는데 어플리케이션 코드 내에서도 버전을 관리하면서 git만을 사용하여 버전을 관리하기가 어려워졌다. 원래는 커밋을 통해 바뀌어야할 버전이 같은 커밋 내에서도 다양하게 존재할 수 있다는 것이였다.

이렇게 디렉토리로 구분해 버전을 관리 할 경우 생길 수 있는 문제들에 대해서 생각을 해보았다.

  1. v1에만 필요한 요구사항이 생긴다면? v2에만 필요한 요구사항이 생긴다면?: 각각 다른 버전이 다른 웹서비스를 지원하고 있었으므로 가능한 이야기이다. 결국 개발자는 버전이라는 이름으로 갈라진 두 개의 어플리케이션을 유지보수 할 뿐이다. 한 모듈이 더 많은 버전을 디렉토리로 구분한다면 더 복잡해질 것임은 자명하다.
    또한 처음에는 유사했던 v1과 v2는 갈수록 다른 모듈이 되어 갈 것이다.
  2. 커다란 변경사항이 생기면서 버전을 한단계 올려야 한다면?: 디렉토리 구조가 추가되며 관련 클래스 및 설정들이 생겨날 것이고 유지보수의 어려움을 초래하게 된다.
  3. 버전의 추가에 따른 중복되는 코드: 결국 API서버에서 버전을 올리는 방식을 이전 버전을 복사하여 확장하는 방식으로 진행하므로 중복된 코드들이 생기게 된다.
  4. 호환성의 부재: 추후 버전을 올릴 때, 변경사항이 크고 이전 버전을 호환 할 수 없으므로 상호운용성이 지켜질 수 없다.

위와 같은 문제때문에 v1의 디렉토리를 제거하고, v2에서 모든 서비스들을 호환 할 수 있도록 개발하기로 했다. 5개의 웹서비스들은 유사한 기능을 API서버로부터 제공받기 때문에, 또한 기획단에서 통일성을 가지는 협의가 가능하므로 이를 호환하는 비용이 크지 않겠다고 생각했기 때문이다.

따라서 최신버전에서 모든 서비스를 호환 할 수 있도록 개발하여 이후의 추가개발에서 상호운용성을 지킬 수 있는 상태를 만들고자 했다.

3.2 파라미터 변경

또 다른 변경사항은 request, reponse의 파라미터의 변경이 생긴 것이였다.
추가적인 기능이 생겨나면서 파라미터의 변경이 클라이언트에게 요구될 수 있다. 하지만 이런 경우도 상호운용성을 지키는 것이 중요한데, 이번 변경사항에서는 이를 완전히 지키지 못해 아쉬움이 있었다.

파라미터에 대해서 생각하며 느낀점은 초기 파라미터의 계획이 굉장히 중요하다는 점이다. 좋은 예시로 HTTP에서의 파라미터를 예시로 가져와보았다.

HTTP 역시 업데이트가 지속적으로 이루어지는 프로토콜로서, 상호운용성을 지키기 위한 노력을 하고있다.(HTTP의 상호운용성이 지켜지지 않으면 웹이 동작할 수 없음) 때문에 초기에 잘못된 설계를 계속하여 가져가는 경우가 있는데 이는 다음과 같다.

  1. Referer 헤더: 요청을 보낸 페이지의 주소를 가지는 헤더. 원래는 Referrer라고 작성해야하지만, 개발자의 오타로 Referer로 작성되었으며 20년이 넘는 시간동안 유지중
  2. charset: 바이트 인코딩 방식의 정의. 정의 당시에 encoding에 대한 개념이 알려지지 않아, charset으로 정의되어 변경없이 사용되고 있음
  3. HTTP status 418: 만우절에 만들었던 임시 상태값이였으나, 이를 정의하는 구현체들이 생겨나면서 걷잡을 수 없이 퍼지게 됨. 초기에는 HTTP 의장이 삭제를 해달라는 요청을 일일이했으나, 현재는 영구결번이 되고 말음

HTTP와 같이 대단한 프로토콜을 만드는 사람들도 위와 같은 실수를 하는 것을 보면,, 실수는 정말 누구나 하는 것이고 반면교사를 삼는 것이 중요한 것 같다.

위의 예시에서 알 수 있듯이, HTTP는 상호운용성과 올바른 정의의 선택지에서 상호운용성을 택하여 운영을 해오고 있다. 신중한 파라미터의 정의가 굉장히 중요하다는 일례라고 보여진다.

그렇다면 어떻게하면 유지보수 시 변경이 적은 파라미터 구성을 만들 수 있을까?

  1. 필수 필드와 선택 필드의 명확한 구분: 버전을 올리다보면, 추가적인 파라미터의 정의가 필요할 때가 있다. 여러 버전에서 문제없이 상호운용성을 지키려면 필수 필드와 선택 필드를 클라이언트에게 명확히 전달해야한다. 물론 이를 위해서는 API문서를 잘 작성해야한다.
  2. 필드의 종속관계 고려: 전달하거나 전달받는 데이터들의 종속관계를 명확히하면, 추후 추가적인 필드가 생겼을 때 유지보수가 용이해진다. 또한 JSON array등의 형태로 중첩된 구조를 피하기도 유리하다.

4. 글을 마치며

API서버의 개발을 해본 경험이 거의 없어 여러 레퍼런스를 찾아보면서 고민을 해보았지만, 많은 부족함이 있는 것 같다..

특정 언어나 프레임워크의 좋은 개발에 대한 책들이 많이 존재하지만 정답이 없는 것처럼 보다 넓은 범위의 개념인 서버의 개발은 더더욱 난해한 영역이라는 생각이 들었다.

때문에 더더욱 개발하는 프로젝트의 특성을 고려하여 이번 회고의 내용대로 개발을 해볼 계획이다. 물론 왜 이렇게 생각했었지 하며 추후 혀를 찰 수 있겠지만, 좋은 과정이 될 것이라고 생각한다.

0개의 댓글