REST는 “Representational State Transfer”의 약자로, 로이 필딩 (Roy Fielding)의 논문에서 처음 소개되었다. 기존 아키텍쳐가 웹의 본래 설계의 우수성을 많이 사용하지 못한다고 판단했기 때문에 나오게 되었다는 REST 는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하여 웹의 장점을 최대한 활용 할 수 있는 아키텍처 스타일이다.
REST를 사용한 API, REST API는 HTTP URI를 통해 웹에서 사용되는 데이터나 자원(Resource)을 명시하고 HTTP 프로토콜의 HTTP Method를 통해 해당 Resource에 대한 CRUD Operation을 적용하는 것을 의미한다. 아주 간단하게 말하자면 REST API는 HTTP Method를 통해 Resource를 처리하는 방식을 말한다.
간단한 비유를 통해 말해보자면 클라이언트와 서버 사이에 데이터와 리소스를 요청하고 요청에 따른 응답을 전달하기 위한 일종의 메뉴판이 있다. 이 메뉴판을 보고 클라이언트는 식사를 주문하듯 서버에 요청을 하고 이에 대한 응답을 메뉴판에 있는 사진이나 음식에 대한 설명처럼 다시 서버에서 클라이언트로 전송하게 된다.
이 과정에서 제일 중요한 것은 알아보기 쉽고 잘 작성된 메뉴판인데 이 역할을 API가 수행해야 하므로 모두가 잘 알아볼 수 있도록 작성하는 것이 필요하다.
REST API를 작성 할 때는 몇가지 지켜야 할 규칙들이 있다. 로이 필딩이 논문에서 제시한 REST 방법론을 보다 더 실용적으로 적용하기 위해 레오나르드 리차드슨(Leonard Richardson)은 REST API를 잘 적용하기 위한 4단계 모델, 리차드슨의 REST 성숙도 모델 (Richardson Maturity Model) 을 만들었다.
REST 성숙도 모델은 총 0~3단계로 나누어지며 로이 필딩은 이 모델의 모든 단계를 충족해야 REST API라고 부를 수 있다고 주장했다. 그러나 실제로 엄밀하게 3단계까지 지키기는 어렵기 때문에 2단계 까지만 적용해도 좋은 API디자인이라고 보며 이런 경우를 HTTP API라고도 부른다.
REST 성숙도 모델에 따르면 0단계에서는 단순히 HTTP 프로토콜을 사용하기만 해도 된다. 이는 REST API라고 할 순 없지만 REST API를 작성하기 위한 기본 단계라고 생각하면 된다.
REST 성숙도 모델 0단계 예시)
요청내용 예약 가능한 시간 확인
요청 POST / appointment HTTP/1.1
[헤더 생략]
{
"date": "2023-03-29",
"doctor": "허준"
}
응답 HTTP/1.1 200 OK
[헤더 생략]
{
"slots":[
{"doctor": "허준", "start": "09:00", "end": "12:00"},
{"doctor": "허준", "start": "14:00", "end": "16:00"}
]
}
요청내용 특정 시간에 예약
요청 POST / appointment HTTP/1.1
[헤더 생략]
{
"doctor": "허준",
"start": "14:00",
"end": "15:00",
"patient": "홍길동"
}
응답 HTTP/1.1 200 OK
[헤더 생략]
REST 성숙도 모델에 따르면 1단계에서는 개별 리소스(resource)와의 통신을 준수해야한다. 모든 자원은 개별 리소스에 맞는 엔드포인트(endpoint)를 사용해야하며 요청하고 받는 자원에 대한 정보를 응답으로 전달해야 한다는 것이 1단계의 핵심이다. 여기서 Endpoint란 API가 서버에서 자원(resource)에 접근할 수 있도록 하는 URL이다. 앞서 0단계의 예시에서는 /appointment
를 사용했지만 1단계에서는 요청하는 리소스가 무엇인지에 따라 각기 다른 엔드포인트로 구분해야한다.
REST 성숙도 모델 1단계 예시)
요청내용 예약 가능한 시간 확인
요청 POST /doctors/허준 HTTP/1.1
[헤더 생략]
{
"date": "2023-03-29",
}
응답 HTTP/1.1 200 OK
[헤더 생략]
{
"slots":[
{"id": 123, "doctor": "허준", "start": "09:00", "end": "12:00"},
{"id": 124,"doctor": "허준", "start": "14:00", "end": "16:00"}
]
}
요청내용 특정 시간에 예약
요청 POST /slots/123 HTTP/1.1
[헤더 생략]
{
"patient": "홍길동"
}
// 만약 예약이 접수되었다면
응답 HTTP/1.1 200 OK
[헤더 생략]
{
"appointment": {
"slot": {"id":123, "doctor":"허준", ...},
"patient": "홍길동"
}
}
// 만약 예약이 마감되었다면
응답 HTTP/1.1 409 Conflict
[헤더 생략]
{
"appointment": {
"slot": {"id":123, "doctor":"허준", ...},
"patient": "홍길동"
"reason": "해당 시간은 이미 예약되었습니다"
}
}
위의 예시에서 예약 가능한 시간 확인 이라는 요청의 응답으로 받게 되는 리소스는 허준이라는 의사의 예약 가능한 시간대이기 때문에 요청시 /doctors/허준
이라는 엔드포인트를 사용했다.
특정시간에 예약인 경우에는 예약이 접수 될시 slots라는 리소스의 123이라는 id를 가진 리소스가 특정환자의 이름으로 예약이 되어 변경되기 때문에 /slot/123
으로 실제 변경되는 리소스를 엔드 포인트로 사용했다.
이처럼 어떤 리소스를 변화 시키는지, 혹은 어떤 응답이 제공되는 지에 따라 각기 다른 엔드포인트를 사용하기에 적절한 엔드포인트를 작성하는 것이 중요하다. 엔드포인트 작성시에는 동사, HTTP메서드, 혹은 어떤 행위에 대한 단어 사용은 지양하고 리소스에 집중해 명사 형태의 단어로 작성하는 것이 바람직하다.
REST 성숙도 모델 2단계에서는 CRUD(Create, Read, Update, Delete)에 따른 적합한 HTTP 메서드를 사용하는 것에 중점을 두며 적합한 HTTP 메서드 사용을 위해서는 유의해야할 몇가지 규칙 있으므로 알아두길 바란다.
GET
메서드 같은 경우는 서버의 데이터를 변화시키지 않는 요청에 사용해야한다.POST
메서드는 요청마다 새로운 리소스를 생성하고 PUT
메서드는 요청마다 같은 리소스를 반환한다. 이러게 매 요청마다 같은 리소스를 반환하는 특징을 멱등(idempotent)하다고 하는데 이 멱등성을 가지는 메서드 PUT
과 그렇지 않은 메서드 POST
는 구분하여 사용해야한다.PUT
메서드와 PATCH
메서드도 구분하여 사용해야한다.PUT
은 교체,PATCH
는 수정의 용도로 쓰인다.이를 다 숙지했다면 전 단계들의 예시를 한번 살펴보자. 전 단계에선 모든 요청에 POST 메서드를 사용했지만 따져보자면 예약 가능한 시간 확인은 예약 가능한 시간을 조회(READ)하는 행위이고, 특정 시간에 예약은 해당 특정 시간에 예약은 생성(CREATE)하는 것이 된다.
때문에 조회(READ)를 위해서는 GET
메서드를 사용하여 요청을 보내고 이때 GET
메서드는 body
를 가지지 않기에 query parameter를 사용하여 필요한 리소스를 전달해야한다.
생성(CREATE)을 위해서는 POST
메서드를 사용하여 요청을 보내야 하며, POST
요청예 대한 응답이 어떻게 반환되는지가 중요하다. 이 경우 응답은 새롭게 생성된 리소스를 보내주기 때문에 응답 코드는 201 Created
로 명확하게 작성해야 하며, 관련 리소스를 클라이언트가 Location
헤더에 작성된 URI를 통해 확인할 수 있도록 하면 REST 성숙도 모델의 2단계를 충족했다고 할 수 있다.
REST 성숙도 모델 2단계 예시)
요청내용 예약 가능한 시간 확인
요청 GET /doctors/허준/slots?date=2023-03-29 HTTP/1.1
[헤더 생략]
응답 HTTP/1.1 200 OK
[헤더 생략]
{
"slots":[
{"id": 123, "doctor": "허준", "start": "09:00", "end": "12:00"},
{"id": 124,"doctor": "허준", "start": "14:00", "end": "16:00"}
]
}
요청내용 특정 시간에 예약
요청 POST /slots/123 HTTP/1.1
[헤더 생략]
{
"patient": "홍길동"
}
응답 HTTP/1.1 201 Created
Location: slots/123/appointment
[헤더 생략]
{
"appointment": {
"slot": {"id":123, "doctor":"허준", ...},
"patient": "홍길동"
}
}
앞서 말했듯 REST 성숙도 모델의 2단계까지 적용하면 대체적으로 잘 작성된 API 라고 할 수 있다. 물론 로이 필딩은 3단계까지 만족해야만 REST API라고 불러야하며 나머지는 HTTP API라고 불러한다고 주장하지만 현재 REST 성숙도 모델의 3단계 까지 적용한 REST API는 드물며 무조건적으로 3단계를 적용해야하는 것은 아닌 추세다.
마지막 단계인 REST 성숙도 모델 3단계에서는 HATEOAS(하이퍼미디어 컨트롤)를 적용해야한다.
Hypermedia As The Engine Of Application State의 약자로, 기본적인 아이디어는 하이퍼미디어를 애플리케이션의 상태를 관리하기 위한 매커니즘으로 사용한다는 것이다.
기존 REST API의 단점 중 하나는 API의 엔드포인트 URL이 정해지고 나면 이를 변경할 수가 어렵다는 점이다. API의 URL을 변경하면 이를 사용하는 모든 클라이언트가 함께 수정되어야 하기 때문에 API URL에 버전을 적용하거나 해서 기존의 것과는 다른 API를 지속적으로 추가해 나가야 했고, 이로 인해 API URL의 관리에 어려움을 겪게 된다.
또 다른 단점은 기본적으로 REST API는 자원의 상태를 고려하지 않는 디자인을 적용하고있어 해당 자원에 대해 특정 작업을 수행하기에 앞서 필요한 데이터를 수집하거나 해당 작업이 가능한지 여부를 판단하는 로직 역시 모두 클라이언트가 가져야 한다. 그러나 HATEOAS를 통해 REST 3단계를 구현하는 경우 이런 단점을 어느 정도 해소할 수 있다.
HATEOAS(하이퍼미디어 컨트롤)를 적용하는 방법은 응답에 리소스의 URI를 포함한 링크요소를 삽입하여 작성하는 것이다. 이때 응답에 들어가게 되는 링크 요소는 응답을 받은 다음에 할 수 있는 다양한 액션들을 위해 많은 하이퍼미디어 컨트롤을 포함하고 있다.
REST 성숙도 모델 3단계 예시)
요청내용 예약 가능한 시간 확인
요청 GET /doctors/허준/slots?date=2023-03-29 HTTP/1.1
[헤더 생략]
응답 HTTP/1.1 200 OK
[헤더 생략]
{
"slots":[
{"id": 123, "doctor": "허준", "start": "09:00", "end": "12:00"},
{"id": 124,"doctor": "허준", "start": "14:00", "end": "16:00"}
],
"links": {
"appointment": {
"href" : "httl://localhost:8080/slots/123",
"method" : "POST"
}
}
}
요청내용 특정 시간에 예약
요청 POST /slots/123 HTTP/1.1
[헤더 생략]
{
"patient": "홍길동"
}
응답 HTTP/1.1 201 Created
Location: slots/123/appointment
[헤더 생략]
{
"appointment": {
"slot": {"id":123, "doctor":"허준", ...},
"patient": "홍길동"
},
"links" : {
"self": {
"href": "http://localhost:8080/slots/123",
"method": "GET"
},
"cancel" : {
"href": "http://localhost:8080/slots/123/cancel",
"method": "DELETE"
}
}
}
위의 예시는 허준이라는 의사의 예약 가능 시간을 확인 한 후에는 그 시간대에 예약을 할 수 있는 링크를 삽입하거나, 특정 시간에 예약을 완료하고 나서는 그 예약을 다시 확인할 수 있도록 링크를 작성해 넣은 것이다. 이렇게 응답 내에 새로운 링크를 넣어 새로운 기능에 접근 가능하도록 하는 것이 3단계의 핵심 포인트이다.
만약 클라이언트 개발자들이 응답에 담겨있는 링크들을 눈여겨 본다면 이러한 링크들은 조금 더 쉽고 효율적으로 리소스와 기능에 접근할 수 있게 하는 요소가 될 수 있다.
대개 정부에서 제공하는 공공데이터는 시민 모두가 쉽게 접근 할 수 있어야하므로 Open API형태로 제공하고 있다. Open API는 Open 이라는 단어 그대로 누구에게나 열려있는 API이다. 그러나 무제한으로 이용할 수 있다는 의미는 아니다. API 마다 정해진 이용 수칙이 있고 그 이용 수칙에 따라 제한 사항(가격, 정보의 제한)등이 있을 수 있다.
그 예로 Open API를 간단하게 경험해 볼 수 있는 페이지인 Open Weather Map 이라는 웹 사이트에서 제공하는 날씨 API를 살펴보면 데이터 제공에대한 설명이 있다.
API 를 이용하기 위해서는 API Key가 필요하다. API Key는 서버의 문을 여는 열쇠로 서버를 운용하는 데에 운용이 발생하기 때문에 서버 입장에서 아무런 조건 없이 익명의 클라이언트에게 데이터를 제공할 의무가 없으므로 서버에서 조건에 맞는 클라이언트에게만 접근 할 수 있는 권한을 API Key형태로 제공하여 데이터를 요청할 때 API Key를 같이 전달해야 원하는 응답을 받을 수 있게 하는 것이다. 대표적인 조건으로는 로그인한 이용자가 있다.
예시를 코드로 직접 작성하셨네요 !! 잘 보고 갑니당!! :D