사내 세미나로 REST API 에 대해서 준비하면서, HTTP API 와 REST API 가 다르다는 걸 깨달았습니다.
이전에 포스팅했던 REST API란, 이란 글은, HTTP API에 가까웠다고 생각하여, 다시한번 세미나 내용을 정리할 결 로이필딩이 말하는 진정한 REST API 에 대해서 정리해보고자 합니다!
[목차]
- REST API란
- REST 와 REST API
- REST 제약조건
- REST 와 HTTP
- REST 4번째 제약조건 "Uniform Interface"
- REST 사용하는 방법
- REST 해야하는 이유
- REST 탄생 배경
- 정리
REST API란???
사실 REST 와 REST API 의 개념을 잘 모르고, 이게 어떤건지 아예 모르신다면 이전에 쓴 REST API(의 탈을 쓴 HTTP API...ㅎ) 를 보고 오시는게 이해하기 수월할거라고 생각합니다.
간단하게 말해서, REST API란, HTTP를 잘 사용하기위해, REST 아키텍쳐 스타일을 모두 준수한 API 입니다.
보통 구글링을 하게되면,
REST API란, UR에 접근할 자원을 명시하고, 행위를 HTTP 메소드로 표현하여, 그 의도를 명확하게 표현하고 해당 자원에 대한 CRUD 오퍼레이션을 적용한 것
이라고 하는 글이 굉장히 굉장히 굉장히 많습니다.
이건, 명확하게 말하자면 REST API 가 아닙니다.
REST 와 REST API
저건 왜!? REST API 가 아닐까요?
그건, REST를 만든 로이필딩이 직접 저러한 형식은 REST API가 아니라고 이야기합니다.
내 논문 어디에도 CRUD에 대한 내용이 없다.
HTTP 메소드는 REST 가 아니라 웹의 아키텍쳐 스타일의 일부이다.
- it is okay to use POST (2009.03.20)
로이필딩이 말하는 REST API 의 본질은, 살짝 다릅니다.
그럼 로이필딩이 말하는
- REST 스럽지 않다??
- 그런건 REST 가 아니야..!!
- REST가 명확하지 않다.
는 무엇일까요!!?!?
REST 란
사실 REST 의 오피셜한 정의와 약자를 살표보면, 무슨말이지 이해가 잘 가지 않습니다..
- REST 약자 : REpresetational State Transfer
- 위키백과 : REST a way of providing interoperability between computer systems on the internet (컴퓨터 간 상호작용을 위한 것)
- REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식
💡이걸 단순하게 정리하자면, "REST란 HTTP 를 잘 사용하기 위한 아키텍쳐 스타일"입니다!
REST API 란
💡 REST API 는 간단하게 정의할 수 있습니다! "REST API란, REST 한 방식으로 데이터를 상호교환 하도록 설계된 API" 입니다!
REST 제약조건
✨로이필딩이 말하는 진정한 REST API 는 REST 의 제약조건을 모두 준수하는 것 입니다.
그럼 REST 제약 조건이란 무엇일까요?
로이필딩의 논문 Chapter 5.를 보면 REST 제약조건 6가지를 설명합니다.
(https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_)
로이필딩의 논문 내용을 간단하게만, 정리해보겠습니다
1. Client - Server
- Client-Server 제약조건이란, API를 통해 정보를 교환하는 주체는, 클라이언트와 서버 구조를 가져야합니다.
- 클라이언트와 서버를 분리함으로써, 서로 의존하지 않는 구조를 가져야합니다.
2. Stateless
- 무상태성 (서로의 상태를 기억하지 않느다.)
- 클라이언트에서 서버로의 요청에는 그 요청을 이해하는 데 필요한 모든 정보가 포함되어있어야 합니다.
- 클라이언트와 서버 모두, 통신하는 상대의 상태를 저장하고 있지 않습니다.
- 요청과 응답이 들어올 때 마다, 상대가 누구인지 파악할 수 있어야합니다.
3. Cache
- 캐시처리 가능 여부
- 요청에 대한 응답 내의 데이터에 캐시 가능여부가 명시되어 있어야함을 의미합니다.
- 📌 cache-control 헤더를 통해 명시 가능
4. Uniform Interface
- 전체 시스템을 파악할 수 있는 인터페이스를 제공해야합니다.
- 전체적인 시스템 아키텍처를 간단하고 잘 파악할 수 있도록 약속된 인터페이스를 제공해야합니다.
- 이 인터페이스를 일반화 시킴으로써, 클라이언트 - 서버간의 결합도를 낮출 수 있습니다.
- 😭 6 가지 제약조건 중 API에서 가장 지키기 힘든 제약조건 입니다. - 이 땜에 진정한 REST API 가 아니다라는 소리가 나옵니다..
5. Layered System
- 계층화 시스템
- 클라이언트는 서버에 직접 연결되었는지, 중간 서버를 통해 연결되었는지 알 수 없어야함을 의미합니다.
6. Code-On-Demand (Optional)
- Server 에서 보낸 코드를 Client에서 실행할 수 있어야함을 의미합니다. (ex - Java Script)
- 선택적인 조건 사항이라 논문에 언급되며
- 필요에 따라, 지켜도되고, 지키지않아도 REST에는 문제가 없다고 이야기합니다.
REST 제약조건 6가지를, 간단하게만 정리를 해보았는데 뭔가 익숙할 수 도있고, 1도 모르겠다 할 수 도있습니다.
여기서는, 각각의 제약조건을 자세하게 설명하기 보다는, REST 의 전체적인 흐름고 4번 제약조건에 대해서 이야기해보겠습니다.
❓뭔가 익숙한데..? 나 잘지키고 있는데..?
라고 생각되시는 분이 분명히 많을거라고 생각합니다!
보통 REST 제약조건은 아래의 조건을 만족하는 경우 4번을 제외한 5가지 제약조건이 대부분 지켜집니다.
1)서버-클라이언트 구조의 웹서비스 아키텍처에서
2)REST 가이드에 따라 자원 중점적 URL 패턴을 사용하고
3)HTTP 프로토콜을 사용한다면
-> 6개중 5개는 HTTP 통신 규약만 잘 따르면 거즘 대부분 지켜짐
HTTP 메세지
HTTP 프로토콜을 지키면 왜 REST의 5가지 제약조건을 만족하는지 조금 더 명확하게 보기위해, HTTP 메세지를 가져와보았습니다.
📌 HTTP 가지는 특성 ( HTTP 와 REST)
- 요청(Client) 와 응답(Server) 의 구조
- stateless(무상태성) 과 connetionless (비연결성)
- Header 의 cache-control를 통한 cache 가능 여부 명시
- Server 의 코드를 담을 수 있는 Body
- HTTP 프로토콜로 통신할 때, 요청과 응답을 보내는 주체는, 중간 레이어에 대해 신경쓰지 않음
요렇다보니까, HTTP 프로토콜을 사용하면 REST 의 많은 부분을 따르게 됩니다.
Uniform Interface 제약조건
✨ 그렇다면, uniform interface 제약조건이 뭐길래 많이들 안지키고 지키기 힘들어할까요
Uniform Interface 제약조건 이란
💡uniform inteface 란, "전체적인 시스템 아키텍처를 간단하고 잘 파악할 수 있도록 약속된 인터페이스를 제공해야한다." 는 제약조건이고 이런 인터페이스 일반화를 통해서 클라이언트 - 서버 구조의 결합도를 낮추기위한 목적의식을 가지고있는 제약조건입니다.
Unifrom Interface 제약조건에는 아래와 같은 4가지 하위 제약조건이 있습니다.
- dentification of resources
- Manipulation of resources through representations
- Self-descriptive messages
- HATEOAS - Hypermedia as the engine of application stat
이중에서도, 1번 2번 제약조건은 잘 지켜지는 편이라고합니다.
문제는 3번 4번 제약조건에 있습니다.
각각의 제약조건과, uniform interface 를 지키기 위한 방법에 대해 이야기해 보겠습니다
1. 자원에 대한 식별 - dentification of resources
uniform Interface의 첫 번째 제약조건, 자원에 대한 식별입니다.
➡️ 간단하게 말해서, 접근하고자 하는 자원을 명시하고, 그 자원을 식별할 수 있어야한다는 것 입니다.
로이필등은 자원을 객체로 바라보았다고 합니다.
객체란, 시간의 흐름에 따라 생성되고, 상태가 변화하고, 객체가 파괴되기도 합니다.
그렇기때문에, 이 자원(객체) 를 식별할 수 있는 변하지않는 식별자가 필요하다는 게 Dentification of Resource 입니다.
👏🏻 즉 URI 를 통해 자원을 식별할 수 있어야한다는 의미입니다.
순서대로 - 요구사항 | 잘못된 예시 | rest를 지킨 예시 입니다.
위와같은 요구사항에 대한 자원은 회원뿐입니다.
회원을 조회하고, 등록하고, 수정하고 삭제하는 명령어가 자원을 의미하지도 않으며, 자원을 식별할 수 있는 식별자 값이 아닙니다.
따라서 URI에 제어하고자 하는 자원(객체) 에 대해 명시하고, 그 객체를 식별할 수 있는 변하지 않는 식별자 {id} 를 URI 를 통해서 식별할 수 있도록 해줘야 합니다.
2. 표현을 통한 자원에 대한 조작 - Manipulation of resources through representations
2번째 유니폼 인터페이스 제약조건, 표현을 통한 자원에 대한 조작입니다.
이건 간단합니다.
💡표현을 통한 자원에 대한 조작이란, HTTP 메소드(표현) 을 통해 HTTP 메세지에 해당 리소스에 대해 어떤 조작을 하는지 명시하라는 것입니다.
여기서 중요한 점은, HTTP 메서드가 주가 아닌, 표현이 중심이라는 점입니다.
HTTP 메서드는 요청 행위에 대한 표현 방법 중 하나일 뿐입니다. (또다른 어떤 표현이 있는지는 잘모릅니다..> _ <)
이걸 강조하는 이유는, REST 의 의미를 해석하기 위함입니다!
REST - Representational State Transfer
즉, REST란 표현된 (자원의) 상태 전송 이라는 의미를 가지고 있습니다.
이걸 위에서는, 전혀 감을 잡지 못했지만 Uniform Inteface 제약조건을 알게 된 지금은 다릅니다
1번 2번을 다시 정리하자면
상태에 대한 표현 값을 응답 (잘못된 상태이든, 저장된 상태이든) -> post 로 새로운 것을 생성했다면 -> 생성되었다는 상태를 응답 201
3. 자기 서술적 메세지 - Self-descriptive messages
💡메세지는 스스로를 설명할 수 있어야한다.
Self-descriptive messages 란 메세지를 읽는 모든 주체들이, 메세지의 모든 요소는 메시지만 보고 그 의미를 파악할 수 있어야한다는 의미입니다.
HTTP/ 1.1 200 OK
Content-Type: application/json
[
{
"op": "remove",
"path": "/a/b/c"
}
]
우리는 이 코드를 보고, Body 에 담긴 "op" 와 "path" 의 의미를 알지 못합니다.
보통의 API 들은 이런 형식의 응답을 보내고, 이것 때문에 Roy Fielding 아저씨가 REST API 가 아니다. 라고 말하는 것입니다.
REST API라고 말하기 위해선, 이 자기 서술적 메세지 제약조건을 지켜야합니다.
즉, 저 응답 메세지만 보고, "op" 와 "path" 가 가지는 의미를 알 수 있어야합니다.
📌 Self-descriptive messages 를 지키기 위한 방법
self descriptive messages 를 지키기위한 방법은 일단은 2가지가 있습니다.
- 미디어 타입의 정의
- Link 헤더의 API 명세를 첨부
1. 미디어 타입 (Media Type) 을 정의하는 방법
결과부터 말하자면, 아래처럼 Content-Type 우리가 Json 으로 보낼꺼야~ 라는 명시를 하는데
이 부분 Content Type 에 입력하는 미디어타입을 직접 만들어서 메세지에 담는다는 의미입니다.
HTTP/ 1.1 200 OK
Content-Type: application/json - hmin + json
[
{
"op": "remove",
"path": "/a/b/c"
}
]
이렇게 하면, IANA - (Internet Assigned Numbers Authority)는 인터넷 할당 번호 관리기관 에 등록되어, 해당 타입에 대한 모든 명시를 할 수 있습니다.
https://www.iana.org/assignments/media-types/media-types.xhtml#application
여기 들어가서, 미디어 타입을 직접 등록할 수 있고, 등록되어있는 미디어타입을 검색할 수 있습니다.
하지만... 너무나 번거롭죠 (영어도 너무 많습니다.)
- 👉 그래서 누군가 이미 정의해둔 🔥 Hal Json 이라는 미디어 타입이 있습니다
HAL
- Hypertext Application Language 으로 JSON, XMl 코드 내의 외부 리소스에 대한 링크를 추가하기 위한 특별한 데이터 타입입니다
HAL은 두 개의 타입을 갖습니다.
- application/hal+json
- application/hal+xml
이 HAL 타입을 이용한다면 쉽게 HATEOAS를 달성할 수 있다고 합니다.
HAL 타입에서는 두 가지의 특징만 이용하면 됩니다.
🏠 리소스와 링크
- 리소스 : 일반적인 data 필드에 해당한다.
- 링크 : 하이퍼미디어로 보통 _self 필드가 링크 필드가 된다.
HAL json 더 자세한 내용과 사용밥 : https://blog.aliencube.org/ko/2015/08/16/applying-hal-to-rest-api/
2. Link Profile 을 사용하는 방법
이것도 결과부터 말하자면, 아래처럼 Link 헤더에 작성된 api 명세서 링크를 첨부하고, rel ="profile"을 명시하는 방법입니다.
위처럼, 데이터 타입을 커스텀 하는 것 보다는 간결한 방법이지만
이것도 API 명세서를 주기적으로 관리해주어야한다는 귀찮은 단점이 있습니다.
HTTP/ 1.1 200 OK
Link : <https://hmin.org/docs/todos> ; rel = “profile”
[
{
"op": "remove",
"path": "/a/b/c"
}
]
profile 사용 방법 (https://www.baeldung.com/spring-response-header)
- Link 헤더에 profile relation으로 API 명세서 링크를 첨부한다
- 메세지를 보는 사람은 명세를 찾아갈 수 있으므로, 문서를 완벽하게 해석할 수 있다.
- 단점은, 클라이언트가 Link 헤더와(RFC 5988) 와 profile(RFC 6906) 을 이해해야 한다.
- 또 Content negotiation을 할 수 없다는 단점이 존재한다..
결론
자기 서술적 메세지라는 의미의 Self descriptive messages 제약조건은, HTML 과 같은 웹에서는 지키기 어렵지 않습니다.
HTML 만 봤을때, 딱 이게 뭐고 어떤것인지 알 수 있기 때문입니다.
하지만 API 에서는 위와같은 방법처럼 지키기가 쉽지 않습니다.
4. HATEOAS - Hypermedia as the engine of application state
💡 하이퍼 미디어를 통한 앱 상태 변경 인터페이스를 제공해야한다.
HATEOAS 제약조건을 쉽게 말하면, 현재 상태에서 어떤 페이지로 이동가능한지 보여야한다는 것 입니다.
가장 좋은 예시가 HTML 의 <a> 태그 입니다.
- 현재 상태에서 NAVER 라는 로고를 누르면 홈화면으로 하이퍼링크되어 앱 상태가 변경되고
- 검색 버튼을 누르면, 검색 되어진 결과 페이지로 하이퍼링크되어 앱 상태가 변경됩니다.
이 처럼, 하이퍼미디어(링크)를 통한, 앱 상태의 변경이 되어야한다는 말인데
이게 API에서도 a 태그가 제공되면 좋겠지만,,, 적용하기가 쉽지 않습니다.
API 에서 HATEOAS를 지키기위한 방법이 2가지 있습니다. (더 있을수도 있지만요!)
- data 로 표현하기
- Header link 로 표현하기
1. Data 로 표현하기
- 이런식으로, 바디 안에, 현재상태에서 이동가능한 링크를 보내 보내는 방법입니다.
- 본문 바디에, data 에다 링크를 첨부한다.
- Json 하이퍼링크 양식을 사용한다.
- 하이퍼링크는 반드시 URI 여야 하나? (놉 링크를 표현할 수 있으면 아무거나 상관 없다)
보통 서비스에는 굉장히 많은 API가 존재하므로 굉장히 많은 수정을 해야합니다..
HTTP/ 1.1 201 OK
Content-Type: application/json - hmin + json
{
"id": 1,
"name": ”Hmin",
"createdAt": "2018-07-04 14:00:00"
"links": [
{
"rel": "self",
"href": "http://api.test.com/users/1",
"method": "GET"
},
{
"rel": "delete",
"href": "http://api.test.com/users/1",
"method": "DELETE"
},
{
"rel": "update",
"href": "http://api.test.com/users/1",
"method": "PATCH",
"more_info": "http://api.test.com/docs/user-update"
"body": {
"name": "{The value to be modified}"
}
…
],
}
2. Link Header 로 표현하기
아래처럼 헤더에 링크를 첨부하는 방법입니다.
HTTP/ 1.1 200 OK
Link : <https://hmin.org/docs/todos> ; rel = “profile”
</articles/1>; rel="previous",
</articles/3>; rel="next";
[
{
"op": "remove",
"path": "/a/b/c"
}
]
조금 더 깔금한 방법이지만, 이것 역시 번거럽고
Header Link에 접근하는 방법또한 일반적이지 않습니다.
✅ 이 방법을 사용하는 곳이, 10년 전 깃 허브 Documentary 페이지에서 페이지네이션을 다음과 같은 방법으로 사용하였습니다. 하지만 현재는,.,, 잘 모르겠네요!
3. Spring HATEOAS (2020)
찾다보니 spring HATEOAS 라는 걸 발견했습니다.
Spring 에서 HATEOAS를 지키기위해 만들어진 모듈같으니 한번 보시는 것도..!!
REST API 를 사용해야 하는 이유 – REST 역사
❓너무 복잡한데… 이거 꼭 해야 될까요??
굉장히 복잡하고, 지키기 어려운데 왜 로이필딩은 REST 제약조건을 전부 지켜야 REST API 라고 하는 걸까요
💡 독립적인 진화를 위해서
로이필딩이 말하길, REST 제약조건은 독립적인 진화를 위해 만들엇다고 합니다.
💡 REST 의 목적
- 서버와 클라이언트가 각각 독립적으로 진화한다.
- 서버의 기능이 변경되어도 클라이언트를 업데이트할 필요가 없다
- REST 를 만들게 된 계기 : "How do I improve HTTP without breaking the web"
- -> 로이필딩이 HTTP를 만지게되면, Web이 깨질거 같은데, 어떻게 해야할까
독립적인 진화란, "서버가 바뀌어도(API 가 추가되고, 변경되어도) 클라이언트가 변경할 필요가 없다" 는 말로, 즉 둘의 의존성을 끊어내는데 있습니다.
REST를 통해 독립적인 진화가 가능한 이유
1. Self - descriptive
- 확장 가능한 커뮤니케이션을 가능하게 함
- 서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 self-descriptive 하므로 언제나 해석이 가능하다.
- 메시지는 언제나 메시지만 가지고 해석이 가능(서버가 변하더라도)
2. HATEOAS
- 어플리케이션 상태전이의 late binding
- 어디서 어떻게 전이가 가능한지 미리 결정되지 않는다
- 어떤 상태로 전이가 완료되고 나서야 그 다음 전이될 수 있는 상태가 결정된다.
- 쉽게 말해서 : 링크는 동적으로 변경될 수 있다.
- 서버가 링크를 바꾼다고 해도, 클라이언트는, 바뀐 링크를 보고 따라가면 된다. (서버가 링크를 마음대로 바꿀 수 있다.?)
👉 자기서술적 메세지(Self Descriptive messages 만족하면)를 사용하면, 서버나 클라이언트가 변경되더라도 오고가는 메시지는 언제나 Self-descriptive 하므로 언제나 해석이 가능합니다.
👉 HATEOAS 를 사용하면 쉽게 말해 : 링크가 동적으로 변경될 수 있습니다…! ex) A tag
따라서, 독립적인 진화를 하기위해선 반드시 Uniform Interface를 만족해야만 합니다.
이런 부분 때문에, REST의 모든 제약조건 (특히 Uniform Interface)를 모두 준수해야만 "진정한 REST API" 다 라고 말합 수 있습니다.
REST 가 지켜지고 있는 사례
📌 REST 가 가장 잘지키고 있는 사례가 웹입니다.
- 웹사이트에서 웹페이지를 변경했다고 해도, 웹 브라우저를 업데이트할 필요없이 똑같이 웹 브라우저에 접속하면, 변경된 내용이 잘 나온다.
- 웹 브라우저가 업데이트 되어도, 웹 사이트가 영향을 받지 않는다.
- HTTP 명세가 변경되어도 웹은 잘 동작한다. (HTTP 4.0 / HTTP 5.0)
- HTML 명세가 변경되어도 웹은 잘 동작한다. ( HTML 5.0 / 5.1 등등)
- 물론 페이지가 깨질 수 있지만, 동작은 한다.
REST가 웹의 독립적 진화에 도움을 주었나 (YES)
- HTTP 에 지속적으로 영향을 줌
- Host 헤더 추가
- 길이 제한을 다루는 방법이 명서 (414URI Too Long 등)
- URI 에서 리소스의 정의가 추상적으로 변경됨 : "식별하고자 하는 무언가"
- HTTP/1.1 명세 최시판에서 REST에 대한 언급이 들어감
- HTTP 나 URI 를 만든 사람이 REST에 깊은 감명을 받았겠지만...!! 로이필딩도 같이 만들었음 ㅎㅎ
무조건 REST API 이여만 하는가
REST 는 웹의 독립적인 진화를 만들었고, 웹은 현재까지 독립적인 진화를 하고 있습니다. -> REST 는 성공적
REST API 는 REST 아키텍쳐 스타일을 따라야하지만, 오늘날의 대부분의 API 는 REST 를 모두 준수하지 않고있습니다.
그렇지만 로이필딩은, REST API 는 모든 REST 제약조건을 지켜야한다고 말하고 있습니다.
꼭 원격 API 가 REST API 여야하나
이건 또, 아니라고합니다...ㅎㅎ
REST 의 아버지 로이필디이 말하길, 애초에 REST 는 비효율적이라고 이야기합니다.
Roy - 시스템 전체를 통제할 수 있다고 생각하거나, 진화에 관심이 없다면 REST에 대해 따지느라 시간을 낭비하지 마라
- 시스템 통제가 가능하다 : 서버와 클라이언트, 시스템 전체를 내가 다 개발할 때 (모든 걸 내마음대로 통제할 수 있다.)
- 진화에 관심이 없다 : 오랜시간에 걸쳐 진화하는 시스템 ( REST를 설계하는데는 많은 비용이 든다.)
정리
- 오늘 대부분의 REST API 는 사실 진정한 REST를 따르고있다고 말할 수 없다
- REST 의 제약조건 중 self-descriptive 와 hateoas를 잘 만족하지 못하고 있기 때문이다
- REST 는 긴 시간에 걸쳐(수십년) 진화하는 웹 어플리케이션을 위한 것 이다.
- REST 를 따를 것인지는 API 를 설계하는 이들이 스스로 판단하여 결정해야한다.
- REST를 따르겠다면, Self-descriptive 와 HATEOAS를 만족시켜야한다.
- Self-descriptive 는 Custom media type 이나 profile link relation 등으로 만족시킬 수 있다.
- HATEOAS는 HTTP 헤더나 본문에 링크를 담아 만족시킬 수 있다.
- REST를 따르지 않겠다면, "REST를 만족하지 않는 REST API"를 뭐라고 부를지 결정하자~
끝! 감사합니다!
참고
- 로이필딩 논문 : https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1
- 그런 REST API 로 괜찮은가 : https://www.youtube.com/watch?v=RP_f5dMoHFc&t=2516s
- 우테코 10분 talk 정님 : https://www.youtube.com/watch?v=Nxi8Ur89Akw
'🔥 공대생은 성장 중 > 세미나' 카테고리의 다른 글
2024 항해 데브랩 후기 (0) | 2024.09.02 |
---|---|
[한빛앤 MSA 세미나] 모니터링 | 강동호 (0) | 2024.08.20 |
[한빛앤 MSA 세미나] 서비스 장애 잘 이해하고 대비하기 | 박순영 (0) | 2024.06.21 |
⚙️ Block, Non-Block, sync(동기), Async(비동기) 의 간단한 개념 (0) | 2022.10.31 |