1. 배경

2000년 Roy Fielding(로이 필딩)의 논문에서 발표됐다. Roy Fielding이 기존의 아키텍처가 웹 본래 설계의 우수성을 많이 사용하지 못하고 있다고 판단하여 웹의 장점을 최대한 활용할 수 있는 네트워크 기반의 아키텍처로 설계한 것이다.

Roy Fielding는 웹의 창시자(HTTP) 중 한 사람이다.


2. 정의

REST는 ROA(Resource Oriented Architecture)를 따르는 웹서비스 아키텍처로 REST의 가장 큰 특징 중 하나가 모든 자원을 리소스로 표현한다는 것이다.

HTTP URI + HTTP Method => HTTP URI로 대상 자원을 명시하고 HTTP Method로 해당 자원에 대한 행위를 지정하는 방식으로 동작


3. REST의 구성요소

  •  메서드 : 자원에 대한 행위를 정의.
  •  리소스 : 자원을 정의. URI(예 - http://sarc.io 와 같은 형식)
  •  메시지 : 자원에 대한 행위의 내용을 정의. 일반적으로 json 문서를 이용해 표현되는 데이터

위의 구성요소를 표현해보면,

    HTTP Post , http://sarc.io/
    {
     "users":{
      "name":"someName"
     }
    }

REST에서는 CRUD에 해당하는 4가지 HTTP 메서드만 사용한다. (POST, GET, PUT, DELETE)

  •  URI의 리소스 생성, 조회, 수정, 삭제
  •  GET/DELETE는 Body를 사용하지 않아 PathVariable, QueryString 사용하며, POST/PUT은 요청 Body도 사용 가능
  •  최근에는 PUT 대신 PATCH라는 메서드가 사용되기도 함. PUT은 해당 자원 전체 교체, PATCH는 일부를 변경한다는 의미. update 이벤트에서 PUT보다 의미적으로 더 적합하다는 평가.
  •  POST 메서드를 제외하면 모두 idempotent 함. (반복적으로 수행했을 때 결과가 같다는 의미)

주의사항이다.

  •  POST에는 URI에 리소스 ID가 없다.
  •  REST API를 호출하다 실패했을 경우, 트랜잭션 복구를 위해 다시 실행해주어야 할 때가 있는데 idempotent하지 않은 메서드들은 기존 값을 저장해두었다가 다시 원복해주어야 한다!

4. REST 특성

  •   유니폼 인터페이스 : HTTP 표준에만 따른다면, 어떤 기술이든 적용 가능, 모든 플랫폼에서 사용가능한 Loosely coupling 형태의 구조.
  •   Stateless : 클라이언트의 컨텍스트를 서버쪽에 유지하지 않음. HTTP Session과 같은 컨텍스트 저장소에 상태 정보 저장하지 않음.
  •   Cacheable : 기존의 웹 표준(HTTP)을 그대로 사용하기 때문에 웹에서 사용하는 기존의 인프라를 그대로 활용 가능. 60~80%의 트랜잭션이 조회성이기 때문에 HTTP 리소스들을 캐싱하면 용량이나 성능면에서 많은 장점이 있다. Last-Modified 태그나 E-Tag를 이용해서 구현 가능하다. 캐시를 이용해서 네트워크 응답시간을 단축시킬 수 있다.
  •   Self-Descriptiveness : REST API 자체가 넘 쉬워서 API 메시지만 보고도 이해가능. 리소스와 메서드를 이용해서 어떤 메서드에 무슨 행위를 하는지 알 수 있음. 메시지 포맷도 json을 이용해서 직관적으로 이해가 가능한 구조를 가진다!
  •   클라이언트 - 서버 구조 : REST 서버는 API 제공 및 API를 이용해 비즈니스 로직 처리 및 저장을 책임지고 클라이언트는 사용자 인증이나 컨텍스트(세션, 로그인 정보 등)를 직접 관리하고 책임지는 구조임. 클라이언트와 서버에서 개발해야 할 내용들이 명확하게 구분되고, 의존성 줄어듬.
  •   Layered System : 클라이언트 입장에서는 REST API 서버만 호출하지만 서버는 다중 계층으로 구성될 수 있다. 순수 비즈니스 로직을 수행하는 API 서버와 그 앞단에 사용자 인증, 암호화, LB 등을 하는 계층을 추가해서 구조상의 유연성을 둘 수 있다. 마이크로 서비스 아키텍처의 API Gateway 또는 간단한 기능의 경우, HA Proxy나 Apache와 같은 Reverse Proxy를 이용해서 구현하는 경우가 많다.

5. REST의 문제점

  • 사용할 수 있는 메서드가 4개밖에 안되기 때문에 send email나 log write와 같은 메서드들은 HTTP 메서드로 표현하기가 모호해진다. 기존의 프로그래밍 스타일이 function이나 method 중심으로 한 행위 중심적인 접근이었기 때문에 REST가 지향하고 있는 자원 기반의 접근에 맞지 않는 것. 오히려 DBMS와 같이 CRUD를 가지고 있는 자원에 대해서는 적절하게 적용된다. 이런 이유로 REST를 아키텍처라고 정의했다는 것!
  • REST 기반의 아키텍처를 설계할 때 가장 어려운 것이 바로 URI를 어떻게 정의할 것인가!임. 이 URI와 Method 만으로도 쉽게 의미를 파악할 수 있어야 하니까!
  • 표준이 없고 관리가 어렵다는 문제점이 있다. 웹서비스의 복잡성과 표준의 난이도 때문에 REST가 부각되고 있는 것인데 이렇듯 REST는 Enterprise 수준의 표준은 없고, Defactor 표준만 있다고 한다. (단순히 많이 사용하고 암묵적으로 암암리에 생겨난 표준 비스무리 한 것이 있을 뿐이란다.)
    실제로 WEB 2.0의 대표 주자격인 Flickr.com도 REST의 특징을 살리지 못하면서 RPC 스타일로 디자인한 API를 HTTP + XML을 사용했다는 이유로 Hybrid REST라는 이름을 붙여서 REST아키텍처에 대한 혼란을 초래했다고 한다.

6. REST API 보안

6.1. Key 방식

가장 기본적인 방법으로 서비스 제공자가 발급한 Key를 통해 인증하는 방식이다. Key는 특정 사용자만 알고 있는 문자열로 구성되어 있으며, 사용자는 메시지 내에 Key를 포함하여 호출한다.

Key가 노출되면 전체적인 보안 이슈가 생기기 때문에 높은 보안 수준이 요구되는 곳에는 적합치 않다.

6.2. 토큰 방식

토큰을 통해 API 서비스 인증을 한다. ID/PW 인증을 통해 사용자에게 토큰이 발행되는데 이 토큰은 사용 가능한 시간이 유효하다. 

토큰을 사용하게 되면 토큰이 노출되더라도 ID/PW는 노출되지 않는 장점이 있다.

6.3. HTTP Basic Auth

Base 64 인코딩된 ID/PW 정보를 HTTP 헤더에 넣어 인증한다. 반드시 HTTPS를 사용해야 한다. 그렇지 않으면 Base 64 디코더를 통해 ID/PW가 노출된다.

6.4. Digest Access Authentication

HTTP Basic Auth의 단점을 극복하기 위해 나온 방식으로 서버에서 난수 생성 후 ID/PW 정보를 해시화한다. 생성된 난수는 서버와 클라이언트가 모두 알고 있다.

난수를 통해 해시한다고 해도 MD5 등 해시 방식에 따라 보안 수준은 달라진다. 보안 수준이 낮은 해시 방식을 사용할 때는 HTTPS 사용이 권장된다.

6.5. 제3자 인증

OAuth 2.0이 대표적이다.


7. REST API 테스트

  • POSTMAN : API 테스트에 적합하다.
  • Katalon : 테스트 자동화에 적합하고 API, 웹, 모바일 테스트가 가능하다.
  • SoapUI : SOAP 및 REST를 위한 자동화 툴이다.

8. API 설계

마이크로 서비스들은 각각 독립성을 가지고 동작한다. 따라서 독립된 버전을 가진다.

8.1. 버전 관리

하위 호환성과 독립된 버전을 가지는 것이 특징이다. [서비스명/버전/리소스] 형태로 구성된다.

예를 들면 /v1, /v2 등이다.

8.2. REST API

자원을 정의하고(URI), 행위를 정의하고(Method), 행위의 내용을 정의(HTTP Message Pay Load)


9. GET

9.1. Code

@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable("id") long id) {
  return orderService.findOrder(id); 
}

9.2. Request

GET /store/orders/5500
Host: shop.sarc.io
Accept: application/json

9.3. Request

HTTP/1.1 200 OK
Date: ...
Content-Length: 956
Content-Type:
application/json
{
   "id": 5500,
   "total": 256.00,
   "items": [ ... ]
}