더 자유롭고, 빠르고, 정확하게: 토스페이먼츠 API 문서 엔지니어링
다른 회사나 서비스에서 만든 API를 사용해 본 경험이 있으신가요? 복잡한 API를 파악하거나 새로운 업데이트를 적용하려고 할 때 설명이 정확하지 않다면 API를 적용하는 데 어려움을 겪죠. 그래서 API를 외부 개발자에게 제공할 때, 명확하고 쉽게 이해할 수 있는 문서는 필수적이에요.
토스페이먼츠는 최고의 문서 경험을 제공하기 위해 자체적인 문서 엔지니어링 시스템을 갖추고 있어요. 엔지니어링 스택으로는 MDX와 OAS(Open API Specification), 마크다운 변환 라이브러리인 remark를 사용하는데요. MDX를 사용하기 때문에 테크니컬 라이터가 다양한 컴포넌트를 사용해 자유롭게 문서를 구성할 수 있고, OAS는 API 하나를 문서에 업데이트하는데 드는 시간과 노력을 줄여줘요. remark와 결합해서 문서 내용 일부를 자동화도 해요. 그럼 지금부터 어떻게 토스페이먼츠 문서 작성이 더 자유롭고, 빠르고, 정확해졌는지 하나씩 알려드릴게요.
더 자유롭게: MDX로 문서 구성하기
문서의 근간은 텍스트 콘텐츠죠. 하지만 테크니컬 라이터들은 사용자가 더 이해하기 쉬운 문서로 만들기 위해 여러 가지 구성을 시도해요. 예를 들어 레이아웃을 만들어서 콘텐츠 구조를 더 보기 좋게 만들거나, 배너나 버튼으로 사용자 행동을 유도할 수 있어요. 토스페이먼츠에서는 테크니컬 라이터들이 이런 콘텐츠 구성을 자유롭게 할 수 있도록 문서 작성에 MDX(Markdown + JSX)를 사용해요. MDX는 Markdown 문서에 React 컴포넌트를 삽입할 수 있게 해주는 포맷이에요. MDX를 사용하면 React 기반의 프레임워크나 사이트에서 동적인 컴포넌트를 Markdown 문서에 쉽게 추가할 수 있죠.
토스페이먼츠 문서 중 자동결제(빌링) 이해하기 페이지를 예로 들어 볼게요. 아래 MDX 문서를 보면, #
로 제목을 나타내고 ![]()
로 이미지를 삽입하는 마크다운 형식을 따르고 있는데요. 중간에 <Grid>
, <IconCard>
같은 컴포넌트 태그도 보이네요. <Grid>
는 토스 디자인 시스템에서 제공하는 컴포넌트로 마크업 없이 그리드를 나눌 수 있게 해줘요. <IconCard>
는 콘텐츠를 효과적으로 제안하는 컴포넌트로, 독자가 제안된 콘텐츠에서 기대할 수 있는 사항에 대한 설명이 들어있죠. 이런 식으로 필요한 태그를 추가하면서 문서를 자유롭게 구성할 수 있어요.
API 문서도 마찬가지예요. API 구조를 만드는 <EndpointSchema>
내부에 <EndpointSignature>
, <TryApiButton>
같은 각종 컴포넌트를 추가하면 오른쪽과 같은 결과물이 나오죠. 조금 복잡해 보이지만 하나씩 살펴보면 쉬워요.
그런데 왼쪽 MDX에 직접 작성하지 않은 파라미터 정보인 필수 여부나 데이터 타입도 오른쪽 결과 화면에 렌더되어 있네요. 작성하지 않은 파라미터 스펙이 어떻게 자동으로 나오는 걸까요?
더 빠르게: OAS로 API 문서 생성하기
OAS를 사용한 덕분인데요. OAS는 RESTful API의 표준 인터페이스를 설명하는 명세 방식이에요. 토스페이먼츠 API는 모두 오픈 API이기도 하고, 앞서 말한 효율화를 위해 OAS 스펙을 기반으로 구성해요. OAS는 다음과 같이 기본 정보, 태그(tag), 경로(path), 구성요소(components) 영역으로 구성되어 있어요.
# 기본 정보
openapi: 3.0.0
servers:
- url: 'http://petstore.swagger.io/v2'
info:
... 생략
tags:
- name: pet
description: Everything about your Pets
... 생략
# URI 정보
paths:
/pet:
post:
... 생략
# 컴포넌트 영역 (스키마 영역)
components:
requestBodies:
... 생략
schemas:
... 생략
기본 정보에는 Open API 버전과 서버 정보 등을, URI 정보에는 각 API의 URI와 메소드를, 컴포넌트 영역에는 스키마와 요청 바디 등의 구성요소를 정의할 수 있죠.
예를 들어 OAS가 다음과 같이 작성되어 있을 때, ‘결제 승인 API’의 엔드포인트를 찾아보면 어떤 메서드(post
)이며 요청 바디는 어떻게 생겼는지(requestBody
), 응답은 어떻게 돌아오는지(response
) 등 API에 관련된 정보를 쉽게 확인할 수 있어요.
이 OAS 구조를 더 적극적으로 활용하기 위해 추가한 워크플로우도 있는데요.
API 변경이나 추가로 서버 코드에 생긴 변화(예:feature/update-api
)가 서버 레포지토리의 main
브랜치로 머지돼요. 그러면 GitHub Actions를 통해 문서 레포지토리에 자동으로 다음과 같은 스키마 업데이트 PR이 만들어 집니다.
이제 테크니컬 라이터가 리뷰한 새로운 API 스펙 PR을 문서 레포지토리에 머지하고 문서 콘텐츠를 준비하면 됩니다.
이 워크플로우를 활용하면 테크니컬 라이터는 어떤 엔드포인트가 추가됐는지, 해당 엔드포인트의 요청과 응답은 어떤 스키마 형태인지, 에러 코드는 무엇인지 전부 파악하고 바로 문서에 적용할 수 있어요. OAS를 사용하고, 문서에 반영되어야 하는 변경사항을 PR로 자동화함으로써 스펙 전달 및 이해를 위한 과정이 훨씬 간결해졌죠.
더 정확하게: MDX와 OAS의 결합과 remark 플러그인 활용
각 API 엔드포인트에 대한 상세한 정보를 포함하고 있는 OAS 스키마에는 파라미터의 타입, 필수 여부, 그리고 null 여부 등이 있는데요. 이런 OAS 정보를 활용해서 어떻게 문서 콘텐츠를 자동화 할 수 있는지 알아볼게요.
- 테크니컬 라이터가 MDX 파일에
EndpointSchema
태그와 파라미터의 이름, 기본 설명을 작성해요.<EndpointSchema id="PaymentConfirmRequestDto" method="GET" path="/v1/payments/{paymentKey}"> ... #### Request Body 파라미터 - **paymentKey** 결제의 키 값입니다. 최대 길이는 200자입니다. 결제를 식별하는 역할로, 중복되지 않는 고유한 값입니다. - **orderId** 주문 ID입니다. 주문한 결제를 식별합니다. 충분히 무작위한 값을 생성해서 각 주문마다 고유한 값을 넣어주세요. 영문 대소문자, 숫자, 특수문자 `-`, `_`로 이루어진 6자 이상 64자 이하의 문자열이어야 합니다. 결제 데이터 관리를 위해 반드시 저장해야 합니다. - **amount** 결제할 금액입니다. ... </EndpointSchema>
- 작성한
EndpointSchema
컴포넌트가 MDX에 작성된list
노드(- **paymentKey** 결제의 키 값입니다. …
같은 값이죠.)를 찾고<SchemaProperties />
컴포넌트 형태로 재구성해요. 아래와 같이요.{ type: `mdxJsxFlowElement`, name: `SchemaProperties`, children: [ listItem 노드들 ], data: { _mdxExplicitJsx: true } }
- 이제
list
노드의 자식인listItem
노드를 순회하면서<SchemaProperty name={property} nullable={nullable}>{설명}</SchemaProperty>
형식의 컴포넌트 노드를 만들어요. 그런 뒤 다음과 같이SchemaProperties
의 children으로 해당 노드를 추가합니다.{ type: `mdxJsxFlowElement`, name: `SchemaProperty`, children: [ 설명 노드 ], attributes: [ { type: `mdxJsxAttribute`, name:`name`, value: property }, { type: `mdxJsxAttribute`, name: `nullable`, value: { type: 'mdxJsxAttributeValueExpression', data: { estree: { sourceType: 'module', type: 'Program', body: [ { type: 'ExpressionStatement', expression: { type: 'Literal', nullable, // boolean }, }, ], }, }, }], data: { _mdxExplicitJsx: true } }
EndpointSchema
가 컴포넌트에 지정된id
와 일치하는 엔드포인트의 API 정보를 OAS 스키마에서 찾아오고, 컨텍스트에 넣어 처리합니다.EndpointSchema
는 한 번만 적용하면 문서 내에서 반복적으로 사용되는 컴포넌트를 자동으로 처리할 수 있어요.- 이렇게 변경된 AST의 결과물을 JSX로 표현하면 아래처럼 바뀝니다. 파라미터의 데이터 타입과 필수 여부는 remark를 사용해 마크다운 문법을 변환하고, 변환된 마크다운은 JSX를 통해 웹 페이지에 표시될 형식으로 재구성됩니다.
**#### Request Body 파라미터** <RequestBody> <RequestBodyParameter name="paymentKey"> 결제의 키값입니다. 최대 길이는 200자입니다. 결제를 식별하는 역할로, 중복되지 않는 고유한 값입니다. </RequestBodyParameter> <RequestBodyParameter name="orderId"> 주문 ID입니다. 주문한 결제를 식별합니다. 충분히 무작위한 값을 생성해서 각 주문마다 고유한 값을 넣어주세요. 영문 대소문자, 숫자, 특수문자 `-`, `_`로 이루어진 6자 이상 64자 이하의 문자열이어야 합니다. 결제 데이터 관리를 위해 반드시 저장해야 합니다. </RequestBodyParameter> <RequestBodyParameter name="amount"> 결제할 금액입니다. </RequestBodyParameter> </RequestBody>
OAS로 알 수 있는 나머지 정보는 React 컴포넌트가 알아서 추가해요. 파라미터 이름과 설명만 작성하면 나머지는 알아서 처리해 주니까 테크니컬 라이터들은 반복 작업에 시간을 낭비하지 않게 됐고, 문서의 정확도도 높아졌죠.
자유롭고 풍부한 콘텐츠 구성, 높은 정확도, 그리고 사람이 하는 일은 최소화하는 토스페이먼츠의 API 문서 엔지니어링에 대해 소개해 봤어요. 엔지니어들이 구축해 둔 이런 문서 엔지니어링 시스템 덕분에 테크니컬 라이터는 불필요한 일은 덜고 더 좋은 문서를 만드는 것에만 집중할 수 있어요. 이런 환경이 곧 최종 사용자에게 더 나은 문서를 제공하는 데 도움을 준다고 생각해요.
토스페이먼츠 개발자센터에는 API 문서 외에도 다양한 문서 엔지니어링이 적용되어 있답니다. 더 알고 싶은 내용이 있다면 아래 의견 남기기를 통해 남겨주세요!
Write 한주연, 신지호 Review 박수연 Graphic 이나눔, 이은호