어플리케이션 레벨 의사결정은 이제 반 정도 한 것 같다. 뭐 이렇게 자잘한 것까지 다 결정하냐 싶겠지만, 내가 맘대로 정하고 통보하는 것보단 나을 것 같았기 때문에, 그리고 꽤 재밌는 이야깃거리일 것 같아서 이렇게 하고 있다. 12챕터의 내용들은 딱히 몰라도 상관 없기 때문에, 맘에 안 들면 그냥 13챕터로 넘어가도록 하자.

의사결정 - 시각 데이터 저장 방식

배경과 요구사항

  • 데이터의 생성일과 최근 수정일을 기록하는 것은 비즈니스적 지표를 뽑아내고(ex - 1월에 가입한 사용자 수, 날짜별 신규 게시글 수 등), 서비스에서 사용하고(ex - 게시글 작성일자 표기, 마이페이지에서 사용자 가입일 표시 등), 관리의 관점에서 데이터의 변화를 추적할 수 있는 기준이 될 수 있기에 여러모로 의미가 크다. 이렇게 데이터와 관련된 시각 정보를 기록하는 것을 timestamping이라 하는데, 이것도 데이터 저장에 대해 정해진 룰이 없으면 나중에 큰 기술부채가 될 수 있다.
  • 글로벌 서비스 시 유연하게 대응할 수 있는 방식이어야 한다.
  • millisecond 단위까지 표현할 수 있어야 한다. 사실 이렇게 단위를 결정할 때는 기술 조직 전체의 협의를 진행하는 것이 좋지만, 기술 조직 전체가 나 혼자니까 내 맘대로 정했다.
  • MySQL에서 시각 데이터를 관리하기 위한 DATETIME 타입이 잘 인식할 수 있어야 한다.
  • human friendly한 포맷이면 좋다. 예를 들면 Unix time(1547580999)보다 시각 문자열(2019-01-15 19:36:39)이 낫다.
  • Unix time처럼 특정 시각에 문제를 발생시키는 포맷이 아니어야 한다. 구체적인 내용은 2038년 문제를 참고하자.

선택지

  • Unix time
  • Asia/Seoul 기준의 시각 문자열
  • UTC 기준의 시각 문자열
  • ISO 8601 format 문자열

의사결정

UTC 기준의 시각 문자열로 저장하도록 하자. 그 이유는,

  • Unix time은 2038년 문제를 일으키고(2038년까지 서비스가 살아있을지 모르겠지만), 사람이 읽기 힘들다.
  • 어느 timezone 기준으로 시각 데이터를 저장하는지에 대해 일관성만 보장되면 상관 없으니 그냥 KST를 써도 당장 문제는 없겠지만, UTC가 '국제 표준시'로서 기준점이 되기도 하고, local timezone에 의존하며 생길 수 있는 실수를 방어하기가 좋다. 시각을 다루는 대부분의 라이브러리가 제공하는 now()utcnow()의 차이인건데, 예를 들어 한국 region에서의 now()미국 버지니아 region에서의 now()는 각각 Asia/Seoul(UTC+9), America/Virgin(UTC-4) 기준의 현재 시각을 반환할 것이다. 코드가 실행되는 운영체제의 timezone 설정에 의존한다는 것이다. 그냥 애초에 시각 정보를 UTC 기준으로 생성하면 timezone이 어떻게 생겨먹었던 어디서든 동일한 시차(UTC+0)의 시각 데이터를 만들 것이므로 실수를 방지할 수 있을 것이다. 물론 반대로 now(timezone='Asia/Seoul')같은 코드를 통해 '애초에 KST 기준으로 생성하면 되지 않나'라고 생각할 수도 있겠지만, 위에서도 말했듯 UTC는 '국제 표준시'로서의 기준점이 되니 굳이 KST를 고집할 필요가 없다.
  • ISO 86012019-01-15T19:36:39+09:00같은 포맷인데, '시각은 이거고, 이 시각은 UTC에 얼마 만큼의 시간을 더하거나 빼서 만들어진건지'를 명시한다. 사실 이건 시각 데이터에 대한 표현 단에서 쓸모있는 포맷이라, 저장 단에서는 투머치다. 바로 아래 의사결정에서 한 번 더 얘기할 것이다.

의사결정 - 시각 데이터 표현 방식

배경과 요구사항

  • 필요에 따라, UTC 기준으로 저장된 시각 문자열을 API에서 내려줘야 할 일이 생길 것이다. 예를 들면 '게시글 목록' API에서 각 게시글의 생성일을 포함시켜 주는 것이다. 이에 따라 표현(representation)에 대한 결정이 필요하다.
  • timezone에 대해 자유로울 수록 좋다. 바로 위 의사결정에 있었던 '글로벌 서비스 시 유연하게 대응할 수 있는 방식이어야 한다.'라는 요구사항의 연장선이다. 프론트엔드 관점에서, API가 반환한 시각 문자열이 어느 timezone을 기준으로 했는지에 대해 신경쓸 필요가 없어야 한다.
  • 시간을 다루는 라이브러리들이 잘 파싱할 수 있는 포맷이어야 한다. 환경에 따라 케바케겠지만 190115-193639보단 2019-01-15 19:36:39가 더 안전할 것이라고 생각한다.
  • 현재의 어플리케이션 기술스택에 무리 없이 적용할 수 있는 포맷이어야 한다.

선택지

  • YYYY-MM-DD HH:mm:ss.SSS
  • Unix time
  • YY-MM-DD HH:mm:ss.SSS
  • ISO 8601 format(YYYY-MM-DDTHH:mm:ss.SSS±hh:mm)
  • RFC 3339 format

의사결정

ISO 8601 format으로 표현하도록 하자. 그 이유는,

  • ISO 8601날짜와 시간에 관련된 데이터 표현을 다루는 국제 표준이다. 애초에 ISO 8601의 ISO국제 표준화 기구를 의미한다. ISO 8601 format의 시각 문자열을 파싱 못하는 정신나간 환경은 없을 것이라 생각한다.
  • ISO 8601이 표준화한대로 시각을 표현하려면, 그레고리력 날짜24시간제에 기반하는 시간, 그리고 UTC와의 시간 간격(time interval)을 조합해 만든다. 글로벌(다중 timezone 위에서) 서비스를 한다고 했을 때, 시각 데이터에 대한 국제화를 위해 ISO 8601을 사용한다는 것은 충분히 납득 가능하다고 생각한다.
  • 어차피 UTC로 저장했으니 그냥 그대로 YYYY-MM-DD HH:mm:ss.SSS 포맷을 써도 될 것 같기도 하다. 그러나 '시각 문자열이 UTC다'라는 것은 단지 내부적으로 정한 룰일 뿐이고, 데이터에 이를 명시할 수 있는 방법이 뻔히 존재하므로 충분히 써먹도록 하자.
  • RFC 3339ISO 8601과 비슷하게 생긴 시각 데이터 표현 표준이다. ISO 8601의 부분집합이라고 보면 되는게, 두 글자로 이루어진 연도 표현(2018을 18로 표현)이 불가능하고 + 마침표 문자를 millisecond 하위 단위에서만 사용할 수 있고 + date와 time을 구분하는 시간 지정자인 'T'를 대신해 공백을 허용하는 것 정도가 핵심적인 차이점이며 나머지는 모두 ISO 8601과 동일한 스펙이다. 조금 더 완전한 표현(complete representation)을 사용하도록 제약한다는 것인데, 이는 의사결정의 요구사항을 만족하는 데에 추가적인 기여를 딱히 하지 않는다. 게다가 Python에서 자주 쓰이는 시간 관련 라이브러리인 datetime과 arrow를 따져 보면, 둘 다 RFC 3339 format을 '제대로 지원'하고 있지 않다. isoformat()은 지원하더라도 rfcformat()은 지원하지 않는다는 것이다. 어차피 두 글자로 이루어진 연도 표현을 피하는 것 정도만 지켜주면 되는데, datetime이나 arrow나 isoformat()은 이미 이게 포함되어 있는 상태다. Golang은 RFC 3339를 자주 써서, 우리가 결정한 어플리케이션 기술스택이 그 쪽이었다면 RFC를 썼을텐데 모든 상황이 우리가 ISO 8601을 쓰도록 만들고 있다.
  • ISO 8601과 RFC 3339를 제외한 나머지 선택지들은, '이 시각 데이터가 어느 timezone을 기준으로 했는지'를 신경써야 할 수밖에 없다. 이렇게 되면 결국은 프론트엔드 레벨에서 시각 데이터에 timezone 정보, 혹은 특정 timezone과의 시간 간격 정보UTC+9와 같이 별도로 추가해서 쓰게 될텐데, timezone에 대한 명시를 백엔드가 해주면 'API가 내려주는 모든 시각 데이터는 UTC 기준이다.'처럼 내부적인 룰을 귀찮게 따로 얘기하지 않아도 되니 더 낫고, 이를 위해 ISO 8601이 좋은 해결책이 되어줄 것이라고 생각했다. 이미 '시간 간격은 UTC를 기준으로 한다'는 게 표준으로서 정의되었으니 말이다.
  • 부가적인 옵션이긴 하지만, 사람이 읽기 좋다는 것도 이유 중 하나다.

의사결정 - JSON key 네이밍 룰

배경과 요구사항

  • 우리가 사용하기로 한 직렬화 포맷인 JSON의 핵심 타입 중엔 Object라는 타입이 있는데, 이는 key-value 매핑으로 데이터를 나열하도록 되어 있다. Python의 DictionaryJava의 Map을 떠올리면 되는데, '사용자'의 데이터를 표현하기 위해 'id', 'password', 'nickname', 'email'같은 key에 값들을 달아주는 것을 예로 들 수 있다. 이러한 key들은 'signin_date'처럼 여러 단어가 섞이는 경우도 생긴다. 이러한 '이름'들에 대한 표기 룰을 정해둬야 한다. '이름을 짓는 방법'이라기보단 'key에서 단어와 단어 사이를 어떻게 구분할 것인지'를 결정한다고 생각하면 될 것 같다.
  • 코딩을 조금 해 봤다면 '이름을 잘 짓는 것'과 '이름 짓기에 일관성을 지키는 것'은 생각보다 꽤 중요하다는 것을 이미 깨달았을 것이다.
  • 프론트엔드가 사용하는 네이밍 룰과 섞였을 때, 위화감이 적거나 없어야 하며 관례를 고려하자. Python이 어떤 네이밍 룰을 쓰는지는 상관 없다.

선택지

  • 신경쓰지 않는다. ex) 'signindate'
  • 공백으로 분리한다. ex) 'signin date'
  • 카멜 케이스(Camel Case)를 사용한다. ex) 'signinDate'
  • 파스칼 케이스(Pascal Case)를 사용한다. ex) 'SigninDate'
  • 스네이크 케이스(Snake Case)를 사용한다. ex) 'signin_date'

의사결정

카멜 케이스를 사용하자. 그 이유는,

  • 프론트엔드에선 이러한 데이터 처리 로직들을 JavaScriptTypeScript로 처리하게 될텐데, 대부분의 인지도 있는 코드 가이드라인들은 Object의 key를 네이밍할 때 camel case를 사용하도록 권고하고 있다.
  • 모바일 어플리케이션에서도 변수 네이밍에 camel case를 사용하는 가이드라인이 지배적이다. 모바일 어플리케이션 개발에 일반적으로 사용하는 Java, Kotlin, Objective-C, Swift가 그렇다. 이 쪽에도 변수 네이밍 룰을 통일해 주면 좋을 것 같았다. val signinDate = payload['signinDate']val signinDate = payload['signin_date']보다 나을 것이다.
  • Python은 변수 네이밍에 snake case를 사용하지만, 프론트엔드를 배려하고 관례를 지키도록 하자는 입장이다.

일러두기 - JWT 관리 방식

JWT 관리 방식에 대해서는 필자도 정확히 아는 입장이 아닌지라 쉽게 얘기할 수 있을 것 같지 않고, 내용 자체도 길어질 것 같아서 JWT에 관한 의사결정과 코드 업데이트는 나중으로 미뤄서 별도의 챕터로 작성할 예정이다. 당장은 'JWT를 제대로 못 쓰는' 상태'일단 동작하게 만들기'를 먼저 해보자.

일러두기 - 사용할 라이브러리

WAS를 개발하기 위해 사용할 라이브러리는 다음과 같다. Pipfile에 리스팅될 라이브러리의 목록이라고 이해하면 되며, 의사결정을 거하게 할 필요가 없는 것 같아서 한 곳에 정리한다.

  • Flask : 9. Compute Engine 결정과 Hello World 서버 배포에서 이야기했다.
  • sqlalchemy, mysqlclient : 12. 어플리케이션 레벨 의사결정 - (1)에서 이야기했다.
  • arrow : 시간에 관한 로직을 잘 처리하기 위해 사용한다. 딱히 길게 얘기할 것 없이, Python에 내장되어 있는 시간 라이브러리인 datetime보다 훨씬 낫다.
  • flask-jwt-extended : JWT를 관리하기 위해 사용한다. pyjwt를 써서 비교적 low-level로 JWT를 관리하면 조금 더 많은 일들을 할 수 있지만, 굳이 그렇게까지 할 필요가 없다. flask-jwt라는 라이브러리가 한동안 star 수가 더 많았는데, 개발이 제대로 진행되고 있지 않고 있는데다 라이브러리의 컨셉 자체가 그렇게 좋은 편이 아니었어서 오래 전에 flask-jwt-extended에게 역전당했다.
  • flask-restful : API 로직을 class 단위로 작성할 수 있게 해주는 flask의 MethodView를 조금 더 확장한 라이브러리다. 더 탄탄한 어플리케이션 구조를 만드는 데에 큰 도움을 준다.

일러두기 - 민감한 데이터 관리 방식

데이터베이스 접속 비밀번호같은 민감한 데이터들은, 아무리 private repository라고 하더라도 소스코드 바깥에서 관리하는 게 좋다. 이전에 AWS well architecture에 관한 글을 읽었을 때, '깃허브가 해킹당하면 private도 의미가 없다'라는 내용이 있었다. 이러한 데이터들을 어디에 관리할지는 사람마다 다른데, 이 내용은 별도의 챕터로 분리하는 것으로 하고, 당장은 환경 변수에서 관리하도록 하자. AWS Lambda Management Console에 들어가서 이전에 배포해 두었던 함수를 선택한 후 '환경 변수' 섹션을 통해 데이터를 넣어 두겠다. RDS MySQL의 엔드포인트사용자 이름, 비밀번호를 각각 DB_ENDPOINT, DB_USER, DB_PASSWORD라는 이름으로 추가했다.

값들은 그대로 따라하지 말고, 독자 여러분의 상황에 따라 입력해 두자. 엔드포인트는 RDS Management Console에 들어가서 DB 인스턴스를 클릭하면 볼 수 있고, 사용자 이름과 비밀번호는 이전에 RDS 인스턴스를 만들며 입력해 두었던 마스터 사용자 이름과 마스터 암호를 사용하자.

StatsD는 로그 메트릭을 수집하는 영역에서 ProxySQL과 비슷한 용도로 쓰인다. UDPTCP를 통해 countertime과 같은 통계 데이터를 받아, Graphite같은 backend에 전송하는 Proxy다. 따라서 ProxySQL처럼 connection 중계가 아니라 통계적인 데이터 집계 처리 용도의 proxy로서 사용된다.

조금 더 쉽게 설명하면, StatsD는 메소드 호출 빈도와 소요 시간, 서비스 내에서 사용자가 일으키는 conversion(로그인, 장바구니 담기, 결제 등) 수 등을 MongoDB, MySQL, Datadog, zabbix, influxdb와 같은 backend에 적절히 기록한다. Graphite와 가장 케미가 좋다. (StatsD가 지원하는 backend 목록)

StatsD가 주는 메리트

ProxySQL처럼 auto failover와 같은 high availability 지원도 안 되는데 왜 굳이 쓰나? 싶겠지만, 애초에 StatsD를 비롯한 proxy 구조 자체의 이점이 있다. 바로 client-side 지원이다.

만약 InfluxDB로 로그 메트릭을 수집하고자 하더라도, 사용하는 언어에 안정적인 InfluxDB client 라이브러리가 없다면 번거로울 수밖에 없다. 이 경우 StatsD같이 InfluxDB를 backend로 지원하는 프록시 라이브러리를 사용하면 되고, 없더라도 구현하기 비교적 더 쉽다. TCP/UDP 소켓 기반으로 간결하게 통신할 수 있도록 설계되어 있기 때문이다. 그리고 StatsD는 TCP/UDP 프로토콜과 꽤 밀접하게 붙어 있기에, HAProxy같은 소프트웨어 로드 밸런서를 통해 high availability도 보장해줄 수 있다.

추가적으로 StatsD는 통계 데이터 집계 면에서 우수하다. StatsD는 데이터를 모아 두다가 일정 시간마다 집계 처리를 하고, 이들을 한번에 backend로 전송한 후 StatsD의 데이터를 모두 비운다. 이 때문에 로그를 visualize하기 이전에 통계 데이터를 미리 집계해둘 수 있다. 들어오는 데이터를 모두 실시간으로 전송하는 것보다, 잠시동안 모아 뒀다가 집계 작업 후 한번에 전송하는 게 추후의 집계 작업을 위한 컴퓨팅 비용이나 네트워크 비용 면에서 우수하다.

Metric의 형태

조금 추상적인 단어긴 한데, 어떤 데이터가 오가는 형태를 metric이라고 부를 수 있을 것 같다. StatsD도 데이터를 표현하기 위한 포맷이 존재하고, 아래와 같다.

<bucket>:<value>|<type>

예를 들면, gorets:1|c가 될 수 있다. 'gorets'로 명시된 bucket은 namespace 개념이다. 그냥 key라고 생각해도 된다. bucket은 StatsD에서 이야기하는 이름이고, metric name이라고 말할 수도 있다. 구분자(|) 뒤에 'c'로 명시된 type은 StatsD에서 제공하는 타입들 중 하나다.

StatsD는 데이터를 잠시 보관해 뒀다가 일정 시간마다 이들을 backend에 전송한다고 했다. 이와 같은 pipelining을 StatsD에선 flush interval이라 부르며, 기본값은 10초다.

Metric Type

StatsD에는 4개의 metric type(Counting, Timing, Gauges, Sets)이 있다.

Counting(:|c)

'c'로 표현한다. method_check_id_duplicated:1|c처럼 사용할 수 있다. 이는 간단한 카운터로, bucket(metric name)에 value를 더하는 작업이다. 메소드나 API 호출 수 등에 사용할 수 있다. StatsD에선 bucket마다 카운트 값을 누적하고 있다가, flush interval마다 backend에 카운트의 합을 전송하고 StatsD에선 count를 0으로 재설정한다.

Timing(:|ms)

'ms'로 표현한다. api_user_auth:82|ms처럼 사용할 수 있다. 이 경우, metric name인 api_user_auth를 위해 value인 82ms가 소요되었다는 의미이다. API별 소요 시간 등에 사용할 수 있다. StatsD에 이 데이터를 쌓으면, flush interval마다 평균, 표준 편차, 합계, 상한, 하한을 계산하여 backend에 이들을 전송한다.

Gauges(:|g)

'g'로 표현한다. goroutines_a:-8|g처럼 사용할 수 있다. 말 그대로 '게이지'를 표현하기 위해 사용하며, counting에 비해 값을 감소시킬 수 있어서 하드웨어 사용량같은 측정 값이나 스레드 갯수처럼 위아래로 이동할 수 있는 counter 등에 사용할 수 있다.

Sets(:|s)

's'로 표현한다. user:planb|s처럼 사용할 수 있다. flush interval마다 bucket에 전달된 고유한 value의 수를 계산하여 backend로 전송한다. SQL로 따지면 distinct count라고 생각하면 된다. MAU나 DAU 등을 뽑아낼 때 사용할 수 있을 것 같다.

'배경지식' 카테고리의 다른 글

ProxySQL  (1) 2019.02.12
직렬화와 JSON  (0) 2019.02.12
ORM  (0) 2019.02.12
HTTP 메소드  (0) 2019.02.12
HTTP 헤더  (0) 2018.11.02

API 스펙 설계가 끝났으니 이제 프론트엔드 팀에게 전해줄 문서를 작성해야 한다. 이거야 뭐 대충 마크다운같은 걸로 열심히 시간 쏟아서 정리해도 되는 부분이지만, 더 나은 방법이 없을지부터 고민해 보자. 이번 챕터에서는 API 문서화 방식을 결정한다.

의사결정 - API 문서화 방식

난 처음에 엑셀로 API를 문서화했다. 메소드 URI, 요청 파라미터, 응답 status code별 설명, 응답 body 등등을 컬럼으로 두고 내용을 채웠었다.

이게 어떤 문제가 있냐면,

  • 변경을 추적하기 어렵다.
  • 변경이 생길 때마다 프론트엔드에게 새로운 파일을 전달해줘야 한다. 대안으로 Google Sheets같은 중앙화된 문서화 도구를 쓰는 방법이 있다.
  • 엑셀과 같은 모던한 문서화 도구들은 중복을 추상화하기 어렵다. 예를 들어, '게시글 작성'과 '댓글 작성' API의 response body 포맷이 동일하다고 치면, API 문서화에 특화된 도구들은 이걸 따로 분리해서 참조할 수 있게 만들어져 있다. 중복 제거는 통일성 있는 확장을 위해 중요한데, 이걸 포기하는 셈이다.
  • 가독성이 나쁘다. 비슷한 endpoint끼리 묶어서 카테고리화 시키고, 설명에 테이블이나 리스트와 같은 HTML 기반의 컨텐츠를 추가하는 등의 일이 힘들다(적어도 엑셀 많이 안 다뤄본 나로서는).

그냥 API 문서화 도구의 결과물을 사진으로 만나보자.

HTTP API의 문서화 방식을 표준화시키기 위해, yaml 파일로 작성하는 형태의 OpenAPI라는 스펙이 존재하며 거의 모든 API 문서화 도구들은 이런 OpenAPI 스펙에 대응되어 있다. 따라서, OpenAPI 스펙에 따라 작성한 API 문서를 시각화해줄 도구를 선택해야 한다. 간소화된 문법으로 작성한 문서를 OpenAPI 포맷으로 convert해주는 도구라면 더 좋고. 또는 OpenAPI 신경 안쓰고 GUI 방식으로 문서를 작성하는 자체적인 문서화 서비스일 수도 있다.

배경과 요구사항

  • 변경을 추적하기 쉬워야(버전 관리가 가능해야) 한다. Git으로 관리할 수 있는 것이라면 더 좋다.
  • 문서를 작성하는 일이 고통스럽지 않아야 한다. raw한 OpenAPI 3.0 spec은 유지보수하기 정말 쉽지 않다.
  • UI가 예뻐야 한다.
  • Private로 관리할 수 있어야 한다.

선택지

의사결정

GitBook을 사용하겠다. 그 이유는,

  • Excel을 쓰는 방식은 너무 구리다.(위에서 말했던 것처럼)
  • SwaggerHub는 OpenAPI 스펙 그대로 문서를 정의해야 한다. 따라서 모든 API를 한 페이지(파일)에서 관리해야 하는데, API 한 대여섯개만 넣어도 가볍게 1000줄이 넘어가서 관리가 힘들다.
  • 소스코드에 임베딩하는 방식은, 라이브러리 단에서 HTML+CSS 리소스 + 소스코드에서 추출한 문서 정보를 가지고 있다가 /docs 같은 uri에서 문서를 웹으로 서빙하는 형태다. 유명한 것으로는 Flask 기반의 flasgger가 있다. 소스코드에 문서가 포함되어 있으니, 리뷰 과정에서 API 스펙이 변경되었을 때 이를 문서에도 반영했는지 볼 수 있어서 좋고 대부분 API 각각에 문서가 주입되는 형태라서 가독성도 괜찮다. 그러나 API 문서의 수정이 필요할 때마다 어플리케이션 전체를 다시 빌드하고 배포해야 해서 리스크가 크다. 따로 분리하는 것이 좋겠다고 생각했다.
  • ReDoc과 Slate는 OpenAPI 스펙에 맞춰진 문서를 조금 더 간소화된 문법으로 작성할 수 있어서 좋은데, 직접 관리하는 경우 조금 귀찮다. Amazon S3 website같이 스토리지 서비스에서 정적 웹사이트를 호스팅하는 형태로 관리하게 될텐데, 변경을 자동으로 배포하기 위해 배포 자동화를 설정해야 할테고, API가 외부로 공개되어야 한다면 status check도 붙어야 하고, 상황에 따라 ReDoc이나 Slate에서 제공하는 React App을 커스텀하기도 해야 한다. 관리 포인트가 늘어나기 때문에 보류.
  • 관리 포인트가 감당 가능하다 하더라도, ReDoc과 Slate가 간소화한 문법을 쓰는 것이 사실 경험 상 문서를 작성하는 데에 생산성이 그렇게 비교될 정도로 좋지는 않았고 썩 즐겁지도 않았다.
  • 관리 포인트를 줄인답시고 ReDoc과 Slate를 GitHub Pages에 올리는 경우, 문서를 private로 관리할 수 없다.
  • GitBook은 문서 작성에 대한 스트레스가 비교적 적다. GUI로 작성하기 때문. 내부적으로 OpenAPI 스펙으로 관리하고 있지는 않고 그냥 자체적인 API 문서화 툴이다. 과거에도 여기저기서 GUI 기반의 API 문서화 플랫폼을 개발하고자 하는 시도가 많았지만 조잡했어서 안 썼는데, 최근에 들어가 본 GitBook은 괜찮았던 것 같다.

OpenAPI에는 $ref라는 문법으로 중복을 관리할 수 있어서 좋은데, GitBook은 그렇지 않다. 하지만 편히 문서를 작성할 수 있으니 트레이드오프 한다고 생각하자.

준비

GitBook에 로그인하고, workspace를 만들어서 API를 문서화하자.

지금까지

  • 버전 관리 시스템으로 Git을 사용하기 시작했다.
  • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
  • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
  • 개발 프로세스와 브랜칭 모델을 정립했다.
  • HTTP API 아키텍처 기반으로 API 스펙을 디자인하기로 했다.
  • JSON을 직렬화 포맷으로 결정했다.
  • Authorization 헤더로 인증 정보를 명시하기로 했다.
  • 인증 스키마에 JWT 기반의 Bearer를 사용하기로 했다.
  • API 스펙을 정의했다.
  • GitBook으로 API를 문서화했다.


+ Recent posts