REST API

REST Series

  1. REST API #1 - 이해하기
  2. REST API #2 - 디자인 가이드
  3. REST API #3 - 안티 패턴 (현재 글)

지난 포스팅에 이어, REST API 안티 패턴에 대해 포스팅 해보겠습니다.

이전 편을 안보신 분이 있다면 위 링크에서 확인해주세요.


지난 두 포스팅을 통해 REST API 가 어떤 것이고, 어떻게 디자인 하면 되는지 알아봤습니다.

우리가 어떤 걸 해야한다고 배울 때, 더 효과적으로 배우려면 어떤 걸 하지 말아야하는지를 배우면 좋습니다.

그래서 이번 포스팅은 REST API 에서 하지 말아야할 안티 패턴 (REST Anti Patterns) 에 대해 얘기해보자 합니다.


REST Anti patterns

REST Anti patterns

인터넷에서 “RESTful API” 를 찾아보면 많은 블로그, 문서들이 “이게 REST 한 API 란다?” 라고 얘기합니다.

그렇지만 많은 수의 글들이, 그리고 그 글을 보고 만든 API 들이 REST 하지 않은 경우가 많습니다.

제 생각엔 REST 개념 자체가 어렵기도 하지만, 이걸 제대로 적용하려면 개발자가 챙겨야할 디테일들이 많기 때문이죠.

그래서 저도 급한 경우 REST 의 외관을 표방하지만, 응답에 링크나 하이퍼미디어를 표현하지 않거나 에러 코드를 제공하지 않는 등 디테일을 못챙기는 부분이 많습니다.

늘 그렇지만 개발자, 특히 스타트업 개발자라면 언제나 시간에 쫒기기 마련이고,

완성 못하는 것보다, 완벽하지 않은 것이 더 쓸만하다

- 키위남

이라는 말이 있듯이 완벽을 추구하는 것은 개발자에게 위험한 생각일 수도 있기 때문이죠.

그럼 이제 본론으로 넘어가서 REST API Anti pattern 에 대해 알아보도록 하겠습니다.


1. GET,POST HTTP 터널링

HTTP 터널링은 모든 요청을 하나의 메소드로 처리하는 것을 말합니다.

예를 들어 회원 가입도 GET, 회원 조회도 GET, 회원 삭제도 GET 혹은 POST 로 정보 조회 등이 있습니다.

# HTTP 터널링의 예시
# GET 으로 회원 가입하기
GET /users?method=create&email=charlie@kiwinam.com

# POST 로 멤버 리스트 가져오기
POST /users?offset=0&limit=10

위 예시처럼 동작과 맞지 않는 메소드를 사용하거나 모든 동작에 하나의 HTTP 메소드를 사용하는 것을 HTTP 터널링이라고 합니다.

이 패턴의 문제점은 이렇습니다.

  1. 자원이 URI 에 표현되지 않습니다. URI 보다 쿼리 파라미터에 동작들이 설명됩니다.
  2. HTTP 메소드가 실제 동작과 일치하지 않습니다.
  3. 크롤러가 의도치 않는 부작용을 일으킬 수도 있습니다. (예를 들어 GET 요청인 줄 알고 크롤러가 시도했지만 실제론 회원 가입이 일어나는 경우)


2. URI 에 행위(method)을 표현

이전 포스팅에서도 언급했던 부분입니다.

URI 에 동사를 사용하지 않는다.

이 말은 REST 한 API 는 URI 를 명사로만 표현한다 라는 것입니다.

그 의미는 API 가 실제로 하는 동작들, 예를 들어 생성하고, 삭제하고, 수정하고, 검색하고 등의 행위 (method) 는 URI 표현하지 않는다. 라는 의미입니다.

이유는 간단합니다.

REST 는 HTTP 를 어떻게 더 잘 사용할 것인지에 대한 고찰이 담겨 있는 정수 같은 것입니다.

HTTP 에는 이미 4개의 method 로 요청에 대해 정리하였습니다.

  1. GET - 정보를 가져옵니다.
  2. POST - 정보를 생성합니다.
  3. PUT - 정보를 수정합니다.
  4. DELETE - 정보를 삭제합니다.

이미 HTTP 요청을 보내는 순간 API 의 동작을 정의하기 때문에, URI 에 중복해서 표현할 필요가 없습니다.


3. 캐시 무시

HTTP 에는 속도 증가 그리고 트래픽과 서버의 자원을 아끼기 위한 많은 엔지니어링 테크닉들이 적용되어 있습니다.

그 중 대표적인 것이 여러분들도 익히 들어 알고 계실만한 캐시 입니다.

캐시라는 것은 웹 페이지나 Response 에 대해 내부에 저장하고, 동일한 페이지 혹은 동일한 요청이 있을 때 실제로 서버에 파일을 요청하거나 Request 에 대한 Response 를 받는 게 아니라,

기존에 있는 것을 보여줌으로써 빠르게 + 적은 자원을 사용해 효율적으로 요청을 처리할 수 있습니다.

다만 이런 캐시도 Request Header 에 한 줄을 표시하면 사용하지 못하는데, 그것은

Cache-control: no-cache

입니다.

이렇게 설정하면 어떤 것도 캐시 내부에 저장할 수 없습니다.

캐시는 RESTful API 에서 중요한 이점 중 하나입니다.


4. 하이퍼미디어 무시

다른 글들에서 HATEOS 라고 표현되는 Hypermedia as the engine of application state 를 무시하지 말자! 라는 내용입니다.

하이퍼미디어의 특징을 이용해서 HTTP Response 에 현재 API 에 관련되어 있는 다른 동작들에 대한 URI 를 함께 제공하는 것을 HATEOS 라고 합니다.

HATEOS 를 잘 활용하면, REST 에서 어려운 개념인, 자기 표현 (Self-DEscriptive) 가 높아져서 API 가독성이 좋아집니다.

하이퍼미디어를 제공하는 API 의 Response 를 예시로 보여드리겠습니다.

GET /users?offset=50&limit=50
HTTP 200
{
    "users": [
        {
            "id": 1,
            "name": "kiwinam"
        },
        {
            "id": 2,
            "name": "charlie"
        }
    ],
    "links":[
        {
            "rel": "self",
            "href": "http://api.kiwinam.com/users",
            "method": "GET"
        },
        {
            "rel": "prev_list",
            "href": "http://api.kiwinam.com/users?offset=0&limit=50",
            "method": "GET"
        },
        {
            "rel": "next_list",
            "href": "http://api.kiwinam.com/users/offset=100&limit=50",
            "method": "GET"
        },
        {
            "rel": "delete_all_users",
            "href": "http://api.kiwinam.com/users",
            "method": "DELETE"
        },
        {
            "rel": "create_user",
            "href": "http://api.kiwinam.com/users",
            "method": "POST"
        }
    ]
}


5. MIME 타입 무시

HTTP 는 요청할 때 내가 보내고자 하는, 받고자하는 컨텐츠의 형식을 사전에 협상할 수 있습니다.

많이 사용하는 컨텐츠 타입은 json, xml, yaml 등이 있는데, 최근 json 형식으로 통일되고 있는 추세입니다.

REST API 를 설계하면서 컨텐츠 타입을 중복해서 제공하거나 제공하지 않는다면, 클라이언트와 소통에 있어 어려움을 겪을 수 있습니다.

예를 들어 json 과 xml 을 동시에 제공하는 경우, 클라이언트는 데이터 파싱을 두 번 해야합니다.

혹은 mime 타입을 설정하지 않는 경우 클라이언트가 어떻게 데이터를 파싱해야할 지 모르는 상황도 생길 수 있습니다.


6. 하나의 동작을 위해 여러 번의 API 를 호출하지 말자.

이 부분은 예를 들어 설명하는 것이 더 확실할 것 같아 바로 예시를 보여드리겠습니다.

# 회원 가입
# 1. 동일한 이메일 존재하는지 확인
GET /users/charlie@kiwinam.com

# 2. 유저 데이터 생성
POST /users
{
    "email": "charlie@kiwinam.com"
}

# 3. 유저 데이터에 이름 추가
PATCH /users/charlie@kiwinam.com
{
    "name": "kiwinam"
}

# 4. 유저 데이터에 비밀번호 추가
PATCH /users/charlie@kiwinam.com
{
    "password": "123456"
}

예시를 보여드리기 위해 좀 극단적으로 요청을 많이 보냈지만, 이런 경우가 생길 수 있습니다.

API 는 하나의 동작을 제공하되, 그 안에 여러가지의 기능을 조합해서 하나의 동작을 제공하는 것이 좋습니다.

# 회원가입
POST /users
{
    "email": "charlie@kiwinam.com",
    "name": "kiwinam",
    "password": "123456
}

7. 자기 표현 구조를 무시

거의 대부분의 REST API 가 REST 하지 않은 증거를 찾는다면, 이 부분에서 찾을 수 있습니다.

자기 표현 구조 (Self-descroptiveness)는 API 표현 자체가 쉽고 직관적이여서 api 만 읽고도 어떤 API 인지 알 수 있는 것을 얘기합니다.

API 의 URI 에서는 자기 표현 구조를 지키려 하는 곳이 많은데, 응답 헤더나 요청 메세지, 응답 메세지에는 자기 표현 구조를 따르지 않는 곳이 많습니다.

위에 언급한 HEATOS 를 지키지 않거나, 헤더에 정보들이 클라이언트, 중개자, 서버에서 API 를 이해하기에 부족한 경우 등이 있습니다.



여기까지 REST API 에 대해 자세히 알아봤습니다.

그런데 역시 쉽지 않고, 지켜야할 규칙들과 아직 엔지니어들 사이에도 의견이 분분한 항목 등.. 제대로 된 REST 를 만드는 것은 힘든 일이네요.

규격이 아직 정의 되지 않은 REST 이니, 너무 REST API 가 이렇다, 저렇다 논쟁하지 말고 더 좋은 코드를 위해 고민하는 것도 좋은 방법인 것 같습니다.

이번 포스팅이 여러분들에게 도움이 됐다면 좋겠습니다.

감사합니다.


연관된 글들

REST Series

  1. REST API #1 - 이해하기
  2. REST API #2 - 디자인 가이드
  3. REST API #3 - 안티 패턴 (현재 글)