RESTFul API에 대해서 알아보던 중 궁금한 점이 생겼다.
진정한 RESTFul 의미는 무엇일까?
나의 백엔드 서버는 ‘RESTFul‘ 한가?
그래서 RESTFul API에 대해 조사해보고, 실제로 그것을 만들기 위해 노력해보았다.
REST API를 알기전에 API의 정의를 먼저 살펴보자.
API는 컴퓨터나 컴퓨터 프로그램사이의 연결이다. 일종의 소프트웨어 인터페이스이며 다른 종류의 소프트웨어에 서비스를 제공한다.
- wikipedia
전혀 무슨말인지 모르겠다.
그럼 조금 더 쉽게 API에 대해서 알아보자.
우리가 식당에 갔을 때 요리사에게 바로 주문을 하는 것이 아니라, 점원에게 요청을 하고 점원이 다시 우리의 주문을 요리사에게 요청하고 만들어진 요리를 점원이 가져다준다. (요리사에게 바로 요청하지 않는 이유는 쉽게 떠오를 수 있다)
즉, 보통에 웹에서 클라이언트(손님), DB(서버) 사이에서 API가 점원의 역활을 해준다.
SELECT * FROM inventory WHERE status = "A" AND qty < 30
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
API가 대신 접근권한도 관리하고, 우리 대신 효율적인 쿼리를 만들어 데이터를 가지고와 필요하다면 가공시켜 우리에게 응답을 줄 것이다.REST는 HTTP 프로토콜을 그대로 활용하여 웹의 장점을 최대한 활용할 수 있는 아키텍처 원칙 세트이다.
리소스를 이름으로 구분하여 해당 리소스의 정보를 주고 받는 모든 것을 의미한다.
위에 분 로이필딩께서 ‘REST’를 제안하셨다.
로이필딩은 박사논문으로 'REST’를 발표했다. 그는 논문에서 ‘REST’를 만들게 된 이유로 아래와 같이 설명했다.
“웹의 구조를 서술하는 비공식 문서들과 두 개의 소개 형식의 논문 등이 있었지만 실제 웹 구현이 너무 빨라 이미 시대에 뒤떨어진(out-dated) 경우가 많았다” 또 “그 당시 이미 캐시와 프록시 등이 웹에는 존재했지만 이에 대해 인식하는 표준은 없다시피 했다.“
그렇기에 웹에 대한 새로운 표준, 또는 표준의 확장이 꼭 필요했다.
또, 로이필딩은 웹의 최대 장정 중 하나인 ‘하위호환성’에 대해서도 고민하였다. 기존에 웹에서도 동작하고 ‘HTTP' 프로토콜 기능을 증가시킬 방법으로 ‘REST’를 고안한 것이다.
REST의 구체적인 특징을 자세하게 보고싶다면 아래에 글을 추천한다.
[Network] REST란? REST API란? RESTful이란? - Heee's Development Blog
REST라는 말이 어려워 보이지만 결국 권위있는 사람이 제시하는 HTTP를 사용하는 웹과 관련된 애플리케이션의 일관성을 위한 가이드라인이다.
그렇다면 이제 위에 개념을 합쳐보자.
‘Rest API’는 REST 가이드라인을 기반으로 API를 설계한 것을 ‘REST API’라고 한다.
하지만, 실무에서 쓰이는 이 ‘REST API’라는 말이 로이필딩의 의도와는 다르게 변질된 거 같다.
왜냐하면 2016년에 Microsoft에서 ‘Microsoft REST API Guidelines’를 발표했지만 로이필딩은 그것은 REST API가 아니라 그냥 HTTP API라고 해야한다고 말했다.
api-guidelines/Guidelines.md at vNext · microsoft/api-guidelines
대부분의 REST API라고 지칭하는 것들은 REST 가이드라인을 완벽하게 지키지 않았다.
특히, ‘Uniform Interface’의 제약 조건 중 ‘Self-descriptive messages’(메시지가 스스로 설명해야 한다. 즉, 메세지만 보고 무슨 뜻을 의미하는지 알아야한다.) ‘HATEOAS’ (애플리케이션의 상태는 HyperLink를 이용해서 전이되어야 한다.)를 거의 지키지 못하고 있다.
이에 대한 설명은 아래에 영상을 보기를 추천한다. REST API에 대한 근본적인 설명과 함께 웹의 특징을 잘 설명해주셔서 이해하기가 좋았다.
그리고 실무에서 REST API라고 지칭하지만, 사용자 ‘Authorization’ 방식으로 쿠키, 세션을 사용한다.
이것은 REST 규칙중 하나인 ‘Stateless (무상태)’를 이미 위반한 것이다.
그러니깐 구글링 하였을 때 나오는 대부분의 'REST API’ 관련 블로그 글들은 로이필딩이 말하는 전통적인 ‘REST API’ 보다는 ‘Microsoft REST API Guidelines’, 또는 실무에서 주로 쓰이는 API 설계 방식이라고 보는게 맞을 꺼 같다.
그리고 위에서도 언급했지만 그와 관련해서 규칙이 잘 정리된 이 글을 추천한다.
[Network] REST란? REST API란? RESTful이란? - Heee's Development Blog
REST API를 설계할 때 대표적인 방식을 살펴보면 ‘URI는 동사보다는 명사, 대문자보다는 소문자를 사용하여야 한다’
HTTP GET http://www.appdomain.com/users/123
HTTP GET http://www.appdomain.com/users/123/address
또 REST API 설계 방식 중 하나인 ‘리소스에 대한 행위는 적절한 ‘HTTP Method (GET, PUT, POST, DELETE)’로 표현해야 한다’에 대해 살펴보자
위에서 대표적인 CRUD에 대해 어떤 Method를 사용해야하는지 설명하고 있다.
그 중 PUT
과 PATCH
를 살펴보자.
RFC문서에서도 PUT
과 PATCH
역활을 위와 비슷하게 설명하고 있다.
RFC 5789: PATCH Method for HTTP
오늘은 이 규칙을 중점적으로 나의 API 서버는 과연 이것을 잘 지키고 있는가를 살펴보고자 한다.
‘Express’를 이용한 서버에서 ‘Mongoose’를 통해 MonogDB에 기본적인 CRUD를 담당한다.
그 중 ‘UPDATE’에 주목해보자.
exports.update = asyncHandler(async (req, res, next) => {
const updatedArticle = await Article.findByIdAndUpdate(
req.params.article_id,
req.body,
{ new: true }
);
res.json({ result: "ok", article: updatedArticle });
});
router.put(
"/:article_id",
verifyToken,
verifyArticleId,
articlesController.update
);
만약 클라이언트가 아래와 같이 요청을 보낸다면
HTTPRequest
PUT `/articles/${articleId}`
{
title: "Hello World"
}
아래와 같이 MongoDB에 저장된 데이터가 업데이트 되어야 한다.
{
_id: "627620efd2a10c5dd2d0cabd"
title: "Hello World"
}
하지만, 내가 사용한 ‘findByIdAndUpdate’는 전체 데이터를 ‘replace’ 하는것이 아니라 클라이언트가 보낸 데이터를 이용하여 이미 존재하는 아이템에 해당 속성(’title’)을 업데이트 할 것이다.
위에 ‘**Microsoft REST API Guidelines’에 따르면 PUT Method은 전체 요청으로 받은 객체를 통채로 교체할 때 (replace) 때 사용해야 하지만 코드에서는 그러고 있지 않다.**
그럼 이것을 조금더 REST스럽게 바꿔보자. PUT과 PATCH에 대한 동작을 각각 정의해주었다.
exports.replace = asyncHandler(async (req, res, next) => {
const replacedArticle = await Article.findOneAndReplace(
req.params.article_id,
req.body,
{
upsert: false,
new: true,
}
);
res.json({ result: "ok", article: replacedArticle });
});
exports.update = asyncHandler(async (req, res, next) => {
const updatedArticle = await Article.findByIdAndUpdate(
req.params.article_id,
req.body,
{ new: true }
);
res.json({ result: "ok", article: updatedArticle });
});
router.put(
"/:article_id",
verifyToken,
verifyArticleId,
articlesController.replace
);
router.patch(
"/:article_id",
verifyToken,
verifyArticleId,
articlesController.update
);
replace를 관련 로직은 ‘mongoose’에 ‘findOneAndReplace’ 메서드를 사용하였다.
이제 ‘PUT’ Method에 대해서 적절한 방식으로 동작하고 클라이언트가 보낸 데이터를 이용하여 ‘replace’, ‘update’가 가능해졌다.
포스트맨을 활용하여 API가 제대로 확인해보자.
위에서 보는것과 같이 REST API를 규칙을 따르려고 한 의도대로 PUT은 ‘replace’ PATCH는 ‘update’ 동작하고 있는 것을 볼 수 있다.
API와 REST에 대한 간단한 개념을 살펴보고 REST API에 대한 오해와 대표적인 규칙들에 대해 살펴보았다.
그리고 직접 그 규칙을 따르기 위해 노력해보았다.
직접 실습해 본 결과 내가 한 부분이 굉장히 작은 부분임에도 불구하고 규칙을 전부 따르기가 굉장히 어렵다는 것을 느꼈다.
그리고 진정 REST 규칙을 전부 지켜서 설계한 API를 RESTFul API라 지칭한다.
그럼 우리 전부 RESTFul API를 향해 가야할까?
"REST emphasizes evolvability sustain on uncontrollable system. If you think you have control over the system or aren't interested in evolvability, don't waste your time arguing about REST
. - Roy T. Fielding
로이필딩은 시스템 전체를 통제할 수 있다면 그리고 진화에 관심이 없다면(🥲 ) REST에 대해 따지느라 시간을 허비하지 말라고 한다.
내가 생각하기에는 REST를 지키기 위해 노력하는 것도 중요하지만, 실무에서 클라이언트, 서버 단 개발자들 끼리의 소통과 사내에서 약속된 문서를 통해 효율적인 API 설계도 중요하지 않을까 생각해본다.
하지만 마지막으로 진정 중요한 것은...!
로이필딩이 말한 REST 규칙을 모두 지키지 않고 ‘RESTFul API’라고 지칭하지 말자.
이분이 화내신다.
자원을 수정하는 HTTP 메서드 - PUT vs PATCH