1. 배경
REST는 2000년 Roy Fielding(로이 필딩)의 박사 논문에서 발표됐다. Roy Fielding은 기존 아키텍처가 웹 본래 설계의 장점을 충분히 활용하지 못하고 있다고 판단했고, 웹의 장점을 최대한 활용할 수 있는 네트워크 기반 아키텍처 스타일로 REST를 정리했다.
Roy Fielding은 HTTP 명세 작성에 참여한 웹 아키텍처의 주요 인물 중 한 사람이다.
2. 정의
REST는 ROA(Resource Oriented Architecture)를 따르는 웹서비스 아키텍처로, 가장 큰 특징 중 하나는 모든 대상을 자원(Resource)으로 표현한다는 점이다.
즉, HTTP URI로 대상 자원을 명시하고 HTTP Method로 해당 자원에 대한 행위를 지정하는 방식으로 동작한다.
HTTP URI + HTTP Method = 자원 + 행위
3. REST의 구성 요소
- 메서드: 자원에 대한 행위를 정의한다.
- 리소스: 자원을 정의한다. URI 예시는 http://sarc.io 와 같은 형식이다.
- 메시지: 자원에 대한 행위의 내용을 정의한다. 일반적으로 JSON 문서를 이용해 표현되는 데이터가 여기에 해당한다.
위의 구성 요소를 표현해보면 다음과 같다.
HTTP Post , /
{
"users":{
"name":"someName"
}
}
REST에서는 일반적으로 CRUD에 해당하는 HTTP 메서드를 사용한다. 대표적으로 POST, GET, PUT, DELETE가 있으며, 일부 수정에는 PATCH도 사용된다.
- URI의 리소스를 생성, 조회, 수정, 삭제하는 데 사용한다.
- GET/DELETE는 일반적으로 요청 Body를 사용하지 않고 Path Variable이나 Query String을 사용한다. POST/PUT은 요청 Body도 사용할 수 있다.
- 최근에는 PUT 대신 PATCH라는 메서드가 사용되기도 한다. PUT은 해당 자원 전체 교체, PATCH는 일부 변경이라는 의미에 가깝다. update 이벤트에서 PUT보다 의미적으로 더 적합하다는 평가도 있다.
- 일반적으로 GET, PUT, DELETE는 idempotent하다. 즉, 반복적으로 수행했을 때 서버의 최종 상태가 같다는 의미다. PATCH는 구현 방식에 따라 멱등성이 달라질 수 있다.
주의사항은 다음과 같다.
- POST로 새 리소스를 생성할 때는 보통 URI에 리소스 ID를 포함하지 않는다. ID는 서버가 생성하거나 요청 본문에 포함된 값으로 처리하는 경우가 많다.
- REST API 호출이 실패했을 때 트랜잭션 복구를 위해 다시 실행해야 하는 경우가 있다. 이때 idempotent하지 않은 메서드는 중복 실행으로 데이터가 달라질 수 있으므로 기존 값을 저장하거나 중복 요청을 방지하는 처리가 필요하다.
4. REST 특성
- Uniform Interface: HTTP 표준을 따른다면 어떤 기술이든 적용 가능하며, 여러 플랫폼에서 사용할 수 있는 느슨한 결합(Loosely Coupling) 형태의 구조를 지향한다.
- Stateless: 클라이언트의 컨텍스트를 서버 쪽에 유지하지 않는다. HTTP Session과 같은 컨텍스트 저장소에 상태 정보를 저장하지 않는 방식이다.
- Cacheable: 기존 웹 표준인 HTTP를 그대로 사용하기 때문에 웹에서 사용하는 인프라를 활용할 수 있다. 많은 트랜잭션이 조회성이기 때문에 HTTP 리소스를 캐싱하면 용량이나 성능 면에서 장점이 있다. Last-Modified나 ETag 등을 이용해 구현할 수 있으며, 캐시를 이용하면 네트워크 응답 시간을 단축할 수 있다.
- Self-Descriptiveness: REST API는 리소스와 메서드를 통해 어떤 자원에 어떤 행위를 하는지 비교적 쉽게 파악할 수 있다. 메시지 포맷으로 JSON을 사용하면 직관적으로 이해하기 쉬운 구조를 만들 수 있다.
- 클라이언트-서버 구조: REST 서버는 API 제공, 비즈니스 로직 처리, 저장을 책임지고 클라이언트는 사용자 인증이나 컨텍스트(세션, 로그인 정보 등)를 관리한다. 클라이언트와 서버에서 개발해야 할 내용이 명확히 구분되고 의존성이 줄어든다.
- Layered System: 클라이언트 입장에서는 REST API 서버만 호출하지만, 서버는 다중 계층으로 구성될 수 있다. 순수 비즈니스 로직을 수행하는 API 서버 앞단에 사용자 인증, 암호화, 로드 밸런싱 등을 담당하는 계층을 추가해 구조적 유연성을 둘 수 있다. 마이크로서비스 아키텍처의 API Gateway 또는 간단한 기능의 경우 HAProxy나 Apache와 같은 Reverse Proxy를 이용해 구현하는 경우가 많다.
5. REST의 문제점
- 사용할 수 있는 HTTP 메서드가 제한적이기 때문에 send email이나 log write와 같은 행위를 HTTP 메서드만으로 표현하기가 모호해질 수 있다. 기존 프로그래밍 스타일이 function이나 method 중심의 행위 기반 접근이었다면, REST가 지향하는 자원 기반 접근과 맞지 않는 경우가 생긴다. 반면 DBMS처럼 CRUD를 가진 자원에는 비교적 적절하게 적용된다. 이런 이유로 REST를 단순한 규격이 아니라 아키텍처 스타일이라고 정의한다.
- REST 기반 아키텍처를 설계할 때 가장 어려운 것 중 하나는 URI를 어떻게 정의할 것인가이다. URI와 Method만으로도 의미를 쉽게 파악할 수 있어야 하기 때문이다.
- REST 자체가 엄격한 구현 표준을 제공하는 것은 아니기 때문에 관리가 어렵다는 문제점이 있다. 웹서비스의 복잡성과 기존 표준의 난이도 때문에 REST가 부각되었지만, REST에는 Enterprise 수준의 단일 표준이 있는 것이 아니라 사실상 널리 쓰이는 관례, 즉 de facto 표준에 가까운 방식들이 존재한다.
- 실제로 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 자체는 노출되지 않는 장점이 있다. 다만 토큰도 인증 수단이므로 HTTPS 사용, 만료 시간 설정, 재발급 정책 등을 함께 고려해야 한다.
6.3. HTTP Basic Auth
Base64로 인코딩된 ID/PW 정보를 HTTP 헤더에 넣어 인증한다. 반드시 HTTPS를 사용해야 한다. 그렇지 않으면 Base64 디코딩을 통해 ID/PW가 노출될 수 있다.
6.4. Digest Access Authentication
HTTP Basic Auth의 단점을 극복하기 위해 나온 방식으로, 서버에서 난수를 생성한 뒤 ID/PW 정보를 해시화한다. 생성된 난수는 서버와 클라이언트가 모두 알고 있다.
난수를 통해 해시한다고 해도 MD5 등 해시 방식에 따라 보안 수준은 달라진다. 보안 수준이 낮은 해시 방식을 사용할 때는 HTTPS 사용이 권장된다.
6.5. 제3자 인증
OAuth 2.0이 대표적이다. 사용자가 서비스에 직접 ID/PW를 전달하지 않고, 인증 서버를 통해 발급받은 토큰으로 API 접근 권한을 위임하는 방식이다.
7. REST API 테스트
- Postman: API 테스트에 적합하다.
- Katalon: 테스트 자동화에 적합하고 API, 웹, 모바일 테스트가 가능하다.
- SoapUI: SOAP 및 REST를 위한 자동화 툴이다.
간단한 테스트에서는 요청 Method, URL, Header, Body를 먼저 확인하고, 응답 코드와 응답 Body가 기대한 값인지 검증하면 된다. 예를 들어 조회 API라면 200 OK와 JSON 응답을 확인하고, 생성 API라면 201 Created 또는 서비스에서 정의한 성공 응답을 확인하는 식이다.
8. API 설계
마이크로서비스들은 각각 독립성을 가지고 동작한다. 따라서 서비스별로 독립된 버전을 가질 수 있다.
8.1. 버전 관리
하위 호환성과 독립된 버전을 가지는 것이 특징이다. 일반적으로 [서비스명/버전/리소스] 형태로 구성할 수 있다.
예를 들면 /v1, /v2 등이다.
버전은 URI에 포함하는 방식 외에도 헤더를 이용하는 방식이 있다. 어떤 방식을 선택하든 클라이언트가 사용하는 API 계약을 쉽게 파악할 수 있고, 기존 클라이언트가 갑자기 깨지지 않도록 관리하는 것이 중요하다.
8.2. REST API
REST API를 설계할 때는 자원을 정의하고(URI), 행위를 정의하고(Method), 행위의 내용을 정의한다(HTTP Message Payload).
예를 들어 주문 조회라면 자원은 /orders/{id}, 행위는 GET, 응답 메시지는 주문 정보를 담은 JSON이 될 수 있다.
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. Response
HTTP/1.1 200 OK
Date: ...
Content-Length: 956
Content-Type:
application/json
{
"id": 5500,
"total": 256.00,
"items": [ ... ]
}