본문 바로가기

Web

REST한 API를 만들기 위한 약속

이번 글에서는 REST API 를 구현하는데 도움이 되었던 내용을 공유하려고 한다. 앞으로 언급되는 내용들은 정답이 아니고, 하나의 약속임을 먼저 밝힌다.

우리가 약속할 내용을 다음과 같다.

  • URL을 작성할 때 주의할 점
  • SSR
  • 버전관리
  • 자원을 필터링해야 한다면 Query Parameter 를 이용하자.
  • 데이터를 묶어서 전달해라.
  • 약속된 상태코드를 사용해라.
  • Stateless 하게 작성해라.

1. URL을 작성할 때 주의할 점

1.1 명사를 사용해라

URL은 우리가 제공 할 자원을 중심으로 구성되며, 자원은 반드시! 명사 로 써야하며 절대로 동사를 사용하면 안된다. 아래 예시를 보자

  • GET /lectures - 강의 리스트를 조회한다.
  • GET /lectures/12 - #12 라는 특정 강의을 조회한다.
  • POST /lectures - 새로운 강의을 생성한다.
  • PUT /lectures/12 - #12 라는 특정 강의을 수정한다.
  • PATCH /lectures/12 - #12 라는 특정 강의을 부분적으로 수정한다.
  • DELETE /lectures/12 - #12 라는 특정 강의을 제거한다.

1.2 복수형을 사용해라

반드시 따라야하는 네이밍 규칙은 없지만 URL은 반드시 깔끔하고 명확 해야 한다. 당신의 URL을 표현할 때, 문법적으로 복수형을 사용해야 할 때가 아니라도, 항상 복수형을 써야 한다.

person / people, goose / geese 와 같은 난해한 복수형들을 사용한다면 API를 사용하는 사람들의 짜증을 유발할 것이며, URL을 이해하는 어려울 것이다.

1.3 관계를 표현해라

자원들 간에 관계를 가질 수 있다면, RESTful 원칙은 아주 유용한 가이드를 제공해준다. 아래 몇가지 예시를 보자.

강의는 여러개의 평가를 가질 수 있다. 이 평가들은 /lectures endpoint 에 아래와 같이 맵핑되어 진다.

  • GET /lectures/12/comments - #12 라는 강의의 평가 리스트를 조회한다.
  • GET /lectures/12/comments/15 - #12 라는 강의의 평가 중 #5의 평가를 조회한다.
  • POST /lectures/12/comments - #12 라는 강의에 새로운 평가를 생성한다.
  • PUT /lectures/12/comments/5 - #12 라는 강의에 #5의 평가를 수정한다.
  • PATCH /lectures/12/comments/5 - #12 라는 강의에 #5의 평가를 부분적으로 수정한다.
  • DELETE /lectures/12/comments/5 - #12 라는 강의에 #5의 평가를 삭제한다.

2. SSR

항상 SSL을 사용해라. 이것은 API를 디자인하는데 있어 가장 기본적인 원칙이다. 당신이 만든 API의 endpoint 들이 SSL을 통해 노출 될 수 있도록 해라.

3. 버전관리

API는 항상 버전관리가 되어야 한다. 버전관리는 API endpoint들을 개발/수정하는데 있어 좀 더 빠르고, 유효하지 않는 요청들로 부터 도움을 준다. 또한, 오래된 API를 끊키지 않고 제공하면서 동시에 새로운 API도 제공 할 수 있도록 한다.

아래와 같이 버전을 사용해라!

/api/v1/lectures/

4. 자원을 필터링해야 한다면 Query Parameter 를 이용하자.

쿼리 파라미터를 이용하는 경우는 크게 아래 3가지로 나눌 수 있다.

  • Filtering : 강의 리스트를 조회하는 endpoint /lectures 가 있을 때, 당신은 열려 있는 강의만 조회할 수 있도록 제한하고 싶을 수도 있다. 그럴 때에는 다음과 같이 필터링을 하자. GET /lectures?state=open . state 는 필터를 구현 할 수 있는 쿼리 파라미터다.
  • Sorting : 필터링과 유사하게, sort 는 Sotring 규칙을 표현할 수 있는 파라미터다. Sotring 할 필드들을 콤마로 구분하고, 내림차순으로 Sorting 하고 싶은 경우에는 앞에 - 를 붙이는 것이 일반적이다. 예를 들어, GET /lectures?sort=priority, -created_at 은 강의의 우선 순위를 오름차순으로, 생성된 시간을 내림차순으로 정졀하여 조회한다.
  • Searching : 필터링과 정렬이 우리가 조회하려는 정보를 충분히 나타내는데 부족할 수 있다. 이 때, q 파라미터로 full-text search 를 지원하면, 좀 더 정확한 정보를 조회할 수 있다.

위 3가지를 조합한 예시를 보자.

  • GET /lectures?sort=-updated_at - 최근에 업데이트 된 강의를 조회한다.
  • GET /lectures?state=open&sort=-updated_at - 최근에 업데이트된 강의 중 열려있는 강의를 조회한다.
  • GET /lectures?q=tutorial&state=open&sort=-priority,created_at - 우선순위가 높고, 생성된지 오래된 강의 순으로 검색하는데 검색어는 'tutorial' 이다.

5. 데이터를 묶어서 전달해라.

// Good 🙆
{
  "success": true
  "error": null
  "data": {
    {
      "name": 'foo',
      "rating": 4,
      "guides": [
        {
          "name": "a",
          "role": "A"
        }.
      ]
    }
  }
}

// Bad 🙅
{
  "name": 'foo',
  "rating": 4,
  "guides": [
    {
      "name": "a",
      "role": "A"
    }.
  ],
  "success": true,
  "error": null
}

데이터를 묶어서 전달하게 되면, 프론트엔드 개발자와 친해질 수 있다.

프론트엔는 아래와 같이 처리하면 된다.

const { data, error } = payload
if (error) { throw ...}

6. 약속된 상태코드를 사용해라

6.1. 요청한 데이터에 문제가 있을 경우

  • 400 - 요청한 정보가 완전히 구성되어 있지 않은 경우
  • 422 - 요청한 정보가 구성은 맞지만, 값이 유효하지 않은 경우
  • 404 - 요청한 정보도 맞고, 값도 유효하지만, 데이터가 없을 경우
  • 409 - 이미 데이터가 있는 경우

6.2. 인증 오류

  • 401- access token이 만료되어 권한이 없는 경우
  • 403 - access token은 유효하지만, 접근 권한이 없는 경우

6.3. 일반적인 경우

  • 200 - 성공
  • 204 - 정상적으로 처리되었지만, 반환되는 정보가 없는 경우
  • 500 - 서버쪽에 문제가 있는 경우

7. Stateless하게 작성해라

모든 상태는 Client Side 에서 처리 되어야 한다. 이 말은 요청 처리하는데 있어 모든 정보들이 요청 안에 포함되어 있어야 한다. 서버는 이전 요청에 대한 정보를 알고 있지 않으며 알고 싶어 하지도 않는다.

'잡지의 다음 페이지 내용이 궁굼해요' 라는 API를 만들 수 있을까?

/magazines/nextPage 와 같이 구성을 한다면, 서버는 아래와 같은 서비스 로직이 필요하다

nextPage = currentPage + 1
send(nextPage)

위 서비스 로직은 _Client Side_에 있어야 한다. 따라서 API는 아래와 같이 구성되면 클라이언트에서 모든 정보들을 넘겨줘야 한다.

GET/magazines/page/7 또는 GET /magazines?page=7

위 내용들이 당신의 멋진 API를 만드는데 조금이나마 도움이 되었으면 좋겠다.

'Web' 카테고리의 다른 글

OAuth 완벽하게 알고가기  (0) 2020.04.09