웹 개발을 처음 시작할 때는 Django를 배우며 서버에서 HTML을 직접 렌더링하는 방식이 당연하다고 생각했다. 프론트엔드와 백엔드가 하나의 프로젝트 안에서 함께 동작하며, 데이터베이스에서 가져온 정보를 그대로 템플릿을 통해 사용자에게 보여주는 방식이 일반적이라고 여겼다. 하지만 이후 Spring을 배우면서 API 개발이라는 개념을 접하게 되었고, Django에서의 웹 개발 방식과는 확연히 다른 구조라는 것을 알게 되었다.
처음에는 단순히 "프론트엔드와 백엔드를 분리하면 API가 필요하다" 정도로만 이해했다. 하지만 정작 SSR(Server-Side Rendering)과 RESTful API가 정확히 무엇인지, 이 둘이 어떤 차이를 가지는지 명확하게 알지 못했다. SSR은 서버에서 HTML을 렌더링해서 클라이언트에 보내는 방식이고, RESTful API는 클라이언트가 필요한 데이터를 요청하고 JSON 등의 형식으로 응답을 받는 방식이라는 정도로만 알고 있었지만, 실제로 이 두 개념이 웹 개발에서 어떻게 활용되고 어떤 장단점을 가지는지 깊이 고민해 본 적은 없었다.
그래서 이를 제대로 정리해보고 싶었다. RESTful API는 현재 가장 널리 사용되는 방식이지만, 단순히 "API를 만든다"라는 수준을 넘어 RESTful한 설계를 위해서는 어떤 원칙을 따라야 하는지, 그리고 RESTful API가 기존의 웹 통신 방식과 비교했을 때 어떤 차별점을 가지는지 명확히 이해하는 것이 중요하다고 생각했다.
그래서 이번 글에서는 RESTful API가 등장한 배경과 기존 웹 통신 방식의 차이점, RESTful API의 핵심 개념과 설계 원칙, 그리고 RESTful API의 한계점과 이를 보완할 수 있는 대안 기술들까지 정리할 예정이다. 나처럼 RESTful API를 쓰면서도 그 의미를 제대로 정리하고 싶었던 사람들에게 도움이 되었으면 한다.(사실은 내가 공부하기 위함이다..)
RESTful API는 왜 사용하게 되었을까?
1. 기존 웹 통신 방식의 한계
초기 웹 애플리케이션은 서버에서 HTML을 렌더링하여 클라이언트에 보내는 방식(SSR, Server-Side Rendering)을 사용했다. 이 방식에서는 사용자가 페이지를 변경할 때마다 전체 HTML을 다시 받아와야 했기 때문에, 성능이 낮고 비효율적이었다.
이후 Ajax(Asynchronous JavaScript and XML)가 등장하면서, 클라이언트는 페이지 전체가 아닌 필요한 데이터만 서버에 요청할 수 있게 되었다. 하지만, 당시의 웹 서비스들은 여전히 SOAP(Simple Object Access Protocol) 기반의 API를 많이 사용하고 있었는데, 이 방식은 XML 기반이라 복잡하고 무겁다는 단점이 있었다.
2. REST의 등장과 API 설계 표준화
REST는 기존의 SOAP API보다 훨씬 가볍고, HTTP 프로토콜을 그대로 활용할 수 있어 직관적인 API 설계가 가능했다. RESTful API는 JSON을 사용하여 데이터를 주고받을 수 있으며, HTTP 메서드와 URI를 활용한 간결한 구조 덕분에 API 개발 및 사용이 쉬워졌다.
결과적으로, RESTful API는 빠르게 확산되었고, 오늘날 대부분의 웹 및 모바일 애플리케이션에서 기본적인 API 설계 방식으로 자리 잡았다.
SSR (HTML을 직접 렌더링하는 방식)
웹 개발을 시작할 때 Django를 처음 배우면서 서버에서 직접 HTML을 생성하여 브라우저에 전달하는 방식(SSR, Server-Side Rendering)을 사용했다.
SSR은 화면의 렌더링이 서버에서 이루어지는 아키텍처를 의미한다. (하지만 현대의 SSR은 첫 HTML 렌더링을 서버에서 처리하고, 이후엔 클라이언트에서 처리하는 하이브리드 형태의 SSR을 말한다.)
Django에서는 views.py에서 데이터를 받아 templates/ 폴더에 있는 HTML 파일을 렌더링하고, 이를 클라이언트(브라우저)로 응답한다.
예를 들면, 다음과 같은 코드로 데이터를 받아 HTML을 렌더링한다.
from django.shortcuts import render
from .models import User
def user_list(request):
users = User.objects.all()
return render(request, "user_list.html", {"users": users})
그리고 user_list.html에서는 받은 데이터를 다음과 같이 사용할 수 있다.
{% for user in users %}
<p>{{ user.name }}</p>
{% endfor %}
이 방식은 매우 직관적이고, 서버에서 HTML을 생성해 주기 때문에 빠르게 웹 애플리케이션을 개발할 수 있는 장점이 있다.
이후 접하게 된 Spring Boot 역시 처음에는 Django처럼 서버에서 HTML을 렌더링하는 방식(Thymeleaf, JSP)도 사용해 볼 수 있었다. Spring에서도 Controller를 만들어 데이터를 HTML로 넘길 수 있다.
하지만 프로젝트가 점점 커지고, 프론트엔드와 백엔드가 분리된 구조(React, Vue.js 같은 프레임워크 사용)로 넘어가면서 한계가 보이기 시작했다. 특히, 프론트엔드 개발이 발전하면서 HTML을 서버에서 직접 렌더링하는 방식보다 프론트엔드와 백엔드를 분리하는 방식이 더 효율적이라는 것을 알게 되었다. React나 Vue.js 같은 프론트엔드 프레임워크를 사용하면, 서버는 단순히 데이터를 JSON으로 반환하고, HTML은 프론트엔드에서 동적으로 렌더링하는 방식이 더 적합했다.
이제 Django나 Spring Boot에서 서버는 HTML을 직접 렌더링하는 것이 아니라, JSON을 반환하는 API 서버 역할을 하게 된 것이다.
Spring Boot도 기본적으로 HTML을 렌더링할 수 있지만, API 개발을 위해 @RestController를 사용하면 JSON 데이터를 반환하는 API를 만들 수 있다. 하지만 Django는 기본적으로 HTML을 렌더링하는 웹 프레임워크이기 때문에, API 서버로 사용하려면 Django REST Framework(DRF)를 추가적으로 적용해야 한다.
SOAP API
웹 서비스에서 데이터를 주고받는 방식은 계속 발전해 왔다. 현재 대부분의 웹 서비스는 RESTful API를 표준으로 사용하지만, 과거에는 SOAP API가 널리 사용되었다고 한다. (물론 나도 SOAP API를 설계해 본 적은 없지만 이 글을 포스팅하면서 알게 되었다.)
SOAP(Simple Object Access Protocol)은 네트워크 상에서 애플리케이션 간 안정적인 통신을 위한 표준 프로토콜이다. XML을 기반으로 데이터를 주고받으며, HTTP, SMTP, TCP/IP 등 다양한 프로토콜 위에서 동작할 수 있다. 또한, WSDL(Web Services Description Language)을 통해 명확한 API 정의를 제공하고, 보안과 트랜잭션 관리 기능을 포함하고 있어 데이터 무결성과 신뢰성을 보장하는 데 강점이 있다.
그러나 SOAP은 몇 가지 단점이 있다.
1. XML 기반의 메시지 형식만을 사용하기 때문에 데이터 크기가 크고, 이를 처리하는 데 많은 리소스가 필요하다. 이로 인해 네트워크 비용이 증가하고, 성능이 저하될 수 있다. 또한, RESTful API에서 사용하는 JSON보다 가독성과 효율성이 떨어진다.
2. 구조가 복잡하고 개발 및 유지보수가 어렵다. SOAP은 WSDL(Web Services Description Language)이라는 스키마를 기반으로 동작하는데, 이는 API를 정의하는 데 있어 강력한 표준화를 제공하지만, 반대로 API 변경이 있을 경우 수정하는 과정이 어렵고 번거롭다. 반면 RESTful API는 URI 설계와 HTTP 메서드만으로 유연하게 API를 확장할 수 있어 훨씬 직관적이고 쉽게 변경할 수 있다.
3. SOAP은 HTTP 표준을 완벽하게 활용하지 못한다는 한계도 있다. REST API는 HTTP의 GET, POST, PUT, DELETE 같은 메서드를 활용해 직관적으로 리소스를 조작할 수 있지만, SOAP은 자체적인 메시지 구조를 가지고 있어 HTTP의 기본적인 기능(상태 코드, 캐싱, 헤더 활용 등)을 온전히 활용하기 어렵다. (SOAP은 모든 요청을 POST로 처리하며, 리소스를 URI로 표현하지 않고 XML 내부에서 정의한다.) 따라서 HTTP를 단순한 메시지 전달 수단으로만 사용하며, HTTP 프로토콜의 특징을 적극적으로 활용하지 않는다.
4. 모바일 및 클라우드 환경과의 적합성이 낮다. SOAP은 데이터를 주고받을 때 XML을 사용하기 때문에 데이터가 무겁고, 처리 속도가 느리다. 반면 RESTful API는 가볍고 빠른 JSON 형식을 주로 사용하여 네트워크 트래픽을 줄이고, 모바일 및 클라우드 환경에서도 높은 성능을 발휘할 수 있다.
물론 이러한 단점에도 불구하고, SOAP은 보안성이 좋고, 트랜잭션 관리 기능을 제공하기 때문에 여전히 특정 분야에서는 사용되고 있다고 한다. 금융 시스템(은행, 보험, 증권 등)에서는 트랜잭션 무결성(ACID)과 높은 보안 수준이 필수적이기 때문에 SOAP이 적합하다고 한다. 또한, 정부 및 공공기관에서의 전자 서명 및 인증 시스템과 같이 신뢰성이 중요한 서비스에서도 SOAP이 여전히 활용된다.
결과적으로, SOAP은 구조적으로 복잡하고 무겁다는 단점이 있지만, 높은 보안성과 트랜잭션 안정성이 요구되는 환경에서는 여전히 중요한 역할을 하고 있다. 그러나 일반적인 웹 애플리케이션과 마이크로서비스 환경에서는 RESTful API가 훨씬 가볍고 유연하기 때문에, 현재 대부분의 웹 서비스는 RESTful API를 중심으로 구축되고 있다.
RESTful API란?
웹 서비스에서 데이터를 주고받는 방식은 계속 발전해 왔으며, 현재 가장 널리 사용되는 방식이 RESTful API이다. RESTful API는 REST(Representational State Transfer) 아키텍처 스타일을 따르는 API로, HTTP 프로토콜을 활용하여 리소스(Resource)를 효과적으로 관리하는 방식이다. RESTful API는 단순하고 직관적인 구조, 확장성과 유연성, 그리고 웹 표준을 준수하는 방식 덕분에 오늘날 대부분의 웹 및 모바일 애플리케이션에서 표준처럼 사용되고 있다.
REST란?
REST(Representational State Transfer)는 웹에서 리소스를 효과적으로 사용하기 위한 소프트웨어 아키텍처 스타일이다. REST의 핵심 개념은 리소스(Resource)와 이를 조작하는 HTTP 메서드를 기반으로 한다.
REST의 기본 원칙
RESTful API가 되기 위해서는 다음과 같은 원칙을 따라야 한다.
1. 클라이언트-서버 구조 (Client-Server Architecture)
클라이언트(React, Vue.js)와 서버(Django, Spring Boot)를 완전히 분리하여 독립적으로 동작하도록 설계해야 한다. 서버는 데이터를 제공하는 역할만 수행하며, UI 관련 처리는 프론트엔드에서 담당한다.
2. 무상태성 (Stateless)
서버는 클라이언트의 상태를 저장하지 않는다. 즉, 클라이언트의 모든 요청은 독립적으로 처리되어야 하며, 필요한 모든 정보를 포함해야 한다. 이러한 구조 덕분에 확장성이 뛰어나고, 여러 대의 서버를 사용해 부하를 분산할 수 있다.
3. 캐시 가능 (Cacheable)
HTTP의 Cache-Control 헤더를 활용하여 자주 변경되지 않는 데이터를 캐싱하면 성능을 향상시킬 수 있다.
4. 계층화된 시스템 (Layered System)
RESTful API 서버는 여러 계층(보안, 로드 밸런서, 데이터베이스 등)으로 구성될 수 있으며, 이를 통해 보안 강화, 성능 최적화, 유지보수성 향상이 가능하다.
5. 일관된 인터페이스 (Uniform Interface)
API의 구조가 일관되어야 한다. 예를 들어, /users라는 엔드포인트에서는 GET 요청 시 모든 사용자 조회, POST 요청 시 사용자 생성, PUT 요청 시 사용자 정보 수정, DELETE 요청 시 사용자 삭제와 같은 일관된 동작을 해야 한다. 이러한 일관성이 깨지면 API 사용이 어렵고 유지보수성이 떨어진다.
RESTful API 설계 원칙
RESTful API는 사용하기 쉽고, 유지보수성이 좋은 API를 설계하는 것이 중요하다.
이를 위해 다음과 같은 원칙들을 준수해야 한다.
1. 리소스(URI) 설계 원칙
- 리소스를 명확하게 식별할 수 있도록 설계해야 한다.
- 리소스는 명사를 사용하고, 소문자+하이픈(-) 또는 언더스코어(_)를 사용하는 것이 일반적이다.
-> 잘못된 예시 GET /getUserList , GET /show_users?id=1-> 올바른 예시 GET /users (사용자 리스트 조회) , GET /users/1 (ID 1인 사용자 조회)
- 행동(액션)은 URI가 아니라 HTTP 메서드를 사용해야 한다.
-> 잘못된 예시 GET /create-user , GET /delete-user?id=1-> 올바른 예시 POST /users (사용자 생성), DELETE /users/1 (ID 1인 사용자 삭제)
2. HTTP 메서드 사용 원칙
RESTful API에서는 HTTP 메서드를 활용해 리소스를 조작하는 방식을 정의한다.
HTTP 메서드 | 역할 | 예시 (Users Resource) |
GET | 리소스 조회 | GET /users (모든 사용자 조회) |
POST | 리소스 생성 | POST /users (새 사용자 생성) |
PUT | 리소스 전체 수정 | PUT /users/1 (ID=1 사용자 전체 수정) |
PATCH | 리소스 일부 수정 | PATCH /users/1 (ID=1 사용자 일부 수정) |
DELETE | 리소스 삭제 | DELETE /users/1 (ID=1 사용자 삭제) |
3. 응답 상태 코드 사용 원칙
RESTful API는 HTTP 상태 코드를 활용하여 클라이언트가 요청 결과를 이해할 수 있도록 한다.
200 OK | 요청이 성공적으로 처리됨 |
201 Created | 새로운 리소스가 성공적으로 생성됨 |
204 No Content | 요청이 성공했지만 응답 본문이 필요 없음 (DELETE 요청 등) |
400 Bad Request | 잘못된 요청 (유효하지 않은 파라미터 등) |
401 Unauthorized | 인증이 필요함 |
403 Forbidden | 권한이 없음 |
404 Not Found | 요청한 리소스를 찾을 수 없음 |
500 Internal Server Error | 서버 내부 오류 |
4. HATEOAS 원칙
HATEOAS(Hypermedia as the Engine of Application State)는 RESTful API의 중요한 개념 중 하나로, 클라이언트가 API의 응답을 통해 수행할 수 있는 추가적인 액션을 동적으로 탐색할 수 있도록 하는 방식이다. 즉, API 응답에 현재 리소스에 대한 정보를 제공하는 것뿐만 아니라, 클라이언트가 다음에 수행할 수 있는 작업(링크)을 포함하는 것이 핵심이다.
# HATEOAS 미적용 API 응답
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
# HATEOAS 적용 API 응답
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"links": {
"self": { "href": "/users/1" },
"friends": { "href": "/users/1/friends" },
"update": { "href": "/users/1", "method": "PUT" },
"delete": { "href": "/users/1", "method": "DELETE" }
}
}
이렇게 하면 클라이언트가 API 응답을 보고 어떤 액션을 수행할 수 있는지 쉽게 알 수 있다. API 문서를 보지 않아도 동적으로 기능을 탐색할 수 있다는 것이 HATEOAS의 가장 큰 장점이다.
RESTful API를 평가하는 방법은?
RESTful API를 평가하는 공식적인 기준(국제 표준)은 존재하지 않지만, API가 얼마나 RESTful한지를 판단하는 대표적인 기준 중 하나로 리처드슨 성숙도 모델(Richardson Maturity Model)이 있다. 이 모델은 RESTful API가 얼마나 REST의 원칙을 따르는지 4단계(0~3단계)로 나누어 평가하는 방식이다. 각 단계별로 RESTful API의 성숙도를 평가할 수 있다.
0단계 (Level 0) - HTTP 기반 API (RESTful하지 않은 API)
HTTP를 사용하지만 RESTful하지 않은 API
POST /api
Content-Type: application/json
{
"action": "getUser",
"userId": 1
}
가장 낮은 단계인 0단계는 단순히 HTTP를 사용하지만, RESTful하지 않은 API를 의미한다. 이 단계에서는 모든 요청이 POST /api 같은 단일 엔드포인트로 들어오며, HTTP 메서드를 활용하지 않는다. API가 마치 원격 프로시저 호출(Remote Procedure Call, RPC)처럼 동작하는 방식으로, 모든 요청을 본문(body)에 담아 처리한다. 이 방식은 HTTP의 장점을 활용하지 못하며, 확장성과 유지보수성이 낮은 단점이 있다.
1단계 (Level 1) - 리소스 중심 (Resources)
리소스를 개념적으로 분리하여 URI를 설계한 API
POST /users?action=getUser&id=1
1단계에서는 RESTful한 설계를 위해 리소스(Resource) 개념이 도입된다. 엔드포인트를 /users, /products와 같이 리소스 중심으로 설계하여 API를 보다 직관적으로 만들 수 있다. 하지만 여전히 요청 방식이 RESTful하지 않을 수 있으며, POST /users?action=delete&id=1처럼 HTTP 메서드를 제대로 활용하지 않는 경우가 많다. 따라서 API 구조가 개선되긴 했지만, 여전히 RESTful하지 않은 부분이 존재한다.
2단계 (Level 2) - HTTP 메서드 활용 (HTTP Verbs)
HTTP 메서드를 적극적으로 활용한 RESTful API
GET /users/1 # 사용자 조회
POST /users # 사용자 생성
PUT /users/1 # 사용자 전체 수정
PATCH /users/1 # 사용자 일부 수정
DELETE /users/1 # 사용자 삭제
2단계에 도달하면 API는 RESTful 원칙을 상당 부분 따르게 된다. 이 단계에서는 HTTP 메서드(GET, POST, PUT, DELETE)를 활용하여 CRUD 작업을 수행하고, HTTP 상태 코드(200, 201, 400, 404 등)를 적절히 활용하여 요청 처리 결과를 명확하게 반환한다. 예를 들어, 사용자를 조회할 때는 GET /users/1을 사용하고, 새로운 사용자를 추가할 때는 POST /users를 사용하는 방식이다. 이 단계부터는 RESTful API의 핵심 원칙을 따르게 되며, 실무에서도 가장 널리 사용되는 형태다.
3단계 (Level 3) - HATEOAS 적용 (Hypermedia Controls)
API 응답에 "다음에 수행할 수 있는 액션(링크)"을 포함하여 동적인 API 탐색 가능
- API 응답에 HATEOAS(Hypermedia As The Engine Of Application State) 원칙을 적용한다.
- API 문서 없이도 클라이언트가 응답의 links 속성을 참고하여 API를 탐색 가능하다.
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"links": {
"self": { "href": "/users/1" },
"friends": { "href": "/users/1/friends" },
"update": { "href": "/users/1", "method": "PUT" },
"delete": { "href": "/users/1", "method": "DELETE" }
}
}
3단계는 RESTful API의 최종 형태로, HATEOAS(Hypermedia As The Engine Of Application State) 개념을 적용하는 것이다. 이 단계에서는 API 응답에 다음에 수행할 수 있는 액션(링크)을 포함하여, 클라이언트가 API를 동적으로 탐색할 수 있도록 한다. 즉, API 문서를 별도로 참조하지 않아도, 응답 데이터를 기반으로 다음 동작을 수행할 수 있다. 예를 들어, 사용자의 정보를 조회하는 API 응답에 self, friends, update, delete 등의 링크를 포함하여, 사용자가 어떤 추가적인 요청을 할 수 있는지 안내하는 방식이다. API 문서 없이도 API를 동적으로 탐색 가능하고, API의 유지보수가 쉬워진다는 장점이 있다.
하지만 HATEOAS는 실무에서 거의 사용되지 않는다. 왜냐하면 API 응답 크기가 커져 네트워크 비용이 증가하고, 프론트엔드에서 이를 활용하는 로직이 복잡해지며, API 문서(OpenAPI, Swagger)나 GraphQL 같은 대체 기술이 등장했기 때문이다. 이 때문에 실무에서는 대부분 Level 2(HTTP 메서드 활용)까지만 구현하고, HATEOAS는 적용하지 않는 경우가 많다.
HATEOAS는 꼭 적용해야 할까?
리처드슨 성숙도 모델에서는 HATEOAS가 적용된 API가 가장 RESTful하다(3단계)고 평가하지만, 위와 같은 3단계의 단점으로 실제로 모든 RESTful API에서 반드시 적용해야 하는 필수 요소는 아니다. 즉, RESTful API의 이상적인 목표일 수는 있어도 의무적인 것은 아니다.
<HATEOAS가 유용한 경우>
- API가 동적으로 변경될 가능성이 높은 경우
- 클라이언트가 API 문서를 몰라도 기능을 탐색할 수 있어야 하는 경우
- 여러 엔드포인트를 탐색하며 동적인 API 연동이 필요한 경우
<HATEOAS가 필요하지 않은 경우>
- API가 고정된 엔드포인트를 사용하고, 명확한 API 문서를 제공하는 경우
- 성능 최적화가 중요한 경우 (HATEOAS를 적용하면 API 응답 크기가 커질 수 있음)
- 마이크로서비스 간 통신과 같이 정형화된 API 호출이 필요한 경우
결론적으로, HATEOAS는 RESTful API의 완성도를 높이는 요소지만, 모든 API에 필수적으로 적용할 필요는 없다. (사실 나도 사용해본 적 없다..)현실적으로 대부분의 API는 리처드슨 성숙도 모델의 2단계(HTTP 메서드 활용)까지만 적용해도 충분히 RESTful한 API가 될 수 있다.
RESTful API의 장점과 한계
RESTful API는 웹과 모바일 애플리케이션에서 데이터를 주고받는 가장 보편적인 방식으로 자리 잡았다. 그 이유는 단순하면서도 직관적인 구조 덕분에 개발자가 쉽게 이해하고 사용할 수 있기 때문이다. 클라이언트와 서버를 분리하는 아키텍처를 기반으로 하기 때문에 확장성이 뛰어나고, 웹 표준을 준수하면서도 다양한 환경에서 유연하게 적용할 수 있다. 하지만 RESTful API가 완벽한 해결책은 아니며, 몇 가지 한계점이 존재한다.
RESTful API의 가장 큰 장점은 HTTP 메서드를 활용해 직관적인 API 설계가 가능하다는 점이다. 예를 들어, GET /users를 호출하면 사용자 목록을 가져오고, POST /users를 호출하면 새로운 사용자를 생성하는 방식이다. 이러한 구조 덕분에 개발자들은 API를 쉽게 이해할 수 있고, 별도의 문서를 보지 않고도 직관적으로 사용할 수 있다. 또한, RESTful API는 클라이언트와 서버를 완전히 분리할 수 있어, 프론트엔드와 백엔드를 독립적으로 개발하고 유지보수할 수 있는 장점이 있다. 같은 API를 웹, 모바일, 데스크톱 애플리케이션에서 재사용할 수도 있다.
또한, RESTful API는 무상태성(Stateless)을 원칙으로 한다. 즉, 서버는 클라이언트의 상태를 저장하지 않으며, 각 요청은 독립적으로 처리된다. 이러한 구조는 서버의 부담을 줄이고 확장성을 높이는 데 기여한다. 예를 들어, 하나의 서버가 트래픽을 감당하지 못할 경우, 동일한 API를 처리하는 서버를 추가하는 방식(Scale-out)이 가능하다. 또한, HTTP의 캐싱(Cacheable) 기능을 활용하면 API 응답을 캐싱하여 성능을 최적화할 수도 있다. 예를 들어, 자주 변경되지 않는 데이터는 캐시를 사용하여 불필요한 요청을 줄이고, 네트워크 트래픽을 감소시킬 수 있다.
RESTful API는 특정 프로그래밍 언어나 플랫폼에 종속되지 않는다는 점에서도 강점이 있다. JSON 또는 XML과 같은 포맷을 사용하여 데이터를 주고받을 수 있어, 다양한 환경에서 유연하게 활용할 수 있다. 예를 들어, 백엔드는 Spring으로 개발하고, 프론트엔드는 React로 개발해도 문제없이 API를 연동할 수 있다. 이러한 표준화된 인터페이스 덕분에 RESTful API는 많은 기업과 개발자들이 선호하는 방식이 되었다.
하지만 RESTful API가 모든 상황에서 완벽한 해결책은 아니다. 대표적인 단점으로 Over-fetching(과도한 데이터 요청)과 Under-fetching(부족한 데이터 요청) 문제가 있다. RESTful API는 엔드포인트에서 미리 정해진 데이터만 제공하기 때문에, 클라이언트가 원하는 데이터를 한 번의 요청으로 가져오기 어려운 경우가 있다. 예를 들어, 사용자의 이름과 이메일만 필요한데, GET /users 요청을 하면 모든 사용자 정보를 받아야 하는 상황(Over-fetching)이 발생할 수 있다. 반대로, 특정 게시글과 작성자의 정보를 함께 가져오려면, GET /posts 요청으로 게시글을 가져오고, 추가로 GET /users/{id} 요청을 여러 번 수행해야 하는 상황(Under-fetching)이 생길 수도 있다.
이러한 문제를 해결하기 위해 GraphQL이 등장했으며, GraphQL을 사용하면 클라이언트가 필요한 데이터만 선택적으로 가져올 수 있다.
또한, RESTful API는 버전 관리를 하는 데 어려움이 있다. 보통 URI를 통해 버전 관리를 하곤 하는데(/api/v1/users), API가 변경될 경우 기존 API와의 호환성을 유지하기 어렵고, 새로운 버전을 만들어야 하는 부담이 있다. 이를 해결하기 위해 HTTP 헤더를 활용하는 방법도 있지만, 여전히 API의 변경 관리에는 신중한 접근이 필요하다.
실시간 데이터 전송이 어려운 점도 RESTful API의 단점 중 하나다. REST는 클라이언트가 요청해야만 응답을 받을 수 있는 방식이기 때문에, 실시간성이 중요한 서비스(채팅, 주식거래 등)에는 적합하지 않다. 이러한 경우에는 WebSocket이 더 적합하다. WebSocket은 클라이언트와 서버 간 양방향 통신을 지원하여, 서버가 클라이언트에게 실시간으로 데이터를 전송할 수 있다.
또한, RESTful API의 이상적인 형태로 평가되는 HATEOAS(Hypermedia As The Engine Of Application State) 개념이 있지만, 현실적으로는 거의 사용되지 않는다. HATEOAS를 적용하면 API 응답에 다음에 수행할 수 있는 액션(링크)을 포함하여, 클라이언트가 API를 동적으로 탐색할 수 있도록 만든다. 하지만 이 방식은 API 응답 크기를 증가시키고, 프론트엔드에서 이를 처리하는 로직이 복잡해지는 문제를 야기한다. 따라서 많은 실무 환경에서는 HATEOAS를 적용하지 않고, RESTful API의 기본적인 원칙만 따르는 경우가 많다.
마이크로서비스 아키텍처(MSA)에서도 RESTful API가 항상 최선의 선택은 아니다. 마이크로서비스 환경에서는 서로 다른 서비스 간 통신이 필요하지만, RESTful API는 네트워크 비용이 높고 요청을 여러 번 수행해야 하는 경우가 많다. 물론 현재도 MSA에서 RESTful API가 널리 사용되고 있지만, 성능 최적화가 필요한 경우에는 gRPC와 같은 고성능 API 통신 방식을 고려할 수도 있다. gRPC는 바이너리 데이터 전송을 기반으로 하여 속도가 빠르고, 서비스 간 통신에 최적화되어 있어 보다 효율적인 데이터 교환이 가능하다.
결국, RESTful API는 가장 보편적으로 사용되는 API 설계 방식이지만, 모든 상황에 적합한 것은 아니다. 단순한 CRUD 작업이 중심이 되는 일반적인 웹 애플리케이션에서는 RESTful API가 충분히 강력한 솔루션이지만, 실시간 데이터가 필요한 서비스, 복잡한 관계 데이터를 다루는 서비스, 서비스 간 통신이 빈번한 환경에서는 다른 대안(GraphQL, WebSocket, gRPC 등)과 함께 고려하는 것이 필요하다.
따라서 API를 설계할 때는 RESTful API가 기본적인 선택이 될 수 있지만, 프로젝트의 특성과 요구사항을 고려하여 적절한 기술을 조합하는 것이 가장 효과적인 접근 방식이 될 것이다. RESTful API는 확장성과 유연성을 갖춘 강력한 설계 방식이지만, 한계점을 보완하기 위해 다양한 기술을 함께 고민하는 것이 중요하다.