RIDI Style Guide

HTTP API 작성 가이드

API는 공개 수준에 따라 아래의 3가지로 구분하며, 가능한 높은 공개 수준을 지원해야 한다.

  1. Public API

    • 서드파티 혹은 임의의 개인이 사용 가능
    • FQDN: api.ridibooks.com
      • 도메인은 API Gateway에 연결되어 있으며, 변경 필요시 @ridi/performance 팀에 요청
    • 보안 프로토콜(TLS)을 사용해야 함
    • 인증에는 OAuth 2.0을 사용
    • 첫 번째 Path Segment는 서비스명으로 시작한다.
      • 예) 검색 서비스: api.ridibooks.com/search/
      • 예) 책 상세 서비스: api.ridibooks.com/books/
  2. Protected API

    • 세컨드파티 혹은 내부 직원만 사용 가능
    • FQDN:
      • 공인 IP가 있다면 {team}-api.ridibooks.com
      • 공인 IP가 없다면 api.{team}.ridi.io
    • 공인 IP를 가질 경우 보안 프로토콜(TLS)을 사용해야 함
    • 클라이언트 라이브러리가 필요한 경우 사용하는 팀에서 직접 작성
  3. Private API

    • 팀 내부에서만 사용 가능
    • 도메인 규칙 없음
    • 포맷, 문서화는 팀 내 규약에 따라 자유롭게 관리
    • 하위호환 유지할 필요 없음

Public/Protected API는,


신뢰할 수 있는 서비스간의 인가(Inter-Service Authorization)

마이크로서비스를 운영하다보면 내부 서버간, 즉 신뢰할 수 있는 서버간의 API 통신이 필요한 상황이 발생한다. 이 때 인가는 JWT(JSON Web Tokens)를 통해 이루어져야 하며, 사용되는 토큰은 아래의 조건을 만족해야 한다.

예) 구매목록 서비스에서 책 서비스의 API를 호출하는 경우

{ // header
  "typ": "JWT",
  "alg": "RS256"
}
{ // payload
  "iss": "library",
  "aud": "book",
  "exp": 1531819973
}


내부 서비스간의 SSO

마이크로서비스 환경에서 최종사용자의 SSO(Single Sign-On)를 지원하기 위해 OAuth2를 사용한다. 이 때 access token은 JWT(JSON Web Tokens) 형태로 발급되며, 사용되는 토큰은 아래의 조건을 만족해야 한다.

예)

{ // header
  "typ": "JWT",
  "alg": "ES256"
}
{ // payload
  "u_idx": 12312233,
  "sub": "antiline",
  "exp": 1518505258,
  "client_id": "**given_client_id**",
  "scope": "all"
}


JWT 서명

RSA 혹은 ECDSA 기반으로 JWT를 서명하는 경우 JSON Web Key Set (JWKS) 형식으로 공개키 집합을 전달한다. JWKS는 키 로테이션을 위한 표준화된 수단을 제공하므로, 기존에 사용하고 있던 PEM 형식의 파일 전달 방식을 대체한다.

  1. JWK 객체는 아래의 조건을 만족해야 한다.

    • kid(Key ID)는 반드시 작성
    • kty(Key Type)은 반드시 "RSA" 혹은 "EC"
      • kty가 "RSA"인 경우 alg(Algorithm)는 반드시 "RS256"
      • kty가 "EC"인 경우 crv(Curve)는 반드시 "P-256"
    • use(Public Key Use)는 반드시 "sig"

    예)

    {
      "keys": [{
        "kid":"1234example=",
        "kty":"EC",
        "crv":"P-256",
        "use":"sig",
        "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
        "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
      }, {
        "kid": "5678example=",
        "kty": "RSA",
        "alg": "RS256",
        "use": "sig",
        "e": "AQAB",
        "n": "987654321"
      }]
    }
    
  2. 서명하는 측은 키 정보를 담은 JSON 파일을 서버에 업로드하고 해당 URL을 통해 공유한다.
    • 예: https://www.googleapis.com/oauth2/v3/certs
  3. 서명하는 측은 인증하는 측이 JWK 형식을 지원하는지 사전에 확인해야 한다.
    • 지원하지 않는 경우 PEM 형식의 정적 파일을 전달한다.


테스트 환경을 공유하기

MSA 기반의 서비스들은 개발에 필요한 테스트 환경이 원활하게 제공할 수 있어야 하며 아래 규칙을 준수해야 한다.

테스트 환경은 무중단으로 운영되지 않을 수 있으므로 사전에 이용을 협의하는 것을 원칙으로 한다.


HTTP API 작성 가이드


HTTP 상태 코드

다양한 response code를 사용하면 그 자체로 명시적이지만 코드 관리가 어려워진다는 단점이 있다. 따라서 아래 명시된 보편적인 response code만을 사용하고 더 자세한 내용은 message-body에서 제공한다.

상태 코드 의미 용도
200 OK 성공적으로 처리 에러 응답에 대해서 사용하지 않도록 한다
201 Created 리소스 생성 완료 가능하면 응답 헤더 Location필드에 리소스를 접근할 수 있는 URI를 포함시킨다.
204 No Content 내용없음 요청 처리 성공 후 특별히 message body에 포함시킬 내용이 없는 경우 사용한다.
301 Moved Permanently 요청한 리소스가 새로운 URI를 부여받았음 새 URI를 응답 헤더 Location필드에 명시한다.
302 Found URI가 임시로 변경됨 301과 비슷하지만 일시적으로 옮겨진 경우에만 사용한다.
304 Not Modified 요청한 리소스가 변경되지 않음 If-Modified-Since 요청 헤더에 대한 응답으로 활용될 수 있다.
400 Bad Request 잘못된 요청 정의되지 않은 형식이나 보내면 안되는 요청에 대한 응답.
401 Unauthorized 리소스 접근 권한이 없음 가능하면 응답에 인증에 관한 정보를 포함시켜 필요한 경우 클라이언트에서 추가 요청을 보낼 수 있도록 한다.
403 Forbidden 숨겨진 리소스에 접근하려 함 필요하다면 거부된 이유를 응답에 포함시키고,아예 공개할 의사가 없다면 404: Not Found를 사용한다.
404 Not Found 매칭되는 URI가 없음 실제 리소스가 존재하더라도 존재 여부를 노출시키지 않고 싶은 경우에 사용할 수 있다.
429 Too Many Requests 너무 많은 요청 가능하면 얼마나 기다린 후에 새로운 요청을 받을 수 있는지 응답 헤더의 Retry-After필드에 명시한다.
500 Internal Server Error 서버 에러 요청은 정상적으로 받았지만 서버 처리 중 문제가 발생할 경우 전반에 사용한다.
502 Bad Gateway 게이트웨이 에러 서버가 게이트웨이나 프록시로 사용 중인 경우 upstream서버에 이상이 있을 때 사용한다.
503 Service Unavailable 서비스 이용 불가 대기 시간을 알 수 있다면 응답 헤더의 Retry-After필드에 명시한다.

참고