이번에 프로그래머스 교육과정과 졸업논문을 진행하면서, 공통적으로 REST로 백엔드를 구현하게 되었습니다!
항상 SSR로 thymeleaf를 렌더링을 통해 진행했었는데, client의 상황을 고려해야 하는 REST 방식을 사용한다는게 미숙한 점도 많았고, 모르는 점도 많았습니다!!
그래도 나는 할 수 있다! 라는 마인드로 인프런 강의를 들으며 코드를 작성했지만,,,,REST를 REST스럽게 짜지 못했다는게 결론이 되었습니다...
단순하게, json 타입으로 데이터를 전달하면 REST API인 줄 알았지만, 실상은 달랐습니다. REST를 REST답게 사용할려면 꼭 지켜야 하는 규칙이 존재함을 알게 되었습니다.
그래서 이번에는 제가 현 상황에서 최대한 REST를 어떻게 REST답게 구현할려고 했는지에 대해 작성하려고 합니다!!
서두가 길었네요!! 바로 시작하겠습니다!!!
프로그래머스에서 진행하는 Spring-Baord 토이 프로젝트를 예로 현재 제가 어떻게 진행했고, 어떻게 수정할지에 대해 보여드리겠습니다!
아래의 코드는 회원가입을 진행했을 때 format 형태입니다.
http://localhost:8080/api/member
Content-Type : application/json
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null
}
위의 코드를 살펴보면, header에는 content-type이 존재하고 body에는 상태, 메세지, 데이터가 전달됨을 확인할 수 있습니다.
혹시 해당 스펙에서 어떤 점이 REST스럽지 못하다고 생각하실까요?
.
.
.
.
.
.
.
.
.
.
.
.
.
.
제 코드가 RESTFUL하지 않은 이유!!는 아래와 같습니다!
1) Non Cacheable
2) Non Uniform Interface
제가 기획한 api는 Rest를 모방한, REST스럽지 못하고 REST가 아닌 코드가 되었습니다.ㅜㅜㅜ
Representational State Transfer의 약자로서 인터넷에서 컴퓨터 시스템 간의 상호 운용성을 제공하는 방법
입니다.
로이 필딩이 HTTP 기획 작업에 참여하면서 웹 환경의 정합성과 상호 운영성 등을 개선하기 위해서 만든 모델이 HTTP Object Model
이라고 합니다. 그리고 이것이 REST의 시초가 되었다고 합니다!
REST는 SOAP과 다르게 프로토콜이 아닌 아키텍쳐입니다. 하나의 구성이자, 약속이므로 필딩이 트위터와 블로그에 계속 언급하다시피, REST의 제약조건을 따르지 않으면, REST가 될 수 없습니다
... 그저 단순한 api?
REST 공식 홈페이지에서 나와 있는 것 처럼, REST의 제약조건은 크게 6가지로 지정되어 있습니다.
URL로 지정된 리소스에 대한 조작을 통일하고 한정된 인터페이스로 수행하는 아키텍쳐 스타일, 이렇게 추상적으로 정의를 내린 이유는 추가적인 확장에 있어 느슨한 결합을 유지하기 위함입니다!
Uniform interface에는 4가지의 세부사항이 존재합니다!
Resouce가 단일한 URL에 매핑되어 리소스에 대한 관리가 진행될 때 Rest 제약조건을 갖추게 됩니다.
여기서 파생된 규칙은 URL를 명칭할때 동사가 아닌, 명사형으로 URL를 구분해야 한다고 합니다.
GET /member/delete/id/1 (X)
위의 코드처럼 동사형으로 URL를 가져가는 것이 아니라,
아래의 코드처럼 적절한 HTTP METHOD와 자원을 식별할 수 있는 명사형 명칭을 통해 URI를 구성해야 합니다!
DELETE /member/1
해당 규칙의 경우에는 일반적으로 HTTP Method를 사용할 때 지켜지는 것 같습니다. 대부분의 개발자분들이 해당 규칙은 지킨다고 생각합니다.
공식문서에 따르면, 표현은 해당 바이트를 설명하는 메타데이터어떤 목적을 가지고 만들어진 데이터
와 함께 바이트 시퀀스를 통해 클라이언트가 식별할 수 있는 리소스를 제공해야 함을 의미합니다.
이 메타데이터를 표현하는 미디어 유형(HTML, JSON 및 XML)이라고 합니다.
클라이언트는 서버가 보낸 리소스가 무엇인지 알 수 없습니다. 추론 또는 상황에 맞추어 판단할 뿐, 이것이 실제 무엇인지는 확신할 수 없게 됩니다.
그렇기 때문에 아래의 코드처럼 특정 리소스를 표현하는 방식을 header에 담아서 요청하고, 응답함으로서 해당 리소스가 어떠한 유형으로 제공되는지 알 수 있게 됩니다.
표현 방식의 유연성과 확실한 미디어 매체 정보를 알려줄 수 있게 됩니다.
Accept: application/json
or
Accept: text/plain
--> 해당 부분도 대다수의 사람들이 까먹지 않고, 구현하는 부분인 것 같네용
자기 설명 메시지??
말그대로 본인을 해석할 수 있는 정보를 본인이 갖고 있어야 함을 의미합니다!
아래의 코드는 앞서 보여드렸던 제 토이 프로젝트 코드입니다.
http://localhost:8080/api/member
Content-Type : application/json
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null
}
여기서 스스로에 대한 정보를 해석할 수 없는 부분은 status, message, data가 있습니다. client는 미디어 타입에 대해 추론할 수는 있지만, 실제로 정확하게 이 데이터가 무엇인지 알 수 없습니다.
그렇기 때문에 해당 정보를 클라이언트에게 알려주기 위해 미디어 타입 문서를 작성하거나, 혹은 프로파일을 통해 미디어 타입에 대한 정보를 제공해야 합니다.
아래의 두가지 버전은 self-descriptive를 만족시키는 방법입니다.
http://localhost:8080/api/member
Content-Type : application/donggeon+json
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null
}
http://localhost:8080/api/member
Content-Type : application/json
Link: <http//donggeon.github.io/docs/rest>; rel="profile"
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null
}
--> 이 부분에 대해서는 많은 개발자분들이 빠트리고 개발을 진행한다고 하시네요!! 저 또한 고려해보지 못한 부분입니다!
축약하여 헤티오스
HATEOAS
라고 합니다!
헤티오스는 애플리케이션의 상태를 하이퍼링크를 통해 전이되어야 함을 의미합니다.
<그런 Rest Api로 괜찮은가? 에서..>
위의 사진은 DEVIEW 2017에서 이응준님께서 강의 해주신 ppt의 일부분을 발췌한 것입니다.
헤티오스는 api를 호출하고 응답하게 되면, 응답시에 다음 로직에서 필요한 애플리케이션의 api 호출을 담아서 제공하는 규칙을 의미합니다.
짧게 다음 상태에 대한 변이 내용을 담아서 보내도록 response를 구성해야 됨을 의미합니다.
글 목록 보기를 통해 제공된 데이터에 다음 단계의 api를 담아서 보내게 됩니다. 그렇게 진행하게 된다면, 프론트에서 하드코딩으로 상태전이를 진행할 필요 없이 server에서 내려주는 응답만으로도 상태를 변경할 수 있게 됩니다.
아래의 코드는 상태 전이를 헤더에서 진행하는 방법입니다.
http://localhost:8080/api/member
Location: /api/member/login
Content-Type : application/donggeon+json
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null
}
아래의 코드는 상태 전이를 body에서 진행하는 방법입니다.
http://localhost:8080/api/member
Content-Type : application/donggeon+json
{
"status": 201,
"message": "MEMBER_SIGN_SUCCESS",
"data": null,
"link" : {
"login" : "/api/member/login"
}
}
server에서 모든 정보를 제공하기 때문에 어떤 Client가 오더라도 상호 운용이 가능하게 됩니다.
더불어 Server의 기능이 변경되거나 Client의 기능이 변경되더라도 상호 독립적으로 운영할 수 있기 때문에 보다 탄력적인 확장이 가능하게 됩니다!!
--> 헤티오스를 적용하는 강의나 혹은 개발 준비생은 극히 드문것 같습니다.ㅜㅜㅜ 이번에 저도 처음 알게 되었습니다!!
해당 제약 조건은 클라이언트와 서버가 서로에 대한 종속성 없이 별도로 발전할 수 있어야 함을 의미합니다. 클라이언트는 리소스 URI만 알아야 합니다. 그 이상의 것은 추후의 확장 혹은 이전 버전에 부정적인 영향을 미칠 수 있기 때문에 최소 수준으로 간단함을 유지해야 하는 제약조건입니다.
결론적으로 어떤 클라이언트 프로그램이든, 어떤 서버 프로그램이든 동작해야 함을 의미합니다.
클라이언트와 서버간에 상호 작용을 진행할 때 각각의 상태를 비저장으로 유지하는 것을 의미합니다. 서버는 클라이언트가 만든 최신 HTTP 요청에 대해 아무 것도 저장하지 않고 무조건 모든 요청을 새 것으로 처리합니다.
대신에 클라이언트는 사용자를 위한 상태 저장을 진행할 수 있는 응용 프로그램이어야 하며, 클라이언트는 애플리케이션의 상태를 관리할 책임을 갖게 됩니다.
그렇기에 서버는 요청에 대한 응답만 할 뿐, 응답 중 발생하는 상태에 대한 정보를 관리하지 않고, 클라이언트만이 관리하게 됩니다.
캐싱은 부하가 감소했기 때문에 클라이언트 측의 성능 향상과 서버의 확장성 범위를 향상시킬 수 있습니다.
그렇기 때문에 REST에서는 캐싱이 적용 가능한 경우, 리소스에 대한 캐싱을 적용해야 하고, 리소스가 캐싱이 가능하다고 알려야 합니다.
캐싱을 통해 클라이언트와 서버간의 확장성과 성능을 증대시킬 수 있습니다.
--> 해당 부분은 아직 학습이 미흡하여 진행하지 못했습니다.ㅜㅜ 추후에 서버에 캐싱을 하여 데이터를 제공하는 예제를 올리겠습니다!
Rest를 사용하게 되면 계층화 시스템 아키텍처를 도입할 수 있습니다!!
데이터를 어떤 서버에서 저장할지, API를 어떤 서버에서 제공할지, 인증과 인가를 어떤 서버에서 진행할지 클라이언트를 확인할 필요도, 알려고 할 필요도 없게 됩니다!
단순하게 제공된 리소스를 받기만 하면 되기 때문입니당!!
그렇기 때문에 REST를 사용하게 되면 서버를 계층화시키고 분산시킬 수 있는 장점이 있습니다!
클라이언트가 리소스에 대한 표현을 응답으로 받고 처리하는데, 어떻게 처리해야 하는지에 대해 서버가 코드를 제공하는 것을 의미합니다.
예를 들어 클라이언트가 특정 기능을 요청하면, 그 기능을 제공하는 자바스크립트를 클라이언트에게 응답할 수 있습니다.!!
REST를 만드신 분의 말씀은 아래와 같습니다..
한,두개의 제약조건을 어겨도 좋다.. 하지만, 그것은 진정한 RESTFUL이 아니다..라고 말씀하셨습니다.
REST를 REST답게 사용하기 위해서는 만드신 분이 설정하는 제약조건을 무조건적으로 따라야함을 알 수 있었습니다...
이번에 REST를 학습하면서, 제가 얼마나 무지했는지 알게 되었습니다.
포스팅을 하면서 제가 짠 코드를 수정한다고 했지만, 결국에는 REST의 제약조건을 다 맞추지 못해 REST가 아닌 REST API를 짜게 되었습니다ㅜㅜ
위에서 설명한 REST 제약 조건말고도 다양한 REST 제약조건이 존재합니다! 아래의 블로그를 통해 확인하시면 될 것 같습니다!!
그 외에도 RESTFUL하게 짜기 위한 다양한 규칙들이 존재합니다!