Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

I/O Docs를 사용한 API 문서화

요즘은 프로젝트를 하면서 RESTful API를 만들어야 하는 경우가 많아서 API 문서화를 어떻게 해야하는 가에 대해서 고민을 많이 했다. RESTful API는 사실 경우의 수도 많고 은근 복잡해서 문서가 자세해도 삽질이 수반되기 마련이기에 curl이나 REST 클라이언트 도구등을 써서 테스트를 해보면서 개발을 해야한다. Google Cloud Console같은 것을 제공하면 사용하기가 제법 편하다.(그래도 하다보면 curl등으로 직접 요청을 날려보게 되기 마련이지만...)

작년 KSUG에 갔다가 윤성준님 발표에서 Swagger라는 도구를 처음 보고 RESTful API 문서로써 참 훌륭하다고 생각했다. API 문서자체가 실제 요청을 날려볼 수 있는 동작가능한 문서이므로 어떤 형태로 제공하는지 이해하기가 쉽고 잘못되었을 때 바로바로 확인해 볼 수 있기 때문이다. 물론 공개된 문서라면 좀더 설명등이 많이 추가되어야 하겠지만 사내 커뮤니케이션으로는 아주 훌륭해 보였고 언젠가 꼭 써봐야지 하고 있었다. 그래서 쓸 일이 있어서 여러번 검토를 해봤지만 아주 맘에 들지는 않았다. 별로 써보진 않았지만 Swagger는 문서화를 자동화로 만들어주는데 이런 자동화를 위해서 Swagger쪽에 좀 코딩을 해야한다. 모델등을 만들어야 하는데 이게 실제 서비스의 모델과 중복이라서 관리가 만만치 않아보였고(이부분을 자동화해주는 도구가 좀 있기는 하다) 문서화를 위한 작업으로는 너무 비대해 보였다.

그러다가 눈에 띈 게 Mashery의 I/O docs였다.

I/O docs 설치

I/O docs를 사용하려면 Node.js가 필요하고 Redis 서버가 있어야 한다. 로컬에 기본 폴더로 실행되어 있다면 그냥 사용하면 되고 설정을 수정해야 한다.

I/O docs에서 최신 소스를 받으면 된다. 약간 안타깝게도 버전관리를 하고 있지는 않으므로 그냥 최신 소스를 받으면 된다. git을 쓴다면 클론을 받거나 그냥 압축파일을 내려받으면 된다. 내려받은 뒤 루트폴더에 있는 config.json.sample파일을 config.json파일로 변경하면 사용이 가능하다. 이 파일에서 서버의 호스트등을 지정할 수 있고 Redis 서버가 로컬에 떠있지 않거나 기본 포트가 아니라면 이 파일에서 수정하면 된다.

npm install로 관련 의존성을 설치하고 node app이나 npm start로 시작하면 되고 윈도우는 npm run-script startwin로 실행한다.(여기서 npm을 이용하면 supervisor로 실행이 되므로 변경사항을 자동으로 감시한다.) 기본 설정을 바꾸지 않았다면 http://localhost:3000/에서 다음과 같은 화면을 볼 수 있다.

I/O docs 첫화면

예시용으로 몇가지 사이트의 API 문서를 기본으로 포함하고 있고 문서화가 아주 좋진 않으므로(나날이 좋아지고 있지만) API 문서를 작성할 때 이 예제들을 참고하면 된다.

I/O Docs의 Foursquare API 문서화면

위처럼 API를 기능별로 그룹화해서 펼쳤다 접는 것이 가능하다. 위의 Foursquare는 간단할 설명만 나와 있지만 좀더 자세한 설명을 적는 것도 가능하다. HTTP 메서드별로 색을 다르게 보여주므로 구별하기가 쉽다.

특정 API를 실제로 테스트해 본 화면

이 API 문서는 동작가능한 형태이므로 위처럼 각 파라미터의 설명과 함께 필드를 제공해서 직접 Try It!을 눌러서 어떤 결과가 오는지 테스트해 볼 수 있다. 물론 I/O Docs는 RESTful API 클라이언트의 입장이므로 API 서버는 동작하고 있어야 한다.

API 문서 작성

I/O docs는 API 문서를 JSON으로 작성한다. 즉, 손수 일일이 작성해 주어야 한다. 앞에서 Swagger 얘기를 했었는데 Swagger는 (아주 깊게 써보진 못했지만) 모델에 대한 약간의 코드를 작성하고 이를 기반으로 자동으로 API문서를 만들어 주는 방식이고 스프링 MVC등에서 자동으로 뽑아낼 수 있게 플러그인 같은 것들도 있다. 하지만 실제로 써보면 완전하지 않으므로 손이 좀 가게 되거나 확 맘에 들지 않다. 나는 자동화를 무척이나 좋아하는 개발자이지만 한편으로는 거의 신경안써도 될 정도의 자동화가 아니라면 수동작업이 오히려 낫다고 생각하는 편이다. 내가 코드를 바꿀때마다 빌드만 돌리면 API 문서가 자동으로 쭉쭉 뽑아져 나온다면 바랄게 없겠지만 그렇지 않고 뭔가 손을 대야 한다면 오히려 그냥 간단한 문서화 작업을 하는게 보기도 좋고 낫다는 생각이다.

그래서 I/O docs를 선택한 것인데 다른 도구들이 맘에드는 완전 자동화수준이 아니므로 그럴바에는 손이 좀 가지만 간단한 JSON을 통해서 API 규약을 정해주는게 현재로썬 더 낫다는 판단이다. API 문서라고 해봤자 파라미터 변경이나 URL 변경 등이므로 한번 만들어 놓으면 손이 그렇게 많이 가지도 않는다.

문서화 파일은 public/data/아래 모두 존재하고 apiconfig.json파일이 메인 파일이다. 예를 들어 이 파일을 아음과 같이 정의할 수 있다.

{
  "linkedin": {
    "name": "LinkedIn",
    "protocol": "http",
    "baseURL": "api.linkedin.com",
    "publicPath": "",
    "privatePath": "/v1",
    "auth": "oauth",
    "oauth": {
      "type": "three-legged",
      "requestURL": "https://api.linkedin.com/uas/oauth/requestToken",
      "signinURL":  "https://api.linkedin.com/uas/oauth/authorize?oauth_token=",
      "accessURL":  "https://api.linkedin.com/uas/oauth/accessToken",
      "version": "1.0",
      "crypt": "HMAC-SHA1"
    },
    "keyParam":""
  },
  "foursquare": {
    "name": "Foursquare (OAuth 2.0 Auth Code)",
    "protocol": "https",
    "baseURL": "api.foursquare.com",
    "publicPath": "",
    "privatePath": "/v2",
    "auth": "oauth2",
    "oauth2": {
      "type": "authorization-code",
      "baseSite": "https://foursquare.com/",
      "authorizeURL": "oauth2/authenticate",
      "accessTokenURL": "oauth2/access_token",
      "customHeaders": "",
      "tokenName": "oauth_token"
    },
    "keyParam":""
  }
}

이렇게 정의할 경우 첫 페이지에서 linkedin과 foursquare 두 개의 링크가 생성되고 각각 키에 해당하는 파일(linkedin.json과foursquare.json)이 존재해야 한다. 각 API에 대한 세부사항은 각각의 파일에서 정의하고apiconfig.json`에서는 프로토콜이나 공통되는 경로, 인증방식등을 정의한다.

{
  "endpoints": [
    {
      "name": "players",
      "methods": [
        {
          "MethodName": "선수 목록 조회",
          "parameters": [],
          "RequiresOAuth": "N",
          "URI": "/players",
          "HTTPMethod": "GET",
          "Synopsis": "선수 목록을 조회한다"
        },
        {
          "MethodName": "선수 정보 조회",
          "parameters": [
            {
              "Required": "Y",
              "Type": "string",
              "Name": "id",
              "Description": "선수 ID"
            },
            {
              "Type": "string",
              "Name": "type",
              "Description": "반환 타입",
              "Default": "json"
            }
          ],
          "RequiresOAuth": "N",
          "URI": "/players/:id",
          "HTTPMethod": "GET",
          "Synopsis": "특정 선수 정보를 조회한다"
        }
      ]
    }
  ]
}

각 API별로 JSON파일을 만들어서 위처럼 정의할 수 있다. 모든 API는 endpoints키 하위로 들어가고 배열에 객체마다 자동으로 그룹핑이 되므로 적당한 이름을 주어서 그룹을 분류하고 methods아래에 관련 API를 적으면 된다. MethodName은 문서에 표시될 API 이름이고 파라미터는 필요한대로 정의하면 된다. 인증이 필요한 API를 따로 표시해 줄 수 있고 URI는 apiconfig.json에서 정의했던 URL의 하위만 적으면 된다. 여기서 :id처럼 적으면 패스 파라미터로 동작하고 parameters에 들어간 같은 이름의 값이 경로에 전달된다. HTTPMethod로 요청 메서드를 적어주고 Synopsis에서 간단한 설명이나 예시등을 적어주면 된다. 구조가 복잡하지 않아서 조금 보고 있으면 금새 이해할 수 있고 인증방식도 현재 OAuth등 여러 가지를 지원하고 있다. 전에는 개발 진행상태가 좀 불안하기도 했는데 요즘은 열심히 개발하고 있는 듯 하다.(Node.js라서 프로젝트에 잘 안맞는 부분이 있으면 수정해서 사용하면 된다.)
이를 실행하면 다음과 같이 나온다.

위에서 JSON으로 정의한 API를 I/O Docs에서 본 화면


아쉬운 점?

완전히 맘에 들진 않지만 현재 사용해 본거나 아는 수준에서는 그나마 제일 나아보인다. 어차피 손이 가야한다면 적은 노력으로 괜찮은 API 문서를 제공할 수 있고 클라이언트 개발하는 입장에서는 스펙확인해보고 파라미터별로 결과를 바로 볼 수 있기 때문에 편리한 점이 여러 가지가 있다. 클라이언트와 서버가 따로 개발되는 경우에는 서버쪽에서 값이 갑자기 달라진 경우 무척이나 곤혹을 치루기 때문에... 기본 디자인이 조금더 이뻤으면 하는 바램이 있지만 이건 필요하면 수정해서 써도 되는거니....

좀 더 원하는 형태는 이 I/O Docs를 이용해서 동작가능한 문서화를 제공하고 각 API의 기본값이나 지정한 값으로 전체 요청을 날려서 미리 기대한 결과값이랑 비교해보는 것, 그러니까 실제 서버랑 현재 문서의 스펙과 달라진 점이 있는지 확인하기 위한 테스트가 자동화되었으면 좋겠는데 아직은 그런 기능은 없다.

2013/10/21 18:38 2013/10/21 18:38