REST API 뽀개기

Chaeil·2022년 4월 30일
0
post-thumbnail

다니고있는 스타트업에 프론트엔드 인턴으로 막 들어갔을 때 개발팀 직원분들 대다수가 코로나에 걸리셨다. 혼자 코드를 보면서 낑낑대며 프로젝트 구조를 파악하고 있던 찰나에 같은 팀의 팀장님(모모님)이 세미나 발표를 하는게 어떻냐고 제안을 해주셨다. 입사한지 1~2주라 부담됐지만 결국 동기님(달코님)과 함께 REST API에 대해서 발표를 하게 됐다. 그래서 이 글은 동기님과 열심히 발표를 준비하며 정리한 글이다. 보안상의 이유로 생략, 변형한 부분도 있는점 참고바란다.

1. REST(REpresentational State Transfer)

REST란?

REST는 Representational State Transfer의 약자로 WWW(World Wide Web)과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐 스타일이다.

위의 정의만 봐서는 REST가 무엇인지 와닿기 힘들다. 그래서 REST를 구성하는 각 단어의 뜻을 하나씩 살펴보며 정의를 이해해보자.

우선, REST에서는 기본 데이터 표현을 자원(resource)라고 한다.

문서나 이미지, 시간 서비스, 다른 자원의 모음 또는 “사람"과 같은 가상이 아닌 객체 등 이름을 지정할 수 있는 모든 정보는 자원이 될 수 있다.

  • HTTP URI를 통해 자원을 명시한다.
/programmers
/programmers/{name}
...

representational state

REST에서 state(상태)란 자원(resource)의 상태를 의미한다.

특정 시간의 자원의 상태를 반영하는 정보를 자원의 표현(representation) 이라고 한다.

Representation

: 기계가 읽을 수 있는 자원의 현재 상태의 표현

  • 데이터(representation data)
  • 데이터를 설명하는 메타 데이터(representation metadata)
HTTP/1.1 200 OK
Content-Length: 6 // representation metadata
Content-Type: text/plain // representation metadata
Content-Language: en // representation metadata

hello // representation data

transfer

REST에서 전송(transfer)이란 이 representation의 전송을 의미한다.

예를 들어, 탈잉 홈페이지를 보고 있던 사용자가 탈잉 vod페이지로 가능 링크를 클릭하면, 웹 브라우저는 vod페이지를 렌더링해서 보여줄 것이다. 링크를 클릭함으로써 웹 애플리케이션의 상태가 홈페이지에서 vod페이지로 변경된 것이다.

즉, 서버에서 클라이언트로 representation을 전송(transfer)함으로써 웹 애플리케이션의 상태가 변경된 것이다.

이러한 전송은 주로 HTTP를 통해 JSON(Javascript Object Notation), HTML, XML, Python, PHP 또는 일반 텍스트 등의 형식 중 하나로 전달된다. 이 중 JSON은 언어에 구애받지 않고, 사람과 기계가 모두 읽을 수 있기 때문에 가장 일반적으로 사용되는 파일 형식이다.

정리하자면, REpresentational State Transfer

  • 자원의 표현(representation)에 의해 상태(state) 전달(transfer)
  • 즉, 클라이언트와 서버 간에 representation으로 해당 자원의 상태(정보)를 주고 받는 방법

다시 맨 처음 정의로 돌아가보자

REST는 Representational State Transfer 의 약자”로 WWW(World Wide Web)과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐 스타일이다.

지금까지 REST의 의미를 살펴보았다.

이어서 REST는 아키텍쳐 스타일의 한 종류이기 때문에 이에 대해 정하고 있는 제약 조건들이 있다. 이러한 조건들을 충족시키는 시스템과 API에 대한 개념을 먼저 살펴보도록 한다.

RESTful

REST는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일이다. 이런 REST 형식을 따른 시스템을 RESTful 이라고 부른다.

REST API

REST 아키텍처 스타일을 따르는 Web API를 REST API라고 한다.

REST는 HTTP 표준을 기반으로 구현하므로, HTTP를 지원하는 프로그램 언어로 클라이언트, 서버를 구현할 수 있다. 즉, REST API를 제작하면 델파이 클라이언트 뿐 아니라, 자바, C#, 웹 등을 이용해 클라이언트를 제작할 수 있다.

웹에 존재하는 모든 자원(이미지, 동영상, DB 등)에 HTTP URI를 부여해 자원을 명시하고, HTTP Method (POST, GET, PUT, DELETE 등)을 통해 해당 자원에 대한 CRUD operation을 적용하는 방법론이다.

REST 제약 조건

REST는 6가지 제약 조건이 있으며, 이를 잘 지킬 경우 웹서비스는 RESTful하게 된다.

1) client-server(클라이언트 / 서버 구조)

  • 네트워크가 자원이 있는 서버와 자원을 요청(상호작용)하는 클라이언트로 구성된다.
  • 상호 간 내부 작업을 알지 않아도 되도록 관심사를 분리하는 것이다.
  • 클라이언트와 서버가 독립적으로 진화할 수 있다.

2) stateless(무상태성)

  • 서버와 클라이언트에 상태가 없다는 의미가 아니라, 서로의 상태를 저장할 필요가 없다는 의미이다.
    • 즉, 쉽게 말해 서버가 API를 사용하는 사용자에 대해 아무 것도 기억하지 않는다는 의미이다.
  • 서버는 과거의 요청을 저장하지 않으며, 각 요청은 독립적으로 처리된다.
    • HTTP는 Stateless 프로토콜이므로 HTTP를 활용하는 REST 역시 무상태성을 가진다.
    • HTTP는 서버/클라이언트 모델을 따르며 클라이언트가 서버로 보내는 각각의 요청은 독립적이며 기억하지 않는다.
  • stateless를 통해 얻는 이점은 다음과 같다.
    • 서버 확장에 유리하다. 세션 관련 종속성이 업식 때문에 모든 서버가 요청을 처리할 수 있기 때문이다.
    • 서버측의 동기화 로직을 제거함으로써 REST API를 덜 복잡하게 한다.
    • 캐시에 수월하다. 이전의 요청을 볼 필요없이 하나의 요청만 살펴봄으로써 캐시의 여부를 결정할 수 있기 때문이다.
    • 클라이언트는 각 요청과 함께 필요한 모든 정보를 보내기 때문에 서버는 애플리케이션에서 각 클라이언트를 추적할 필요가 없다.

3) cacheable(캐시 처리 가능)

  • 요처에 대한 서버의 응답에 데이터가 캐시 가능한지 불가능한지 여부가 명시되어 있어야 한다.
  • 보통 HTTP Header에 cache-control 헤더를 이용한다.

4) layered system(계층화 시스템)

  • 서버는 계층화된 시스템 아키텍처를 사용할 수 있다.
    • 자원의 representation을 요청하는 클라이언트와 응답을 보내는 서버 사이에 중간 단계로 보안 계층, 캐싱 계층 등 여러 서버 계층이 있는 형태이다.
    • API 서버는 순수 비즈니스 로직을 수행하고 그 앞단에 사용자 인증, 암호화, 로드밸런싱 등을 하는 계층을 추가하여 구조상의 유연성을 줄 수 있다.
    • PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있게 된다.
  • 그러나 각 레이어는 바로 다음 레이어만 보고 상호 작용하도록 제한된다.
    • 중간 계층이 요청이나 응답에 영향을 미치지 않아야 된다.
  • 클라이언트는 일반적으로 최종 서버에 연결되어 있는지 중간에 연결되어 있는지 알 수 없는 상태이다.
    • 클라이언트는 REST API 서버만 호출한다.

5) code-on-demand (주문형 코드, optional)

  • 유일한 선택적 제약 조건이며 서버가 실행 코드를 클라이언트에 보낼 수 있는 기능을 나타낸다.
  • 클라이언트는 서버에 코드를 요청할 수 있으면, 요청을 받은 서버는 실행가능한 코드 로직(일반적으로 자바스크립트 등 스크립트 형식의 코드)을 제공한다.
    • 클라이언트는 서버로부터 받은 코드를 실행할 수 있다.
    • 해당 코드를 실행하여 클라이언트의 기능을 확장시킬 수 있다.
  • 이 제약 조건은 선택사항으로 API는 요청 시 코드를 제공하지 않아도 RESTful이 될 수 있다.

6) uniform interface(인터페이스 일관성)

  • 자원에 대한 요청을 통일하고 한정적인 인터페이스로 수행하는 아키텍처 스타일이다.
    • 한정된 인터페이스란, GET, PUT, POST, DELETE 4가지 인터페이스로 한정지어서 해당하는 자원에 접근하는 것을 말한다.
    • 컴포넌트 인터페이스에 일반성의 원칙(이미 만들어진 것에 대해 생각할 필요 없이, 단지 재사용하기만 하면 된다는 원칙)을 적용하여, 전체 시스템 아키텍처를 단순화하고 상호작용의 가시성을 향상시킬 수 있다.
  • HTTP 표준에만 따른다면 모든 플랫폼에 사용이 가능하다.
    • 요청하는 클라이언트가 어떤 플랫폼인지에 상관없이, 특정 언어나 기술에 종속받지 않는 특징을 의미한다.
    • 이로 인해 REST API는 느슨함 결함(loosely coupling) 형태를 갖게 된다.

REST의 대부분의 조건들은 HTTP API만 사용하더라도 만족시킬 수 있다.

단, Uniform Interface 조건을 만족시키는 것이 까다롭다. 따라서 REST API를 구분 짓는 가장 큰 핵심이라고 생각한다.

2. Uniform Interface

Uniform Interface 역시 아키텍처 스타일이므로 그에 따른 제약 조건들이 있다. 다음은 Uniform Interface에 대한 4가지 가이드이다.

Uniform Interface 가이드

1) identification of resources (자원의 식별)

  • URI라는 고유 식별자를 사용하여 자원을 식별한다.
    • 탈잉 vod 수업 예시 vod 수업을 나타내는 자원(/vod/view/)을 만들고, 각 수업에 번호를 부여해 개별 수업에 접근할 수 있도록 한다.
      /vod/view/42373

2) manipulation of resources through representations(표현을 통한 자원의 조작)

  • 클라이언트가 자원을 요청할 때 서버는 자원 자체가 아닌 자원의 표현(representation)으로 응답한다.
  • 자원의 표현(representation)은 현재 자원의 상태를 반영하는 정보이며, 이는 클라이언트가 이해하고 조작할 수 있는 형식이다.
    • 대표적인 예로 HTML, JSON, XML 등이 있다.
  • 클라이언트가 특정 표현 형식을 요청하고 서버가 요청에 맞는 표현을 제공하는 것을 컨텐츠 협상(content negotiation)이라고 한다.
    • plain text로 요청
      GET /orders/12345
      Accept: text/plain
    • JSON으로 요청
      GET /orders/12345
      Accept: application/json

3) self-descriptive messages(자기서술적 메시지)

  • 말 그대로 메시지 스스로가 자신에 대해 설명할 수 있어야 한다는 것.
    • 클라이언트가 자원을 가지고 어떤 작업을 할 때 필요한 모든 데이터가 응답되어야 한다.
    • API 문서가 REST API 응답 본문에 존재해야 한다는 것.
  • 확장 가능한 커뮤니케이션을 가능하게 한다.
    • 서버나 클라이언트가 변경되더라도 메시지는 언제나 self-descriptive하므로 API 문서를 통해 언제나 해석이 가능하다.

4) HATEOAS(hypermedia as the engine of application state, 하이퍼링크를 통한 상태의 전이)

  • Hypermedia(링크)에 자기 자신에 대한 정보가 담겨야 한다.
  • Hypermedia(링크)를 통해서 애플리케이션의 상태 전이가 가능해야 한다.
    • 상태 전이란 해당 URI에서 사용자가 다음 행동으로 취할 수 있는 것들을 말한다.
      : 게시글을 조회 했다면 여기서의 상태 저이는 다음과 같다.
      	- 다음 게시물 조회
      	- 게시물 내 피드에 저장
      	- 댓글 달기
    • 이러한 상태 전이가 가능한 것들을 응답 본문에 넣어줘야 한다.
    • 이러한 모든 동작을 URI를 통해 도적으로 알려준다는 의미이다.

예시를 통해 HATEOAS를 통한 이점을 살펴보자

  • 예: 상품 목록 API 응답
    {
      "links": [
        {
          "rel": "detail",
          "href": "http://server/api/items/12345"
        },
        {
          "rel": "order",
          "method": "post",
          "href": "http://server/api/items/order"
        }
      ]
    }
  1. 애플리케이션의 상태가 어떻게 변화할지 예측할 수 있다.
    • 상품목록에서 detailorder 로 이동할 수 있음을 알 수 있다.
  2. API와 사용자 간의 결합이 느슨해진다.
    • 클라이언트는 detail 을 응답 받기 위한 URL을 저장하지 않고 href 값을 얻어와 호출함으로써 API 엔드포인트의 변화로부터 자유로워진다.
  3. 링크 정보를 동적으로 바꿀 수 있다.
    • 클라이언트는 rel 의 이름으로 URI를 사용하기 때문에 URI 수정이 발생하더라도 클라이언트 사읻는 수정이 필요하지 않다.

이 중 self-descriptive message와 HATEOAS의 경우 실제 코드에서 어떤 방식으로 해당 조건을 충족시킬 수 있는지 살펴보자

Self-descriptive message 예시

요청 예시

// self-descriptive X
GET/HTTP/1,1

// self-descriptive O
GET/HTTP/1.1
Host: www.example.orgz // 목적지 추가

응답 예시 1

// self-descriptive X
HTTP/1.1 200 OK

[{ "op": "remove", "path": "/a/b/c" }]

// self-descriptive X
HTTP/1.1 200 OK
Content-Type: application/json

[{ "op": "remove", "path": "/a/b/c" }]

// self-descriptive O
HTTP/1.1 200 OK
**Content-Type: application/json-patch+json** // 어떤 문법과 명세로 작성되었는지 추가

[{ "op": "remove", "path": "/a/b/c" }]
💡 json-patch란? [http://jsonpatch.com/](http://jsonpatch.com/)

응답 예시 2

HTTP/1.1 200 OK
Content-Type: application/vnd.todos+json

[
	{"id: 1, "title": "회사 가기"},
	{"id: 2, "title": "집에 가기"}
]
  1. 문서를 작성하고 JSON의 idtitle 이 무엇을 의미하는지 정의한다.
  2. IANA에 미디어 타입을 등록하고 미디어 타입 문서를 명세로 등록한다.
  • 단점은 매번 media type을 정의해야 한다.

응답 예시 3

GET /todos HTTP/1.1
HOST: example.org

HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://example.org/docs/todos>; rel="profile"

[
	{
		"id: 1, 
		"title": "회사 가기"
	},
	{
		"id: 2, 
		"title": "집에 가기"
	},
]
  1. idtitle 이 뭔지 의미를 정의한 명세를 작성한다.
  2. Link 헤더에 profile relation으로 해당 명세를 링크한다.
  3. 이제 메시지를 보는 사람은 명세를 찾아갈 수 있으므로 이 문서의 의미를 온전히 해석할 수 있다.

HATEOAS 예시

HATEOAS를 달성하기 위해 우리는 HAL JSON을 이용할 수 있다.

HAL

Hypertext Application Language으로 JSON, XML 코드 내의 외부 리소스에 대한 링크를 추가하기 위한 특별한 데이터 타입이다.

HAL은 두 개의 타입을 갖으며, HAL 타입을 이용하면 쉽게 HATEOAS를 달성할 수 있다.

  1. application/hal+json
  2. application/hal+xml

3. REST API 예시

다음의 예시가 REST API 조건들에 부합하는지 살펴보고, 부합하기 위해서는 어떻게 변경되어야 할지 예시를 통해 비교해보자

응답 코드 예시 - 1(HAL JSON)

HATEOAS 달성 실패

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

{
	"Content": "너무 감사합니다. 코로나때문에 비대면으로 전환되서 너무 아쉬웠어요. ㅠㅠ"
	"Id": "956065",
	"Name": "aaa",
	"likes: "0",
	...
}

HAL을 이용하여 HATEOAS 달성 성공

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

{
	"data": { // HAL JSON의 리소스 필드
		"Content": "너무 감사합니다. 코로나때문에 비대면으로 전환되서 너무 아쉬웠어요. ㅠㅠ"
		"Id": "956065",
		"Name": "aaa",
		"likes: "0",
		... 
	},
	"_links": { // HAL JSON의 링크 필드
		"self": { // 현재 api 주소
			"href": "주소?page=2"
		},
		"prev": { // 이전 리뷰 페이지 api 주소
			"href": "주소?page=1"
		},
		"next": { // 다음 리뷰 페이지 api 주소
			"href": "주소?page=3"
		},
	},
}

응답 코드 예시 - 2 (HTML)

  • HTML의 a tag를 통한 Hyperlink

  • Link 헤더를 사용해 previous와 next로의 링크를 제공

4. RESTful한지 판별해보기

사실 REST 제약 조건을 만족하면서, 특히 Uniform Interface의 조건들을 모두 충족하면서 API를 작성하는 것은 굉장히 까다로우며 많은 비용이 요구된다. 또한 실제로 REST의 제약 조건을 다 지키는 서비스 역시 드물다.

API에 대한 문법과 명세 제공

API에 대한 문법과 명세를 설명하고 있는 문서를 제공함으로써 uniform interface의 self descriptive를 만족시킬 수 있다. 여기에 custom media type이나 profile link relation 등을 추가하여 해당 링크를 통해 바로 api 문서에 접근할 수 있도록 수정한다면 보다 완벽하게 조건을 충족시킬 수 있다.

적절한 HTTP 메서드 사용하기

URI에는 리소스만 명시하고 CRUD에 적절한 HTTP 메서드를 사용함으로써 리소스와 행위를 분리시킨다.

review를 리소스로 예시를 들면 API의 작성 예시는 다음과 같다.

올바른 예시

  • GET: review/{reviewId}
  • POST: review
  • DELETE: review/{reviewId}
  • PUT/PATCH: review/{reviewId}

잘못된 예시

  • 조회: review/getReview
  • 등록: review/regReview
  • 삭제: review/deleteReview

잘못된 예시를 보면 URI에 리소스만 있는 것이 아니라 행위까지 명시돼있다.

이런 경우는 보통 HTTP 메서드가 적절하게 사용되지 못하고 있기 때문인데, 주로 GET과 POST 두 가지만 사용하는 경우 혹은 둘 중 무엇을 사용해도 상관 없는 경우이다.

꼭 모든 REST 조건을 만족시켜야 하는가?

self descriptive와 HATEOAS를 달성하기 힘든 경우가 대다수이며 지키려면 구조 설계부터 작업이 진행되어야 하는 경우가 많다. 따라서 모든 REST 제약 조건을 만족할 수 없는 경우, 최소한 적절한 HTTP Method를 사용하여 API를 설계하도록 하자

마치며

세미나를 준비하기 전에는 REST API를 대략적으로만 알고 있었다. 사실 사내의 많은 개발자들 앞에서 세미나를 준비하면서 약간 부담스러운 것도 있었다. 하지만 동기님(달코님)과 힘을 합쳐 열심히 준비하여 REST API에 보다 자세히 알 수 있게 됐다. 무엇보다 이전까지 내가 알고 있던 REST API는 REST API가 아니었으며 HTTP API에 더 가깝다라는 것을 알게 됐다. 이번 세미나를 통해서 학습에 대한 깊이의 중요성에 대해서도 깨달을 수 있었다.

Reference

Day1, 2-2. 그런 REST API로 괜찮은가

그런 REST API로 괜찮은가

RESTful API 이란

REST에 대하여

What RESTful actually means

[Spring] RESTful의 HATEOAS 관련 내용 정리 - RESTful 하려면 어떤 조건들이 필요할까?

HATEOAS를 모르면 당신이 알고 있는 REST API는 REST API가 아니라고 장담할게요.

REST (Representational State Transfer)

profile
서글픈 너의 눈길은 나의 가슴을 아리게 한다

0개의 댓글