카테고리 없음

[Restful 아키텍쳐] RMM의 의미와 각 단계의 설계적 의의

sian han 2025. 8. 8. 19:07

아래 글은 Martin Fowler 의 Richardson Maturity Model - steps toward the glory of REST 를 읽고 정리한 내용입니다. 

 

 

※ RMM

  • Richardson Maturity Model (RMM)
  • 미국 개발자 Leonard Richardson이 제안한 RESTful API 성숙도 측정 모델
  • RMM 은 API가 REST 원칙을 얼마나 잘 지키고 있는지를 Level 0 → Level 3 네 단계로 나누어 설명한다. 

 

RMM 단계

  • Level 0 – The Swamp of POX : HTTP를 단순 데이터 운반(RPC) 용도로만 사용. 모든 요청이 하나의 엔드포인트로 감.
    • ㄴ RPC : Remote Procedure Call, 원격 프로시저 호출 / 네트워크에 있는 다른 컴퓨터(또는 서버)에 있는 함수나 메서드를 로컬 함수처럼 호출할 수 있게 하는 통신 방식
  • Level 1 – Resources : 리소스(자원) 개념 도입. 각 엔터티가 고유 URI로 식별됨.
  • Level 2 – HTTP Verbs : GET, POST, PUT, DELETE 등 HTTP 메서드와 상태 코드(200, 201, 404, 409 등)를 의미에 맞게 사용.
  • Level 3 – Hypermedia Controls (HATEOAS) : 응답에 다음 가능한 행동의 링크 포함(서버가 클라이언트에 경로 안내).

 

RESTful API의 성숙도를 Level 0 ~ Level 3 단계로 나눠 이해하기

 

▶ Level 0

  • RMM 의 출발점은 HTTP를 원격 상호작용의 전송 시스템으로 사용하는 것이다.
  • Level 0에서는 웹의 기본 원리들(URI 구조, HTTP method, HTTP status, 캐싱, HATEOAS, Stateless 등) 을 전혀 사용하지 않고 HTTP를 단순히 “원격 함수 호출을 위한 데이터 운반 도구”로만 쓴다. 

 

예시 상황 : 병원에 가야한다. 예약 가능한 시간을 확인하고(조회) 예약을 진행한다(요청)

 

 

▷ 예약 가능한 시간 조회하기

request

POST /appointmentService HTTP/1.1
[various other headers]

<openSlotRequest date = "2010-01-04" doctor = "mjones"/>

 

response

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot start = "1400" end = "1450">
    <doctor id = "mjones"/>
  </slot>
  <slot start = "1600" end = "1650">
    <doctor id = "mjones"/>
  </slot>
</openSlotList>

 

조회를 완료했으니 예약을 요청한다. 

 

▷ 예약 요청하기

request

POST /appointmentService HTTP/1.1
[various headers]

<appointmentRequest>
  <slot doctor="mjones" start="1400" end="1450"/>
  <patient id="jsmith"/>
</appointmentRequest>

 

response 1 : 예약 성공 시

HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot doctor="mjones" start="1400" end="1450"/>
  <patient id="jsmith"/>
</appointment>

 

response 2 : 예약이 실패한 경우( 다른 사람이 먼저 예약을 진행함 )

HTTP/1.1 200 OK
[various headers]

<appointmentRequestFailure>
  <slot doctor="mjones" start="1400" end="1450"/>
  <patient id="jsmith"/>
  <reason>Slot not available</reason>
</appointmentRequestFailure>

 

  • Level 0 은 위와 같이 단순하게 XML을 주고받는 방식이기 때문에 매우 간단하다. 
  • 병원 예약 시스템이 /appointmentService 같은 하나의 엔드포인트만 제공하여, 모든 요청을 이 한 엔드포인트로 보내어 처리하는 것을 볼 수 있다. HTTP는 단순히 데이터 운반 수단으로만 쓰이고, 리소스를 구분하거나 HTTP 메서드의 의미를 지키지 않고있다. 

 

 

▶ Level 1

  • 위 표에 작성된 RMM 모델에서의 Glory of Rest 에 도달하기 위한 첫번째 단계는 리소스를 도입하는 것이다.
    • 리소스를 도입하는 것 ? : 모든 요청을 하나의 엔드포인트로 보내는 것이 아닌, 리소스별 고유 URI를 부여하고, 해당 리소스에 직접 요청하게 되는 것이다. 

 

▷ 예약 가능한 시간 조회하기

request 

POST /doctors/mjones HTTP/1.1
[various headers]

<openSlotRequest date="2010-01-04"/>

 

 

response

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id="1234" doctor="mjones" start="1400" end="1450"/>
  <slot id="5678" doctor="mjones" start="1600" end="1650"/>
</openSlotList>

 

  • Level 0 과 다르게 각 slot 은 고유한 id 를 갖게 되었다. 

 

 

예약 요청하기 

  • 특정 slot 리소스에 요청을 하는 행위이다.

request

POST /slots/1234 HTTP/1.1
[various headers]

<appointmentRequest>
  <patient id="jsmith"/>
</appointmentRequest>

 

response

HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot id="1234" doctor="mjones" start="1400" end="1450"/>
  <patient id="jsmith"/>
</appointment>

 

  • Level 0 → Level 1
  • 더 이상 모든 요청을 하나의 엔드포인트로 보내지 않는다.
  • 각 자원(리소스)에 고유한 URI를 부여하여, /doctors/mjones는 특정 의사, /slots/1234는 특정 시간 슬롯,
    /slots/1234/appointment는 해당 슬롯의 예약을 나타내고 있는 것을 확인할 수 있다. 
  • 리소스별로 고유한 URI 를 부여하면, 클라이언트가 필요한 자원에 직접 요청할 수 있다. 

 

▶ Level 2 - HTTP Verbs

Level 0 과 Level 1은 둘 다 HTTP를 통해 상호작용을 tunneling 하는 메커니즘 을 사용한다는 점에서 큰 차이가 없다고 볼 수 있다. 

ㄴ HTTP를 통해 상호작용을 tunneling 하는 메커니즘 : HTTP를 그 자체의 웹 메커니즘(리소스, 메서드 의미, 상태 코드, 캐싱 등)을 활용하지 않고, 단순히 데이터 전송 통로(파이프)로만 사용하는 방식

 

Level 2에서는 HTTP를 단순 데이터 전달 통로가 아니라, HTTP 메서드와 상태 코드를 본래 의미에 맞게 사용하는 단계로 발전한다. 

 

▷ 예약 가능한 시간 조회하기

GET 요청을 사용한다. 

request

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

 

response

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

 

  • 위와 같이 GET 요청을 사용하는 것은 중요하다. 
  • GET 은 어떤 것도 상태에 중요한 변화를 일으키지 않는다는 의미이기에 HTTP는  GET 은 안전한 작업으로 정의한다.
  • GET 요청은 서버의 상태를 변경하지 않으므로 언제든 반복 호출이 가능하고, HTTP가 제공하는 캐싱 기능을 활용할 수 있다 ( → 이 덕분에 웹 전반의 성능을 높일 수 있다

 

▷ 예약 요청하기 

예약과 같은 상태를 변경하는 작업에는 POST 나 PUT 같이 상태 변경 HTTP 메서드를 사용한다. 

request

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

 

POST와 PUT을 단순히 create/update로 매칭하는 것은 잘못된 이해이다. The choice between them is rather different to that. (글쓴이는 이에 대해 더 자세하게 설명하고 싶어했지만 내용이 많아 다른 글로 작성할 것을 예고함)

 

response 1

  • 예약 성공 시 
  • Level 2에서는 요청 방식이 Level 1과 같더라도, 서버 응답 방식에서 중요한 차이가 있다.
HTTP/1.1 201 Created
Location: slots/1234/appointment
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

 

  • HTTP 상태코드가 201 인 것을 확인할 수 있다. 이와 같이 예약이 성공하면 201 Created 상태 코드를 반환해 “새로운 리소스가 생성되었음”을 명확히 알린다. 
  • 또한 Location 헤더에 그 리소스의 URI를 함께 제공하여 클라이언트가 이후 해당 리소스를 조회할 수 있게 해준다. 

 

response 2

  • 예약 실패 : 다른 사람이 이미 예약 완료 함
HTTP/1.1 409 Conflict
[various headers]

<openSlotList>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

 

위 response 에서 중요한 점은 HTTP status code 를 사용하여 문제가 발생한 것을 알리고 있다는 것이다. 

다른 사용자가 이미 리소스를 변경한 경우이기에 409 Conflict 가 적합하다. 

 

이전 Level 에서는 이런 경우 200 응답 코드를 사용하고, 오류 응답을 포함하였으나, Level 2 에서는 오류 응답을 명시적으로 사용하고 있다. 

 

▶ Level 3 - Hypermedia Controls

  • 가장 중요 !
  • HATEOAS(Hypertext As The Engine Of Application State)
    • HATEOAS 는 응답 안에 다음 가능한 동작과 해당 리소스 URI를 포함해, 클라이언트가 서버가 제시한 링크를 따라가며 상태 전환을 하도록 하는 개념이다.
    • HATEOAS 의 목적은 “어디에 요청을 보내야 하는지”를 클라이언트가 미리 알 필요 없이, 서버가 제공한 하이퍼링크를 통해 알 수 있게 하는 것이다. 

 

▷ 예약 가능한 시간 조회하기

request

GET /doctors/mjones/slots?date=20100104&status=open

 

response

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/1234"/>
  </slot>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/5678"/>
  </slot>
</openSlotList>

 

각 slot 에는 예약 링크가 포함되어있는 것을 확인할 수 있다. 

hypermedia controls 의 목적1. 다음 동작으로 무엇을 할 수 있는지, 그리고 2. 그 작업을 수행하기 위해 조작해야 할 리소스의 URI를 알려주는 것이다. 

 

 

▷ 예약 요청하기 

request

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

 

 

response

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
  <link rel = "/linkrels/appointment/cancel"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/addTest"
        uri = "/slots/1234/appointment/tests"/>
  <link rel = "self"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/changeTime"
        uri = "/doctors/mjones/slots?date=20100104&status=open"/>
  <link rel = "/linkrels/appointment/updateContactInfo"
        uri = "/patients/jsmith/contactInfo"/>
  <link rel = "/linkrels/help"
        uri = "/help/appointment"/>
</appointment>

 

 

▶ Hypermedia Controls의 장점

  • URI 구조 변경에 강함
    • 클라이언트가 하드코딩된 URI 대신 링크를 사용하므로, 서버가 내부 URI 체계를 바꿔도 동작 유지.
  • 프로토콜 탐색 가능성
    • 응답 링크를 통해 개발자가 다음 가능한 동작을 추측·탐색 가능.
  • 기능 확장 용이성
    • 새로운 기능을 링크로 추가하면 클라이언트가 이를 발견하고 사용할 수 있음.
  • 링크 정보를 동적으로 바꿀 수 있다. (Versioning 할 필요 없이!)

 

Level 3 에서는 단순 데이터 응답이 아니라 다음 행동에 대한 안내까지 포함하는 자기 서술적 API를 제공하고있는 것이 핵심이다. 

 

RESTful API의 성숙도를 Level 0 ~ Level 3 단계로 나눠 이해해 보았다. RMM 의 유용한 점은 RESTful 사고의 기본 개념을 이해하는 데 좋은 단계별 방법을 제공한다는 것이다. (RMM 은 RESTful API 개념을 배우는 데 도움이 되는 도구)