본문 바로가기

책, 강의 정리/세미나정리

[세미나정리] 그런 REST API로 괜찮은가

https://www.youtube.com/watch?v=RP_f5dMoHFc - 이응준님의 세미나를 보고 정리한 내용입니다. 

 

 

들어가기전에 

 

며칠전에 REST API 가 무엇이냐는 질문을 받았다.

명확하지 않은 점이 있어서 말끝을 흐리며

"자원과 Method 만으로 어떤 API인지 알 수 있도록 하는 표준"

이라고 대답했던 것 같다.(사실 이것보다 훨씬 버벅거렸겠지요 .. ㅎㅅㅎ )

 

강의를 듣고 생각해보니 Self-descriptive, HATEOAS 키워드만 얘기했어도 참 좋았을 것 같다.

 

알고보니 대학생 때 한창 면접준비할 때 "REST"가 이해가 안가서 봤었던 자료였고, 지금 다시 보니 그때보다 더 이해가 가는 것 같기도 해서 좋았다.

 

 

시작하며

 

"REST 스럽지 않은 것 같아요 .. REST API 가 아닌거같은데 ..?" 라는 수많은 IT 전문가들의 말에서 "REST"는 어떤 의미일까?

 

REST가 뭔지 알아보니 아래와 같은 정의를 가진다고한다.

"REpresentational State Transfer", "컴퓨터 시스템 간 상호운용성을 제공하는 방법"

 

음, 모르겠다! 여기서 역사를 먼저 살펴보기로한다.

 

 

 

WEB의 역사

 

WEB이 퍼지고 있을 때 아래와같은 고민이 생겼다.

Q. 어떻게하면 인터넷으로 정보를 잘 공유할 수 있을까?

A. 정보들을 하이퍼텍스트로 연결하자. 다음의 규칙을 가지고!

  • HTML: 표현형식
  • URL: 식별자
  • HTTP: 프로토콜

cf) 하이퍼텍스트: 문서 내에서 텍스트를 통해 이동할 수 있는 것. HT 에서 HT 가 하이퍼텍스트에 해당한다.

 

위의 약속을 기반으로 HTTP/1.0 작업하는 도중 호환성에 대한 고민이 생긴다.

"이미 Web 은 전세계에 퍼지고 있다. 근데 HTTP/1.0 을 작업한다 …? 기존의 웹과의 호환성은 ..? "

그리고 "HTTP Object Model" 이라는 "표준"을 만들게 되는데, 이것이 Web 상의 REST 의 시작이다.

 

API가 등장하자

MS의 API 표준 XML-RPC( 훗날 SOAP)

- 최초의 API인 SalesForce API 는 SOAP 으로 만들어졌다.\

- flickr API 는 SOAP으로도, REST 로도 만들어졌다.

-> 오 REST, 새로운데?

-> 오, SOAP 보다 훨씬 빠른데?

 

 

SOAP vs REST 를 비교하기 시작한다.

REST 의 승리 ….. 인줄 알았으나 ,

 

REST API는 REST 하지 않았다.

<CMIS>

CMS 를 위한 표준. REST 바인딩 지원 ….

REST 의 창시자: "그건 REST 가 아니야"

 

<MS 의 REST API 가이드라인 발표> 

REST 의 창시자: 이것도 REST API가 아니다. 이건 HTTP API 다.

그리고, "REST API 를 위한 최고의 버저닝 전략은 버저닝을 안하는 것"

 

 

그럼 뭐가 REST API?

 

"분산 하이퍼미디어 시스템을 위한 아키텍쳐 스타일"을 모두 지켜야 REST API 이다!

 

지켜야 하는 스타일

  • client-server: Client와 Server의 역할이 명확하게 구분되어있어야한다. 의존성을 낮춘다.
  • stateless: Server는 Client 의 상태정보를 계속 가지고있을 필요가 없다. Server는 오는 요청에 대한 응답만 할 뿐.
  • cacheable: 캐시사용이 가능해야한다.( Expire, Cache-Control, ETag 등의 헤더값)
  • uniform interface: 통일된 인터페이스 
  • layered system
  • code-on-demand(optional ) -> 서버에서 클라이언트에 코드를 보낼 수 있다.(Js)

 

 

대부분의 규칙은 HTTP API를 사용하기만 해도 지킬 수 있는 규칙이다.

하지만 가장 지켜지지 않는 규칙이 바로 "Uniform Interface" 규칙이다.

 

 

Uniform Interface

 

RESET의 등장배경: "서버와 클라이언트의 독립적인 진화를 위해"

즉, 서버의 기능이 변경되어도 클라이언트는 업데이트할 필요가 없다. 

 

그러기 위해 모든 REST API는 "공통의 인터페이스"를 가져야하는데 아래 네개의 규칙을 만족해야한다.

  • 리소스는 URI 로 식별
  • HTTP 메서드에 표현을 담아야한다
  • self-descriptive message
  • HATEOAS

 

밑줄친 두가지가 특히나 못지키는 규칙이다. 하나씩 살펴보면,

 

<Self-Descriptiveness Message>

"메시지가 스스로 어떤 API인지 설명해야 한다."

 

GET / -> 목적지가 빠져있다 = REST 하지 않다

 

또, 응답에는 'Content-Type' 이 있어야 한다. 그래야 그 Content-Type 에 해당하는 명세를 찾아가서 어떤 내용을 의미하는지 알 수 있다.  

 

 

 

<HATEOAS>

애플리케이션의 상태가 하이퍼링크를 통해 전이되어야 함.

  • a 태그를 통해 다음 상태로 전이될 수 있도록 제공.
  • 응답에는 Link 헤더로 제공 가능

 

예를들어, 게시글 목록페이지에서 링크를 통해 > 게시글 하나 > 또, 거기서 수정페이지 등으로 상태 전이가 가능하다

 

 

Web은 Uniform Interface를 만족할까?

 

1. 웹페이지를 변경해도 웹브라우저 업데이트 X --------> "독립적이다"

2. HTTP/HTML 명세가 변경되어도 웹은 잘 동작한다 --------> "독립적이다"

3. 하지만 앱은 클라이언트가 서버를 지원하지 않을 수 있다. --> 앱과 서버가 REST 스타일은 따르고있지 않다.

4. Web의 상호운용성에 대한 집착 ------> 이건 좀 재밌다 

  • Referer 오타지만 못고침. 이미 따르고 있는 것이 많으니까.
  • charset 도 잘못 지었지만 못고침. encoding 이 더 적합함
  • HTTP 상태코드 416 포기함 .. (HTTP도 아닌 다른 프로토콜이 이미 사용하고있고, 많은 애플리케이션이 그걸 따르고있어서)
  • HTTP/0.9 아직도 지원함

 

 

REST 가 웹의 독립적 진화에 도움을 주었는가? ----------- YES

  • Host 헤더추가   -----> Self - Descriptiveness
  • 길이제한을 다루는 방법 
  • URI 에서 리소스의 정의가 "식별하고자하는 무언가"라고 추상적으로 변경
  • HTTP/1.1 에는 REST 에 대한 언급이 들어감

 

"Web에 의하면, REST 는 성공했다"

 

 

 

그럼 REST API 는 성공했는가?

 

"REST API 는 REST 표준을 지켜야한다" -> API 에서 REST 는 쉽지 않았다.

 

 

결론: 시스템 전체를 통제할 수 있거나 진화에 관심이 없으면 REST에 시간을 낭비하지 마라 

즉, 사내에서만 쓰는 API 명세 모두가 알아볼 수 있다면 REST에 시간 낭비하지 말아라. 모든 API 가 REST일 필요는 없다.

 

정하자,

  1. REST API 를 구현하고 그렇게 부르자
  2. 포기하고 HTTP API 라고 부르자
  3. REST API가 아니지만 그렇게 부르자 -----------------> 현재(ex: MS)

 

 

 

REST API 를 구현해보자

 

왜 API 는 REST 가 어려울까?

: Json 은 하이퍼링크도, Self-descriptive 도 불안전하다(포맷은 있지만 값에는 범위가 없다)

 

----> 따라서, API 문서가 별도로 필요하다

 

Web) 

GET /todos HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: text/html

<html>
  <body>
    <a href="https://todos/1">회사가기</a>
    <a href="https://todos/2">집에가기</a>
  </body>
</html>

Content-type -> 미디어타입 -> text/html 의 명세를 찾을 수 있음 ------------> Self -descriptive

a 태그를 통해 다음상태로 전이 가능 -----------------> HATEOAS

 

API)

GET /todos HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/json

[
  {"id": 1, "title": "회사가기"},
  {"id": 2, "title": "집에가기"}
]

Content-type -> json 이네? -> json 명세 찾아보자 -> 파싱성공, 근데 id랑 title이 뭔데? --------------_> Self - descriptive 실패

상태전이같은거 없음 -------------------> HATEOAS 실패

 

<Self-descriptive, HATEOAS 와 독립적 진화외의 상관관계>

 

Self-descriptive- -> 확장가능한 커뮤니케이션

: 서버나 클라이언트가 변경되어도 언제나 해석가능하다.

 

HATEOAS -> late binding 가능

: 링크를 마음대로 바꿀 수 있다. 어차피 알려주는 링크대로 따라가는 것.

 

 

 

Self-descriptive 해결)

GET /todos HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/vnd.todo # 방법1. MediaType 바꾸기. 정의하고 IANA에 등록 
# 방법 2. Profile 
Link: <https://example.org/docs/todos>; ref"profile" 

[
  {"id": 1, "title": "회사가기"},
  {"id": 2, "title": "집에가기"}
]

방법 1: 귀찮다

방법 2: 클라이언트가 Link 헤더를 이해해야하고, Content negotiation 불가(클라이언트가 이 Link 헤더가 뭔지 모를 수 있음)

 

HATEOAS 해결)

GET /todos HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/vnd.todo
Link: <https://example.org/docs/todos>; ref"profile" 

[
  {
    "link": "https://example.org/todos/1", # 방법 1 
    "title": "회사가기"
  },
  # 방법 1
  {
    "links":{
      "todo" : "https://example.org/todos/{id}"
    },
    "data":[
      {
        "id": 1,
        "title": "회사가기"
      }
    ]
  }

]

방법 1: 링크포맷을 정의해야 함

방법 2: 헤더로 표현

data, 헤더 모두 활용할 수 있음

 

 

Q. 미디어타입은 꼭 등록해야하나?

A. 절충하자. 모두가 이해하는 API라면 굳이 그럴 필요가 없다.

 

 

느낀점

 

우선.. 함부로 "REST API를 개발했습니다." 라는 말을 하지 않아야겠다고 생각했다.

그리고, 현실적으로 REST API 규약을 지키기란 정말 어려울 것이며, 위에서 나온대로 최소한의 규칙은 지키되, 나름의 절충안을 찾아야하지 않을까 라고 느꼈다.

 

누군가 다시 REST API가 무엇인지 묻는다면

"Web 애플리케이션 환경을 오랫동안 독립적으로 발전시키기 위해 등장한 REST 표준을 지킨 API를 말한다. RESTful을 준수하기 위해서는 Stateless, Client-Servier, Uniform Interface 등의 조건을 만족해야하는데, Uniform Interface의 조건 중 Self-descriptiveness와 HATEOAS를 만족하는 것이 가장 어렵다." 라고 서두를 열어야겠다 : )