Programming

RESTful 설계 원칙에 대한 못다 한 이야기

Junior Lob! 2024. 6. 15. 23:46

 
 

이 글의 결론만 알고 싶다면?

  • 최근 RESTful API라고 부르는 결과물들은 엄밀히 따지자면 대부분 RESTful한 API가 아닙니다.
  • RESTful API라는 키워드를 만족하는데 매몰되기보다는 표준 HTTP API 요소를 활용해 API를 적절히 표현하고 확장하는 아이디어를 이해하는 것이 중요합니다.
  • RESTful API가 적절하지 않은 시점과 상황을 고려하는 유연한 사고가 필요하다고 생각합니다.

 
 
 

이 글을 쓰게 된 배경

2024년 1월 말 게으른 개발자 컨퍼런스에서 “소비자 관점의 API 설계 패턴, 사례 훑어보기” 라는 주제로 발표하며 맥락상 내용에서 제외했거나 시간 관계상 언급하지 못했던 부분을 글로 남겨보고자 합니다. 하단 참고 링크에서 발표 자료를 확인하실 수 있습니다.
 
RESTful 설계 원칙에 대한 이해도가 있고, 클라이언트와의 협업 경험과 함께 API를 실제로 개발하거나 운영해 보신 경험이 있지 않으시다면 아래 내용이 뜬구름 잡는 소리로 들리실 수 있습니다.
 
개인적인 개발 경험과 지식을 기반으로 예측하는 부분도 포함하여 서술하다 보니 틀린 내용이 있을 수 있습니다. 이런 부분은 댓글을 통해 피드백 부탁드립니다.
 
 
 

첫 번째. 여러 사람들은 RESTful 설계 원칙에 대해 잘못 이해하고 있습니다.

RESTful 설계 원칙은 Hypermedia를 기반으로 웹 페이지와 API에 대한 표준화를 위해 고안되었습니다.
 
이 원칙은 이전부터 사용해 오던 SOAP, CORBA와 같은 구현이나 연동 방법이 복잡하면서도 표준화된 설계 원칙이 없었던 프로토콜을 대체하기 위해 만들어졌으며, HTTP API의 표준 기능과 Hypermedia를 최대한으로 활용해 API의 응답이 변경되더라도 브라우저에서 문제가 발생하지 않는 하위호환성을 가질 수 있도록 정리되었습니다.
 
RESTful 설계 원칙은 현재 대다수의 웹 애플리케이션에 적용된 구조인 SPA 개념이 없었던 시기에 고안되었고 (2003년 제안) Non-Hypermedia인 JSON(2001년 제안), XML을 응답 포맷으로 허용하지 않습니다. 그렇기에 원칙적으로는 SPA와 함께할 수 없는 API 설계 원칙이라고 볼 수 있습니다.
 

어라? 저희는 React, Vue 등을 기반으로 한 SPA에 대해 RESTful한 API를 잘 제공하고 있는데요?

분명 이렇게 생각하시는 분들이 있을 것 같습니다. 원칙을 기반으로 확실히 말씀드리자면, 지금까지 만들어오신 “RESTful 한 API”라고 생각하신 결과물음 로이필딩이 제안한 RESTful 설계 원칙과는 괴리감이 있는 결과물입니다.
 
단순히 여러 블로그에서 언급하는 “리처드슨의 API 성숙도 모델(Richardson Maturity Model)을 만족하지 못했기에 RESTful API가 아니다”를 이야기하고자 하는 것이 아닙니다.

API 성숙도 모델의 레밸 3을 준수하고 있더라도 API의 응답 포맷을 Hypermedia가 아닌 JSON, XML로 제공하고 있다면 이것은 RESTful API가 아니라 일부 설계 원칙을 차용한 HTTP API를 구현하신 겁니다.

  • 리처드슨의 API 성숙도 모델은 개별 리소스에 맞는 엔드포인트 정의하고 (래밸 1), 적절하게 HTTP 메서드를 매핑하며 (래밸 2), 링크를 통한 연관 리소스, API 엔드포인트를 제공해야 (래밸 3) RESTful API로 간주할 수 있다고 정리한 모델입니다.


 
 
여러 블로그에서 RESTful API에 대한 글을 작성하시며 놓치는 부분을 언급해 보겠습니다.

  1. Hypermedia로 분류할 수 있으려면 링크와 폼이라는 표준 방식으로 제어될 수 있어야 합니다.
  2. RESTful 설계 원칙을 만족하는 API는 클라이언트와 서버간의 의존성을 명확히 분리합니다. 즉 API의 응답이 바뀌더라도 브라우저의 어떠한 수정 없이도 정상적으로 화면을 그려내는 것이 가능해야 합니다.

 
JSON, XML 응답을 기반으로 한 HTTP & JSON RPC는 첫번째로 언급한 조건을 만족하지 못합니다. 애초에 Hypermedia가 아닌 응답을 Javascript 코드를 통해 구문 분석하여 HTML로 변환하는 방법을 취하고 있기 때문이죠.

그러다 보니 자연스럽게 두 번째 조건도 만족하지 못합니다. 현재 구조상 API 응답의 변경이 발생하게 되면 브라우저에 그려지는 화면에도 영향을 미치게 되는데 이는 클라이언트와 서버 모델 간에 강한 의존 관계가 생겼다는 의미입니다.
 
RESTful 설계 원칙의 창시자인 로이 필딩이나 몇몇 원칙주의자들은 이러한 API를 RESTful API로 인정하지 않고 HTTP + JSON RPC 또는 Data API로 구분하여 부릅니다.
 
좀 더 자세한 내용은 참고 링크 항목의 REST APIs must be hypertext-drivenHypermedia systems JSON Data APIs를 읽어보시길 바랍니다.
 
개인적으로 백엔드 개발자는 RESTful API 개발자를 의미하는 것이 아니라고 생각하기에 RESTful API를 완벽하게 준수하지 않으면 틀린 API다 라는
등 태클 걸고 싶지는 않습니다. 하지만 이러한 이해 없이 RESTful 설계 원칙을 만족해야 한다고 강조하고 포폴 등에 어필하는 것은 의미 없는 용어 놀음에 그친다고 생각합니다.
 
 
 

두 번째. RESTful 설계 원칙은 모든 케이스에 적합한 "정답“이 아닙니다.

2000년대 초중반(2000~2008), 대다수에게 보급된 인터넷과 컴퓨터의 영향으로 사용자 층이 기존 전문가 계층에서 일반 사용자 계층까지 매우 넓어지게 되었습니다.

또한 기존 Hypermedia 기반의 웹 페이지가 아니라 AJAX (2005년 제안, Ajax: A New Approach to Web Applications) 기법을 기반으로 다양한 리소스를 복합적으로 표현하고 동적으로 변경할 수도 있는 서비스들이 출시되기 시작했습니다.

  • 이때부터 RESTful 설계 원칙들을 JSON RPC에 차용하기 시작했습니다. (그러면서도 RESTful API라고 불렀죠.)
  • SoundCloud, Facebook, Twitter, Linkedin 등이 이때부터 시작된 서비스입니다.

 
RESTful 설계 원칙이 고안된지 얼마되지 않아 웹 개발 생태계에 새로운 접근 방식과 패러다임이 퍼지기 시작한 것이죠. (Hypermedia 기반의 API와 새롭게 나타난 HTTP + JSON RPC 기반의 Data API를 같이 사용할 수 없는 것은 아닙니다.)

RESTful 설계 원칙은 AJAX 접근 방식을 적용한 웹 서비스가 복합적인 리소스를 표현하고, 동적인 수정 및 요청을 제공하는 것을 고려하지 못한 원칙입니다. 이를 RESTful 설계 원칙에 포함된 것 중 하나인 HATEOAS(Hypermedia as the engine of application state)를 통해 간접적으로 이해할 수 있다고 생각합니다.

HATEOAS는 연관 관계가 있는 리소스 간의 이동을 링크라는 Hypermedia 요소를 통해 클라이언트에게 제공하도록 가이드합니다. 이를 통해 클라이언트는 어떠한 행위(수정, 추가 등)를 하지 않더라도 제공된 링크를 통해 연관된 리소스를 탐색하는 것이 가능하게 됩니다.
 
이 원칙을 기반으로 Twitter와 같은 피드 웹 페이지를 구현한다고 가정해 봅시다. 만약 본인이 작성한 피드의 댓글과 반응을 확인하고 싶다고 한다면 아래와 같이 탐색해야 합니다.

  1. 피드 목록에서 작성한 피드를 선택하고
  2. 연관된 댓글 리소스에 대한 링크를 눌러 해당 피드에 작성된 댓글 목록을 확인한 후
  3. 피드 페이지로 돌아가서 반응과 연관된 링크를 눌러 어떤 반응들이 있는지 확인한다.


이렇게 필요한 데이터를 탐색해야 한다면, 이 서비스의 유저 경험 및 사용성은 좋다고 할 수 있을까요? HATEOAS가 적극적으로 사용되지 않은 것은 이러한 사용성도 영향을 미쳤을거라고 생각합니다. (이에 대해 정확한 히스토리를 알고 계신다면 첨언 부탁드립니다.)

어찌 됐든 간에 앞서 언급한 서비스들은 RESTful 설계 원칙에 포함된 API 표현 방식을 기반으로 인터페이스를 설계하였으나 Hypermedia와 HATEOAS를 활용하기보다는 JSON 포맷을 이용한 HTTP + JSON RPC 형태로 서비스를 개발하였습니다.

하지만 그 이후 발생한 여러 문제점으로 인하여 대체, 보완할 새로운 방법을 물색하기 시작했습니다.
 
 
해당 서비스들은 아래와 같은 문제들을 겪었습니다.

  • HTTP API 엔드포인트가 기하급수적으로 늘어남에 따라 클라이언트의 복잡성이 증가하였고 이로 인한 병목 현상이 발생하였습니다.
    • 언제 어떤 API를 호출할지 파악하고 연동한 뒤 필요한 응답을 패칭하거나 다른 모델로 변환하는 등 실제 사용자의 기능을 제공하기 위해 반복적이고 복잡한 구조를 유지하는데 들어가는 리소스가 증가했습니다.
  • 클라이언트 환경, 정책에 따라 응답 데이터가 달라지는 경우 이를 모두 하나의 응답 모델에 포함하에 따라서 모델과 응답 페이로드가 비대해지고 그로 인한 이력 관리 문제나 메모리 이슈가 발생하였습니다.
    • 수백 개의 필드와 수십 MB의 응답 페이로드에서 필요한 속성만을 골라내니 수 kb 수준만 사용하는 경우도 있었습니다.
  • 네트워크 경유 횟수가 증가로 인한 성능적인 문제가 심화되었습니다.
    • 일반적으로 클라이언트와 서비스 간의 물리적 거리보다, 서비스 간의 물리적 거리가 짧고 경로를 구성하는 요소가 적기 때문에 더 짧은 지연 시간을 가지게 됩니다.

 
그리고 이러한 문제를 개선하기 위해 다양한 설계 및 실험을 진행하였고 아래와 같은 개선된 API 레이어를 제안 및 구성하기 시작했습니다.

  1. Data API 앞에 추가적인 레이어를 두거나 (Adapter Layer, BFF, Composite API)
  2. 클라이언트 관점의 쿼리 언어(FSQL, GraphQL 등)를 만들어 개발 및 도입


이러한 시도를 통해 (위 서비스들은) 제품 성장을 더욱 가속화할 수 있었습니다. 자세한 내용은 아래 참고 링크를 확인해 주시길 바랍니다.
 
위 사례를 생각해 보면 어니언, 레이어, 헥사고날 등의 아키텍처 패턴에서 레이어라는 단위로 추상화 수준을 나눔으로써 하위 레이어의 변화를 최소화하고 순수성, 재사용성을 향상시키는 것처럼 API 또한 공급자, 소비자 관점에 따라 명확하게 분리하고 단계적으로 구성하는 것이 필요하다 볼 수 있습니다.

  • API Composition, BFF Pattern, headless-commerce나 API Led Connectivity로 불리는 패턴, 설계 원칙은 이러한 시도로 인해 만들어진 원칙과 템플릿입니다.
  • 위 패턴들은 백엔드 개발자가 제공해야만 하는 것들이 아닙니다. 특히 BFF 패턴의 경우 최근 백엔드 개발자가 구축하는 내용으로 많이 소개되었으나, 최초에는 클라이언트 개발팀이 자율성을 가지고 구축하였으며 현재도 그렇게 구성하는 회사들이 존재합니다.

 
 
 

HTTP API의 표준 요소를 통해 쉽게 확장하는 아이디어를 이해하고 적절히 활용하는 것이 중요합니다.

AWS의 CTO인 Werner Vogels도 이야기했던 부분이지만, RESTful 설계 원칙을 일부 차용함으로써 일관성 있게 API를 설계하면서도 표현력이 있으며 API를 최소 단위로 구성해 비즈니스를 유연하게 확장하는 데 도움을 받을 수 있습니다.
 

여기서 강조하고 싶은 부분은 RESTful API에 목매어 충족시키려고 한 것이 아니라 필요한 부분만 차용한 것이라는 겁니다.


다양한 데이터를 제공하거나 복잡한 비정형 데이터를 다루는 도메인, 분산 시스템을 기반으로한 모듈 간 분리 등을 언급하는 현재의 개발 트렌드에서 언제 RESTful 설계 원칙을 차용한 우리의 API 인터페이스가 병목 지점이 될지는 모르는 일이라고 생각합니다.
 
Redis, MongoDB, RabbitMQ, Kafka 등 오픈소스 간의 트레이드오프만 고려하지 말고 이러한 API 설계 영역부터 차근차근 고민할 수 있는 유연한 사고가 필요하지 않을까 싶습니다.
 
 
 

참고 자료