문서 엔지니어링과 API 문서화

이 글은 마이크로소프트웨어 396에 기고된 글입니다.

들어가기

테크니컬 라이터(technical writer)라는 말을 들으면 대부분 ‘라이터’라는 단어만 보고 ‘글 쓰는 사람’이라 생각하기 십상입니다. 물론 틀린 것은 아니지만, 실상 키보드를 두드리며 글 쓰는 일이 테크니컬 라이터 업무의 대부분을 차지하지는 않습니다. 하루에 얼마 동안 글을 쓰는지 측정해 본 적은 없으나, 테크니컬 라이터 톰 존슨(Tom Johnson)이 말하기로는 일하는 시간의 약 10%라고 합니다.

그렇다면 그 밖의 시간에는 뭘 할까요? 역시 톰 존슨에 따르면 개발자 인터뷰, 다른 사람이 쓴 문서 리뷰, 앱 스크린 캐스팅이 필요할 때 아이폰 탈옥, 미디어위키에서 링크된 이미지에 캡션 넣는 방법 찾기 등등이라고 합니다. 그중에서도 제가 말하고자 하는 일은 비록 ‘아이폰 탈옥’ 같은 건 아니지만 결과적으로는 글쓰기와는 전혀 관계 없어 보이는 엔지니어링에 관한 것입니다.

기술 문서 작업을 해보면 한 번 이상은 이런 요청을 받게 됩니다.

‘기술 정보를 담은 텍스트 파일 200개를 워드 파일 하나로 만들어 주세요.’
‘사내 위키에 쓴 게시물을 공식 개발자 사이트에 올려주세요.’

이런 요청을 받았을 때 취할 수 있는 방법은 단 하나, ‘복붙(복사해서 붙여넣기)’입니다. 물론 고결한 지성을 발휘해 텅 빈 백지에 새로운 글 한 편을 써내야만 하는 테크니컬 라이터에게 있어, 아무런 지성을 동원하지 않아도 되는 이런 작업은 재충전 기회기도 하니 하루 정도 투자할 가치는 있습니다. 그런데, 일주일 후 또 이런 요청이 들어옵니다.

‘텍스트 파일을 살펴봤더니, 구버전에만 적용되는 내용이 있었어요. 200개 파일에서 군데군데 수정했으니, 그 부분만 지난번에 만든 워드 파일에 업데이트해주세요.’

그들은 ‘그 부분만 업데이트’라고 말하지만, 부분 업데이트는 자칫하면 빠뜨리는 곳이 생기거나 다른 부분을 망가뜨릴 수 있어서 결국엔 전체를 다시 한번 손봐야 하는 일입니다. 저와 제 동료는 이를 ‘인형 눈알 꿰기’라고 불렀습니다. 애초에 워드 파일에 업데이트해주면 좋으련만, 그렇게는 안 됩니다. 왜 안 되는지는 블로그 분량의 한계 때문에 구구절절 말할 수 없으나, 보통 그렇고 그런 이유로 인형 눈알 꿰기 작업이 반복적으로 발생합니다.

몇 번쯤 인형 눈알 꿰기를 하던 열정에 찬 신입 테크니컬 라이터는 자연스럽게, 워드 파일을 만든 Microsoft를 원망하게 됩니다. 그리고 분노와 원망의 단계를 넘어서고 난 뒤 비로소 문서 엔지니어로 거듭납니다. 테크니컬 라이팅과 문서 엔지니어링이 떼려야 뗄 수 없는 관계인 이유입니다.

저는 본래부터 도구를 만들고 쓰는 것을 좋아해서, Microsoft를 원망하는 기간이 조금 짧았습니다. 덕분에 좀 더 빨리 문서 엔지니어의 길로 들어섰고, 이렇게 그에 관한 글을 쓰고 있습니다. 생각해보면 테크니컬 라이터가 된 후, 처음 맡은 프로젝트에서 백여 개 엑셀 파일을 워드 파일 하나로 만드는 작업을 하게 된 것이 그 길의 시작이었습니다. 당시 프로젝트 매니저 역시 반복되는 엑셀-워드 간 복붙이 비효율적이라고 느껴 해결책을 찾던 중이었습니다. 저는 그 일을 맡아 오피스 오픈 XML(Office Open XML)을 이용해 엑셀을 워드로 변환하는 도구를 만들었습니다.

처음에는 엑셀을 워드로 바로 변환했지만, 데이터 버전 관리와 양방향 변환이 필요해져 엑셀 데이터를 표준 XML 형태로 변환, 저장한 다음 워드 파일로 출력하게 바꿨습니다. 배운 게 도둑질이라고 그 후 저는 문서 생성 자동화, 다양한 포맷 지원을 위한 문서 표준화에 관심을 가지게 됐습니다.

문서 엔지니어링에 속하는 작업은 다양합니다. 문서화 프로세스를 구축하고 프로젝트 산출물을 관리하는 것도 문서 엔지니어링이지만, 이 글에서는 제 관심 분야인 문서 변환과 자동화에 관해서만 써보려고 합니다.

 

API 문서화

테크니컬 라이팅 업무 가운데 API 문서화는 문서 엔지니어링이 가장 많이 적용되는 분야입니다. 이 글에서 다른 기술문서가 아닌 API 관련 문서를 다루는 것도 그런 이유입니다.

API 문서, 주로 API 레퍼런스라고 부르는 이 기술 문서는 프로그래밍 역사와 함께 존재해왔습니다. 뜻 맞는 친구 몇 명이 모여 소프트웨어를 만들던 시절에 반드시 테크니컬 라이터가 함께 했다곤 할 수 없을 테니, 대부분은 프로그래머가 직접 API 레퍼런스를 썼을 것입니다. 처음에는 텍스트 파일로 썼겠지만, 소스코드와 동기화하기도 어렵고 문서를 별도로 작성해야 하므로 부담스러웠겠지요. 그 덕분에 소스코드에서 API 레퍼런스 작성하는 방법이 생겨난 것으로 추측할 수 있습니다.

API 레퍼런스는 흔히 개발 문서라고 하는 개발자 가이드와는 약간 다릅니다. 개발자 가이드는 내용 흐름과 연관 관계가 있지만, API 레퍼런스는 API 간 순서나 연결이 거의 없습니다. 극단적으로 말하면, 한 API 설명이 곧 하나의 문서인 셈입니다. <그림 1>과 <그림 2>에서 보는 것처럼 개발자 가이드는 설명글이 길게 이어지지만, API 레퍼런스는 유사한 것끼리 분류는 했으나 각자 역할만 설명하고 있습니다.

<그림 1> LINE 개발자 가이드 예(LINE SDK for iOS)
<그림 2> LINE API 레퍼런스 예(LineSDKJSONWebToken)

API 레퍼런스는 각 API 설명 형태도 무척 비슷합니다. 라이브러리 API나 프레임워크 API는 함수(메서드)명, 설명, 파라미터, 반환 값이 반복되고, REST API는 엔드포인트(endpoint), 설명, 파라미터, 응답 설명이 반복됩니다. 이처럼 서로 연결되지 않으며 반복되는 구조를 가진 문서는 도구를 이용해 작업하기 좋습니다. 그래서 개발자 가이드를 자동으로 만들어주는 도구는 없어도, API 레퍼런스를 만들어주는 도구는 예전에도 있었고 지금도 계속 발전하고 있습니다.

1995년, 썬 마이크로시스템즈(Sun Microsystems)가 자바(Java)와 함께 소스코드 주석을 이용해 API 문서를 만드는 자바독(Javadoc)을 선보였고, 20년이 지난 지금까지도 널리 쓰이고 있습니다. 자바독의 성공에 고무된 덕분일까요. 다른 프로그래밍 언어도 소스코드를 이용한 자체 문서화 도구를 속속 내놓았습니다. 소스코드 기반은 아니지만, REST API를 위한 ‘OpenAPI Specification(이하 OAS)’도 문서화 도구를 제공합니다.

API 레퍼런스는 꼭 필요할까

이 분야에서 수년간 일한 경험에 기반하여 기술 문서 중에서 개발자가 가장 많이 보는 것은 API 레퍼런스라고 어렴풋이 느끼고는 있었지만, 주관적인 느낌일 뿐 뒷받침해 줄 데이터가 없었습니다. 그런데 고맙게도, SIGDOC에서 실험을 통해 정량적인 데이터를 발표했습니다. 모집단이 작긴 하지만 이런 유의 조사가 많지 않으니 개발자가 새로운 API를 마주했을 때 어떤 행동을 하는지 참고할 만합니다.

SIGDOC가 발표한 데이터를 참고해 만든 <그림 3>은 개발자가 처음 접하는 API를 사용해 문제를 해결할 때 어떤 부분에 얼마나 시간을 할애하는지 보여줍니다.

<그림 3> 문제 해결 시간 동안 개발자 시선이 머문 곳(How Developers Use API Documentation: An Observation Study, SIGDOC)

가장 많은 부분을 차지한 ‘Editor & Client’는 문서 외 작업 시간입니다. 이를 제외하면, 개발자가 기술 문서를 볼 때 가장 많은 시간을 할애한 부분이 API 레퍼런스임을 확인할 수 있습니다. 따라서 인력 및 시간 부족으로 모든 문서를 작성하기 어려운 프로젝트라도 최소한 API 레퍼런스는 작성하는 것이 좋습니다. 그래야 누군가 사용해줄 테니까요. 운 좋게도 API 레퍼런스는 개발자가 작성하기에 가장 부담 없는 문서입니다. 구구절절 이야기를 나열할 필요도 없고, 딱 정해진 내용만 쓰면 되기 때문입니다.

 

API 레퍼런스에는 무엇을 써야 할까

API 문서화 요청을 받으면, 저는 보통 다음 순서로 작업합니다.

  1. API 유형 및 프로그래밍 언어, 출력 포맷을 확인한다.
  2. 그에 맞는 도구를 선정한다.
  3. API 하나를 골라 설명 샘플을 작성한다.
  4. 어디에 뭘 작성해야 하는지를 설명하는 가이드 템플릿을 작성한다.
  5. 개발자에게 API 설명 작성을 요청한 후, 그 내용을 검토한다.
  6. 전체 프로세스를 검수한 다음, 문서 생성을 자동화한다.

5, 6번 단계까지 진행할 수 있다면 더할 나위 없는 문서가 될 수 있습니다. 하지만 프로젝트 종료까지 테크니컬 라이팅을 전담해주지 못한다면 4번 단계까지만 작업해도 꽤 만족스러운 결과를 얻을 수 있습니다.

1번 작업이 필요한 이유는 API 유형과 프로그래밍 언어, 출력물 포맷에 따라 사용할 도구와 가이드 템플릿이 달라지기 때문입니다. 예를 들어, Android용 라이브러리 API 레퍼런스를 HTML으로 만들려면 자바독을 사용하고, 스프링(Spring)으로 작성한 REST API 레퍼런스를 웹서버로 공개하려면 스웨거(Swagger)를 사용하고, 파이썬(Python)으로 작성한 라이브러리의 API 레퍼런스를 전자책으로 배포하려면 스핑스(Sphinx)를 사용하는 식입니다.

아래 코드는 소스코드 주석 기반 API 레퍼런스 생성 도구인 자바독의 예시입니다.

/**
 * 내가 만든 정말 멋진 클래스
 * @author jeongeun.jeon
 * @version 0.1
 */
public class MyFantasticClass {
 /**
 * 주어진 값의 2배를 계산한다.
 *
 * 정수가 아닌 값의 두 배를 얻으려면 see also에 있는 항목을 참고하라.
 *
 * @param base 두 배 하고자 하는 값. 0부터 100까지만 입력할 수 있다.
 * @return 주어진 값의 두 배
 * @see getDouble(float)
 */
 public int getDouble (int base) {
 …(중략)
 }
}

보통은 IDE(integrated development environment)가 메서드 프로토타입에 따라 자동으로 필요한 태그(@param@return 등)를 입력해줍니다. 이 태그를 보면 작성해야 할 내용이 무엇인지 알 수 있습니다. 이처럼 정해진 형식에 맞춰 내용을 작성하게 하는 것은 API 레퍼런스 도구의 기본 기능이자 쓰는 사람이 포기하지 않도록 붙잡아주는 끈입니다. 테크니컬 라이터도 백지를 펼쳐놓고 글을 쓰라고 하면 아득해지는데, 하물며 개발자에게 텅 빈 텍스트 파일을 주면서 쓰라고 하면 어떻게 될까요? 물론 잘 쓰는 사람도 있지만, 보통은 첫 문장을 시작할 때까지 시간이 한참 걸릴 것입니다. 정해진 형식에 따라 내용을 채우게 하는 방법은 객관식 문제와 유사해서, 설명글을 작성하는 속도가 훨씬 빨라집니다.

이 방법이 써야 할 항목을 정해주고 뭘 써야 할지 알려주긴 하지만, 각 항목에 꼭 필요한 내용을 쓰게 하려면 이것만으로는 부족합니다. 4번 단계처럼, 이 부분에는 어떤 내용을 쓰고 어떤 내용을 쓰지 말아야 하는지 설명해주는 가이드 템플릿을 함께 제공하는 것이 좋습니다. 가이드 템플릿은 프로그래밍 언어 또는 API 유형에 따라 다릅니다.

아래 코드는 자바독 가이드 템플릿입니다. 파라미터 설명 가이드 등은 유사한 구조를 가진 다른 API에도 적용할 수 있습니다.

/**
 * 첫 문장에는 동사를 사용해 메서드의 역할을 설명하십시오. '무엇을 반환한다'는 지양하십시오.<p/>
 * 줄바꿈한 다음 아래와 같은 정보가 있으면 기술하십시오.
 * - 이 메서드를 호출하기 전후에 해야 하는 작업이 있다면 기술하십시오.
 * - 특정한 상황에서 이 메서드를 사용하지 말아야 한다면 이유를 설명하십시오.
 * *참고* 영어로 쓴다면 첫 글자는 대문자로 쓰십시오.
 *
 * @param base 어떤 의미의 파라미터인지 쓰십시오.
 * boolean 값이라면 언제 true이고 언제 false 인지 쓰십시오.
 * 숫자라면 범위가 있는지 쓰십시오. enum이라면 그 enum 항목을 링크하십시오.
 * 허락되지 않은 값을 전달했을 때 무슨 일이 발생하는지 쓰십시오.
 * @return 무엇을 반환하는지 명사로 쓰십시오.
 * boolean 값이라면 언제 true이고 언제 false 인지 쓰십시오.
 * 숫자라면 범위가 있는지 쓰십시오. enum이라면 그 enum 항목을 링크하십시오.
 * 문제가 발생했을 때 일반 상황과 다른 의미의 값을 반환한다면 기술하십시오.
 * (예: -1을 반환하는 경우)
 * @see 유사한 기능을 하는 메서드가 있다면 기술하십시오.
 * 어떤 스펙을 구현한 것이라면 그 스펙의 이름 혹은 링크를 기술하십시오.
 */
 public int getDouble (int base) {
 …(중략)
 }
}

가이드 템플릿에도 나와 있지만, API 설명에서 가장 자주 빠뜨리는 내용은 바로 파라미터나 반환 값의 범위입니다. 반환 값이 나열형이라면 어떤 값을 쓸 수 있는지 나열해야 하고, 반환 값이 숫자라면 그 범위나 값의 의미를 함께 설명해야 합니다. 이를 빠뜨리면 API 레퍼런스를 보고 개발하는 사람이 시행착오를 겪기 때문입니다.

따라서, 이런 주의사항을 쓴 가이드 템플릿을 제공하면 훨씬 빠르고 정확하게 설명문을 작성할 수 있습니다. 이렇게 작성된 내용으로 <그림 4>와 같은 출력물을 만들면 API 레퍼런스 작성이 끝납니다.

<그림 4> 자바독 출력물 예(LINE SDK v5.0.0 for Android)

가이드 템플릿이 없더라도 도구를 이용해서 이런 상황을 막을 수 있을까요? 소스코드 주석 기반 도구에서 좀 더 발전한 것이 API 명세서 기반 도구입니다. 이 중 OAS가 그런 기능을 제공합니다. OAS의 주목적은 문서화가 아니라 설계 및 테스트이므로, API를 정의할 때 API가 사용할 객체를 명확히 입력하도록 권장합니다.

예를 들어, 객체의 필드가 enum이면 enum 스키마를 정의한 후 그 위치로 레퍼런스하고, 객체의 필드가 숫자면 최댓값과 최솟값을 입력해 범위를 지정합니다. 비록 필수는 아니지만 다른 도구에 비해 범위 지정을 빠뜨리는 횟수를 줄일 수 있습니다.

 

API 레퍼런스는 어떻게 게시할까

API 레퍼런스 생성 도구 대부분은 HTML 출력물을 만드는 기능이 있습니다. 오픈소스 프로젝트, 특히 전담 테크니컬 라이터가 없는 프로젝트는 이런 도구를 이용해 만든 HTML 파일을 깃허브 페이지(GitHub Pages) 등에 업로드하면 충분합니다. 하지만 공식 개발자 지원 웹사이트를 가진 회사라면 상황이 조금 다릅니다. CMS(content management system) 같은 웹 콘텐츠 관리 도구로 공식 웹사이트를 만들거나 README.ioApiary 같은 개발 문서 서비스를 사용하는 곳도 있습니다. 물론 자체 제작한 웹사이트를 사용하는 곳도 있습니다. 어느 쪽이든 <그림4>에서 본 출력물을 그대로 게시하기에는 어려움이 따릅니다. 기술적인 어려움도 있지만 정책적인 어려움도 있습니다.

구글만 해도, Android API 레퍼런스를 자바독 기반 독릿(doclet)인 Doclava를 사용해 서비스했으나, 자체 도구로 바꾼 지 오래입니다. 소스코드 주석에 기술한 API 설명을 보면 자체 도구도 자바독 기반일 것으로 짐작됩니다.

<그림 5> Android API 레퍼런스 화면

Microsoft는 어떨까요? Microsoft는 IT 기업 가운데 기술 문서를 오픈소스화한 대표적인 곳입니다. 깃허브에 있는 애저(Azure) 기술 문서를 잘 들여다보면, REST API를 자체 정의한 YAML 형식1으로 기술한 것을 볼 수 있습니다. 애저 공식 웹사이트는 이 데이터 정보를 API 레퍼런스로 만들어 제공합니다.

<그림 6> 애저 API 레퍼런스 화면

애저 문서 저장소에서 찾아본 YAML 데이터는 아래 코드와 같습니다.

- uid: azure-arm-sql.JobVersions.get
 name: 'get(string, string, string, string, number, Object)'
 children: []
 type: method
 langs:
 - typeScript
 summary: Gets a job version.
 syntax:
 content: >-
 function get(resourceGroupName: string, serverName: string,
 jobAgentName: string, jobName: string, jobVersion: number, options?:
 Object)
 parameters:
 - id: resourceGroupName
 type:
 - string
 description: >
 The name of the resource group that
 contains the resource. You can obtain this value from the Azure
 Resource
 Manager API or the portal.
...(중략)

오픈소스 프로젝트인 Enact 역시 개발 문서를 깃허브에 공개했으므로, 어떤 도구를 사용했는지 추측해볼 수 있습니다. Enact의 자바스크립트 API 레퍼런스는 JSDoc 형식으로 소스코드에 기술하고, 자체 엔지니어링을 거쳐 웹사이트에 게시하는 방식입니다.

<그림 7> Enact API 레퍼런스 화면

Android와 Enact는 소스코드 주석을 사용하고, 애저는 YAML 기술 형식을 사용했습니다. 방식이 다른 이유는 사용하는 프로그래밍 언어와 API 유형에 따라 널리 사용되는 도구를 활용했기 때문일 것입니다. 그렇다 해도 이 도구를 회사 공식 웹사이트에 통합 적용하기 어려웠다면 과연 사용했을까요?

자바독과 JSDoc은 출력물 커스터마이즈를 지원하며, 애저는 OAS와 유사한 자체 API 명세서를 사용했으므로, 기존 웹사이트에 어울리도록 출력물을 바꿀 수 있습니다. 즉, 세 예시 모두 공식 웹사이트에 잘 녹아드는 출력물을 만들 수 있는 도구 혹은 방법을 선택한 것입니다.

데이터 기반으로 말해야 하는 공학도로서 다소 어울리지 않는 말이지만, 솔직히 말해 외부에 공개하는 문서는 겉모습도 중요합니다. 겉보기에 깔끔하고 아름다운 문서는 글을 읽기 전부터 독자의 관심과 신뢰를 얻을 수 있기 때문입니다. 물론 내용도 제대로 돼 있어야 하지만, 애초에 겉모습이 기업 이미지에 맞지 않거나 아마추어 같으면 내용을 읽기도 전에 신뢰도가 하락할 것입니다.

이런 이유로 저는 외부 공개 문서를 작성할 때 자바독이나 독시젠(Doxygen), 스웨거 등이 제공하는 기본 출력물 사용을 권장하지 않습니다. 기본 출력물은 회사 브랜드 아이덴티티를 담기 어렵고, 그간 회사가 쌓아 온 신뢰감을 문서에까지 전달해주지 못하기 때문입니다. 개인적으로는, 몸담은 회사가 문서 엔지니어링에 대한 기술이나 관심이 없는 것으로 오해를 받을까 우려되기도 합니다.

API 레퍼런스는 이를 사용하는 개발자가 가장 많이 보는 기술 문서이며, 문서 엔지니어링을 어떻게 하느냐에 따라 작성 시간 및 출력물 모양이 크게 달라집니다. 이런 이유로 전문 테크니컬 라이팅 팀을 보유한 회사라면 자체 API 문서화 솔루션을 가져야 한다고 생각합니다. 완전히 새로운 도구를 만들어야 한다는 것이 아니라, 이미 있는 도구를 사용하더라도 트렌드 변화에 맞춰 정확하고 전문적인 결과물을 내놓는 프로세스를 가지고 있어야 한다는 뜻입니다. 구글도 그렇고 Microsoft도 그렇습니다. LINE 역시 다르지 않습니다.

 

좋은 API 레퍼런스 솔루션

그럼 좋은 API 레퍼런스 솔루션이란 어떤 것일까요? 앞서 언급한 예시에서 두 가지 조건을 찾아볼 수 있습니다.

  • 출력 형식이 바뀌어도 쉽게 적응할 수 있어야 한다.
  • 작성하는 사람이 뭘 써야 하는지 쉽게 알 수 있어야 한다.

Android 개발자 사이트는 여러 차례 리뉴얼됐으나, API 레퍼런스는 겉모양만 바뀌었을 뿐 소스코드에 자바독으로 설명을 작성하는 방식은 바뀌지 않았습니다. 소스코드에 작성한 내용을 웹페이지로 변환할 때 구글이 사용한 도구가 웹사이트의 변화에 쉽게 적응할 수 있다는 뜻입니다.

Android 예에서 보듯 API 레퍼런스가 공개될 웹사이트의 형태는 언제든지 바뀔 수 있습니다. 종종 웹사이트가 아닌 PDF 같은 파일이 최종 배포 형태가 되기도 합니다. ‘손목시계로도 인터넷이 되는 시대에 PDF라니’하고 어리둥절하는 사람도 있겠지만, 현장에서는 아직도 PDF가 많이 쓰입니다. 불특정 다수가 아닌 특정 그룹에만 배포하거나, 공식 웹사이트 공개 전에 배포하는 문서가 그 예입니다.

물론 나중에는 그 문서를 웹사이트에서 배포하게 될 가능성이 높습니다. 처음에는 문서를 PDF로 배포하다가 몇 달 뒤 웹사이트에 공개하는 문서화 프로젝트를 저도 몇 번 경험했습니다. 따라서 새로 시작하는 프로젝트에서 매니저가 API 레퍼런스는 PDF로 배포하겠다고 선언하더라도, 테크니컬 라이터는 웹 배포까지 염두에 두는 것이 좋습니다.

변화하는 출력물에 빠르게 대응하기 위해서는 데이터(설명)와 뷰(출력 형태)를 분리하는 것이 기본입니다. 데이터는 인라인 포매팅 외 출력물에 관한 어떤 정보도 포함하지 않아야 하며, 뷰는 데이터에 담긴 정보를 표출하되 형태를 쉽게 변경할 수 있어야 합니다. 이렇게 데이터를 분리해낸 후 어디에 무엇을 써야 하는지 명시적으로 나타내는 구조로 기술하면, 좋은 API 문서화 솔루션의 두 번째 조건을 달성할 수 있습니다.

자바독, JSDoc, OAS와 유사한 YAML 형식 모두 데이터와 뷰를 분리하고 명시적인 구조로 데이터를 작성하게 했습니다. 앞에서 본 예시처럼, 셋 모두 텍스트 기반으로 API 설명을 작성한 다음, 그 데이터에 원하는 뷰를 적용해 출력물을 생성합니다.

앞서 언급한 도구가 지원하지 않는 것, 예를 들어 C++로 작성한 API나 gRPC API는 레퍼런스 문서를 어떻게 만들어야 할까요? 솔직히 말하면, 둘 다 서드파티 API 레퍼런스 작성 도구가 있으니 큰 문제가 되지는 않습니다. 하지만 좀 더 나아가서, 하나의 프로젝트에서 다양한 프로그래밍 언어나 다양한 API 유형을 제공한다고 생각해봅시다. 프로그래밍 언어별로 각각 다른 도구를 사용한다면 프로젝트 전체 API 레퍼런스는 제각각 다른 형태가 될 수밖에 없습니다. 이것까지 고려한다면, 좋은 API 문서화 솔루션 조건을 하나 더 추가해야 합니다.

  • 다양한 프로그래밍 언어와 API 유형을 통합할 수 있어야 한다.

 

API 레퍼런스 솔루션 유스케이스

좋은 API 문서화 솔루션을 위한 세 가지 조건을 달성하기 위해 <그림 8>과 같은 API 문서화 솔루션을 구상해보았습니다.

<그림 8> 좋은 API 레퍼런스 솔루션 조건을 달성하기 위한 API 문서화 작업 흐름

<그림 8>에서 보는 것처럼 소스코드 주석을 이용한 문서화 도구가 있다면 재활용하고, 그런 도구가 없으면 텍스트 기반으로 자체 기술 포맷을 만듭니다. 구조화 데이터용으로 쓰는 XML, JSON, YAML 등의 형식을 사용하는 게 좋겠습니다. 소스코드와 별도 포맷으로 작성한 데이터는 출력 형식을 정의한 템플릿을 적용해 최종 출력물로 변환됩니다. 사용하는 템플릿 언어는 각자 편리한 것을 선택합시다. 저는 이 솔루션을 ‘API 레퍼런스 템플릿’이라고 부르기로 했습니다.

제가 진행하는 프로젝트는 모두 API 레퍼런스 템플릿을 적용했습니다. 안타깝게도 아직 공개하지 않은 프로젝트이기에, 이 글에서는 가상의 데이터를 이용해 API 레퍼런스 템플릿이 무엇인지 소개하려 합니다. 가상 프로젝트는 C++ 기반의 라이브러리 API와 REST API를 제공합니다. 테크니컬 라이터 손에 들어왔을 때 라이브러리 API 설명은 소스코드 주석에, REST API 설명은 사내 협업 도구를 이용한 웹 게시물에 기술돼 있었습니다.

아래 코드는 가상 프로젝트의 C++ 라이브러리 API 설명입니다.

namespace mine
{
 /**
 * 내가 만든 완벽한 C++ 클래스.
 * 생성 후에는 반드시 `CallMe()`를 호출해야 합니다.
 */
 class MyClass
 {
 public:
 /**
 * 용건 있을 때 전화해달라는 함수.
 */
 void CallMe(void);
/**
 * 내 질문에 대답하게 하는 함수.
 * @question 내가 한 질문. null이면 반드시 false를 반환합니다.
 * @return 질문에 대한 응답. 응답이 '그렇다'면 true, '아니다'면 false를 반환합니다.
 */
 bool AnswerMe(char *question);
 …(중략)
 };
}
<그림 9> 가상 프로젝트의 REST API 설명

제가 할 일은 두 종류 API 레퍼런스를 통합해 같은 형식으로 웹사이트에 게시하는 것입니다. 우선 현 상태를 분석해서 어떤 도구를 사용할지 생각해봅시다.

라이브러리 API는 소스코드에 설명을 작성했으니 데이터를 뷰에서 분리한 상태입니다. REST API는 사내 게시판에 썼으니 데이터와 뷰가 혼재돼 있습니다. 따라서, 먼저 라이브러리 API 레퍼런스 데이터를 출력할 뷰 템플릿을 만들고, 그 다음 REST API 문서에서 데이터를 추출한 다음 또 뷰 템플릿을 만들고, 마지막으로 두 종류 API의 뷰를 문서 하나로 통합해야 합니다.

<표 1> API 레퍼런스 템플릿을 적용할 가상 프로젝트 상태

항목라이브러리 APIREST API
원본소스코드 주석사내 게시판
데이터-뷰 분리분리미분리
적용 가능한 기존 도구독시젠 사용 가능없음

<표 1>에서 보는 것처럼, 언어 독립적 문서화 도구인 독시젠(doxygen)을 사용해서 C++ 소스코드에 작성한 주석으로 API 레퍼런스를 만들 수 있습니다. 독시젠으로 HTML 파일을 생성할 수도 있지만, 커스터마이징이 까다로운 데다 우리 목적이 개별 HTML이 아닌 통합 HTML을 제공하는 것이므로, 직접 HTML을 생성하는 대신 통합하기 좋은 포맷으로 만들 것입니다.

사내 게시물로 작성한 REST API 설명은 변경이 잦을 땐 업데이트가 무척 불편하므로, 과감하게 이 방식을 포기하고 수동으로 데이터를 추출해 YAML로 재작성할 것입니다. 반드시 사내 게시물로 공유해야 한다면, 이 YAML 파일을 공유할 수 있습니다. 그 후 각각 데이터를 하나의 문서로 만들려면 언급한 것처럼 HTML이 아닌 통합이 쉬운 포맷을 사용해야 합니다. 여기서는 그 중간 출력물 포맷으로 마크다운(markdown)을 선택했습니다.

 

C++ 라이브러리 API 레퍼런스 작업

우선 소스코드에 있는 라이브러리 API 주석을 마크다운으로 변환합시다. 독시젠은 마크다운 출력을 지원하지 않지만, 자체 정의한 XML 형식으로 API 명세서를 만들 수 있습니다. 이 명세서에는 소스코드가 빠진 순수 API 설명만 담깁니다.

<그림 10> 독시젠 실행 화면

<그림 10>처럼 독시젠의 옵션에서 XML을 출력하도록 설정하고 빌드합니다. 이렇게 생성한 XML 데이터를 다양한 출력물로 만들어주는 도구가 Moxygen입니다. 이를 이용해 마크다운으로 변환합니다.

아래 코드는 C++ 라이브러리 API 주석 데이터를 마크다운으로 변환한 결과(중간 출력물)입니다.

# class `mine::MyClass` {#classmine_1_1MyClass}
내가 만든 완벽한 C++ 클래스.
생성 후에는 반드시 [`CallMe()`](api-CallMe.md#classmine_1_1MyClass_1a20213a9d29c24324d6ea32a0010d3e9f)를 호출해야 합니다.
## Summary
Members | Descriptions
--------------------------------|---------------------------------------------
`public void `[`CallMe`](#classMyClass_1adc72431f56d6803585f60cef6e467991)`(void)` | 용건 있을 때 전화해달라는 함수.
`public bool `[`AnswerMe`](#classMyClass_1a98aecda3ae776dd1f99aad5f9990ff62)`(char * question)` | 내 질문에 대답하게 하는 함수.
...(중략)

Moxygen도 뷰 템플릿을 커스터마이징할 수 있으므로, 필요에 따라 출력물을 수정할 수 있습니다. 하지만 가상 프로젝트에서는 기본 템플릿을 사용하고, 다음에 처리할 REST API 레퍼런스를 이와 비슷한 형태가 되도록 작성할 것입니다.

 

REST API 레퍼런스 작업

REST API 설명은 아래 코드처럼 YAML 포맷에 재작성합니다. 앞 예시에서 본 애저 기술 문서의 방법과 유사합니다.

summary: 사용자 정보 조회
path: /user/{user_id}
method: GET
description: |-
 주어진 아이디(user_id)의 사용자 정보를 조회합니다.
 요청이 성공적으로 서버에 전달되면 200 OK를 반환하며, 요청 처리 중 발생한 오류는 응답 객체의 `error` 필드에 나타납니다.
parameters:
 - name: user_id
 in: path
 description: |-
 조회할 사용자 ID. 사용자 ID는 [사용자 목록](/apis/user-list)에서 확인할 수 있습니다.
 required: true
response:
 header:
 description: |-
 HTTP Result Code가 200 OK일 때 반환하는 정보입니다. [공용 응답 필드](/apis/response-common-fields)를 참고하십시오.
 body_segments:
 id:
 description: |-
 파라미터로 수신한 `user_id`
 required: true
 type: string
 name:
 description: |-
 사용자의 이름
 required: true
 type: string
 image:
 description: |-
 사용자의 사진이 저장된 URL
 required: false
 type: object
...(중략)

척 봐도 OAS와 상당히 유사합니다. 다만, OAS는 모든 API를 한꺼번에 기술하고 응답 코드에 따라 좀 더 다양한 응답 설명이 있는데 반해, 여기서는 한 명세서에 한 API만 기술하고 응답도 ‘200 OK’로 한정했습니다.

물론, 각 프로젝트 특성에 따라 형식을 변경해도 상관없습니다. 이렇게 텍스트로 API 설명을 기술하면 소스코드와 함께 깃(Git) 저장소에서 버전을 관리할 수 있다는 장점이 있습니다. 또, API를 추가하거나 수정할 때도 이 형식에 맞춰 작성하게 되므로, 사내 게시물로 작성할 때보다 고정적인 데이터 구조를 유지할 수 있습니다. 즉, 누군가 정해진 형식에서 벗어나 임의로 테이블이나 그림을 입력할 수 없다는 말입니다.

이제 이 YAML 데이터를 마크다운으로 변환하는 작업이 필요합니다. 마크다운은 뷰를 포함한 문서이므로 레이아웃을 결정해야 합니다. 앞서 말한 대로 C++ 라이브러리 API 레퍼런스의 중간 출력물과 유사하게 만들 것입니다. 템플릿 언어는 핸들바(Handlebars)를 사용합니다.

아래 코드는 REST API의 YAML 데이터를 마크다운으로 변환할 템플릿입니다.

...(중략)
## Summary
Members | Descriptions
---------------------------|---------------------------------------------
{{#each paths}}{{toUpperCase method}} {{path}} | {{summary}}
{{/each}}
{{#each paths}}
## {{#if summary}}{{summary}}{{/if}} `{{toUpperCase method}} {{path}}` {{#if operationId}}{{addId operationId}}{{/if}}
{{description}}
{{! parameters }}
### Request parameters
{{#if parameters}}
Parameter | Type | Description
:--------------:|-------------|------------------------------------------
{{#each parameters}}
`{{name}}`<br/>({{in}}) | {{schema.type}} {{#if schema.items}} of {{schema.items.type}}{{/if}} | {{description}} {{#ifCond required "true"}}O{{/ifCond}}
{{/each}}
{{else}}None{{/if}}
...(중략)

YAML 파일을 읽고 이 템플릿 파일을 적용하면, 아래 코드와 같이 C++ 라이브러리 API 레퍼런스와 유사한 중간 출력물을 얻을 수 있습니다.

## Summary
Members | Descriptions
---------------------------|---------------------------------------------
GET /user/{user_id} | 사용자 정보 조회
## 사용자 정보 조회 `GET /user/{user_id}`
주어진 아이디(user_id)의 사용자 정보를 조회합니다.
요청이 성공적으로 서버에 전달되면 200 OK를 반환하며, 요청 처리 중 발생한 오류는 응답 객체의 `error` 필드에 나타납니다.
### Request parameters
Parameter | Type | Description
:----------------:|-------------|---------------------------------------------
`user_id`<br/>(path) | | 조회할 사용자 ID. 사용자 ID는 [사용자 목록](/apis/user-list)에서 확인할 수 있습니다.
...(중략)

자체 API 템플릿을 구현하는 방법은 주제에서 벗어나기 때문에 상세히 설명하지 않고 깃허브 저장소의 설명과 코드로 대신합니다.

 

레퍼런스 통합

드디어 포맷은 같고 모양은 유사한 뷰로 두 종류의 API 레퍼런스를 출력해냈습니다. 이제 이들을 공식 웹사이트에 집어넣거나 지킬(Jekyll) 같은 정적 사이트 생성 도구를 이용해 자체 웹사이트를 서비스하면 됩니다. <그림 11> 화면은 포맷 변환 도구인 팬독(Pandoc)과 그 HTML 템플릿 중 하나를 이용해 통합 API 레퍼런스 웹페이지를 만들어본 것입니다.

<그림 11> 통합 API 레퍼런스 웹페이지

중간 출력물로 마크다운을 사용하면, HTML뿐 아니라 좀 더 다양한 파일 포맷으로 변환할 수 있습니다. <그림 12>는 그중 하나인 PDF 파일입니다.

<그림 12> 통합 API 레퍼런스 PDF

<그림 12>처럼 유려한 PDF를 만들려면 레이텍(LaTeX)을 사용해야 합니다. PDF 출력 방법에 관해서는 여기서 다루지 않으므로, 관심이 있으면 레이텍을 살펴보시기 바랍니다.

서로 다른 API 레퍼런스를 통합해야 할 필요가 없더라도, 다양한 파일 포맷을 지원하기 위해 마크다운을 중간 출력물로 사용하는 것도 좋은 방법입니다. 앞서 말한 것처럼 PDF 배포 후 웹사이트 게시로 진행되는 문서에 이 방법을 사용하면 웹사이트가 언제 공개돼도 즉시 대응할 수 있습니다.

 

끝맺기

API 레퍼런스를 작성할 때 중요한 요소를 소개하고, 이를 만족할 수 있는 API 레퍼런스 솔루션을 살펴보았습니다. 일견 복잡해 보일 수 있는 방법이지만, 이 모든 것을 스크립트를 통해 단번에 수행할 수 있습니다. 이렇게 프로세스화 된 문서 작업에서는 젠킨스(Jenkins) 같은 지속적 통합 도구를 이용하면 데이터(소스코드 혹은 YAML 파일) 변경 시 자동으로 최종 출력물을 만들 수 있으므로, 초기에 한 번 설정해 주면 손이 갈 일이 많지 않습니다.

독시젠 같이 널리 쓰이는 API 레퍼런스 생성 도구도 그 속을 뜯어보면 소스코드 주석에서 데이터를 추출하고 사용자가 변경할 수 있는 뷰에 데이터를 입혀 출력물을 커스터마이징 할 수 있게 해 놨습니다. 모든 작업이 가려져 있기에 복잡하게 보이지 않을 뿐, 결과적으로 이 글에서 소개한 방법과 크게 다르지 않습니다. 그래도 여전히 복잡해 보인다면, 프로젝트 특성에 따라 중간 출력물 생성 단계나 YAML API 명세서 작성 단계를 생략해도 좋습니다.

이 글의 목적은 쓸데없이 복잡한 프로세스를 적용하라고 권유하기 위함이 아닙니다. API 레퍼런스를 좀 더 쉽게 작성하고 관리하려면 이런 종류의 솔루션을 적용하면 좋겠다는 것이 말하고자 하는 핵심입니다.

이 글의 독자들은 앞으로 API 레퍼런스 생성 도구를 선정할 때 다음 두 가지를 꼭 고려하기 바랍니다.

  • 데이터와 뷰를 분리했는가?
  • 소스코드와 함께 버전 관리할 수 있는가?

첫 번째에 관해서는 본론에서 길게 설명했으니 더 말하지 않아도 될 것 같습니다. 두 번째는 이 글에 자세히 언급하지는 못했지만, 텍스트 기반 API 기술 방식을 적용하면 쉽게 달성할 수 있습니다.

이 글에서 소개한 API 레퍼런스 템플릿 중 YAML로 기술한 API 명세서는 두 가지 고려사항을 만족하는 도구 중 하나입니다. 소스코드 주석기반 문서화 도구가 없는 프로그래밍 언어를 사용하거나, 소스코드가 아직 없거나, 소스코드 수정을 극도로 꺼리는 프로젝트에 알맞은 방법입니다. 좀 더 전문화된 통합 API 레퍼런스 생성 도구가 발표되기 전까지, 앞서 언급한 문제로 인해 API 레퍼런스 작성에 애먹고 있는 사람들이 있다면 쓸만한 해결책이 될 것으로 믿습니다.

참고자료

 


 

  1. YAML은 XML, C, 파이썬, 펄, RFC2822에서 정의된 이메일 양식에서 개념을 얻어 만든, ‘사람이 쉽게 읽을 수 있는’ 데이터 직렬화 양식입니다.