REST vs. GraphQL: 올바른 API 아키텍처 선택
Source: Dev.to
REST 이해하기: 확립된 표준
REST는 Representational State Transfer의 약자로, 월드 와이드 웹의 원칙을 활용하는 아키텍처 스타일입니다. 이는 프로토콜이 아니라 일련의 제약 조건이며, 이를 따를 경우 잘 동작하고 확장 가능하며 유지 관리가 쉬운 분산 시스템을 만들 수 있습니다. REST의 핵심 아이디어는 모든 리소스(예: 사용자, 제품, 주문)를 고유한 URI(Uniform Resource Identifier)로 식별할 수 있는 엔터티로 취급하고, 표준 HTTP 메서드를 사용해 조작하는 것입니다.
REST의 핵심 원칙
-
클라이언트‑서버 아키텍처:
클라이언트와 서버는 독립적이며, 인터페이스만 동일하면 서로 별도로 진화할 수 있습니다. -
무상태성:
클라이언트가 서버에 보내는 각 요청은 요청을 이해하고 수행하는 데 필요한 모든 정보를 포함해야 합니다. 서버는 요청 사이에 클라이언트 컨텍스트를 저장하지 않아야 합니다. -
캐시 가능성:
서버의 응답은 캐시 가능 여부를 명시해야 합니다. 이를 통해 클라이언트는 이후 요청에 대해 응답을 재사용하여 성능을 향상시킬 수 있습니다. -
계층화 시스템:
클라이언트는 직접 최종 서버에 연결되어 있는지, 중간에 있는 중계 서버에 연결되어 있는지 일반적으로 알 수 없습니다. -
통일된 인터페이스: (가장 중요한 제약)
- 리소스 식별: 요청에서 URI를 사용해 리소스를 식별합니다.
- 표현을 통한 리소스 조작: 클라이언트는 리소스의 표현(예: JSON, XML)을 받아서 이를 다시 서버에 전송함으로써 리소스를 조작합니다.
- 자기 기술적 메시지: 각 메시지는 이를 처리하는 방법을 설명하기에 충분한 정보를 포함합니다.
- 하이퍼미디어를 통한 애플리케이션 상태 전이 (HATEOAS): 클라이언트는 응답에 포함된 링크를 통해 사용 가능한 동작을 발견하고 API를 탐색할 수 있어야 합니다. 강력하지만, HATEOAS는 실제 구현에서 가장 잘 적용되지 않는 경우가 많습니다.
REST 실전 예시
전자상거래 애플리케이션을 만든다고 가정해 보겠습니다. 다음과 같은 RESTful 엔드포인트가 있을 수 있습니다:
모든 제품 조회
GET /api/v1/products
[
{
"id": 1,
"name": "Laptop",
"price": 1200.00
},
{
"id": 2,
"name": "Keyboard",
"price": 75.00
}
]
특정 제품 조회
GET /api/v1/products/1
{
"id": 1,
"name": "Laptop",
"price": 1200.00,
"description": "Powerful and lightweight laptop."
}
새 제품 생성
POST /api/v1/products
{
"name": "Mouse",
"price": 25.00
}
제품 업데이트
PUT /api/v1/products/2
{
"price": 80.00
}
제품 삭제
DELETE /api/v1/products/2
REST의 강점
- 단순함과 친숙함: REST는 널리 이해되고 채택되고 있습니다. 개발자는 HTTP 메서드와 상태 코드를 일반적으로 잘 알고 있습니다.
- 확장성: 무상태 특성 덕분에 높은 확장성을 가집니다.
- 캐시 가능성: 내장된 HTTP 캐시 메커니즘을 활용하면 성능을 크게 향상시킬 수 있습니다.
- 리소스 지향: 리소스를 URI에 명확히 매핑함으로써 API 구조가 직관적입니다.
REST의 약점
- 과다 조회 및 부족 조회: 클라이언트가 필요 이상으로 많은 데이터를 받는 경우(과다 조회)나, 필요한 모든 데이터를 얻기 위해 여러 번 요청해야 하는 경우(부족 조회)가 발생합니다.
- 엔드포인트 폭증: 애플리케이션이 성장함에 따라 엔드포인트 수가 관리하기 어려워질 수 있습니다.
- 버전 관리 어려움: API 버전을 관리하는 것이 복잡해질 수 있습니다.
GraphQL 소개: API를 위한 쿼리 언어
GraphQL은 API를 위한 쿼리 언어이자 기존 데이터를 사용해 해당 쿼리를 실행하는 런타임입니다. Facebook에서 개발했으며, REST보다 더 효율적이고 강력하며 유연한 대안을 제공합니다. REST와 달리 서버가 API 엔드포인트와 반환 데이터를 정의하는 반면, GraphQL은 클라이언트가 정확히 필요한 데이터를 지정할 수 있게 합니다.
GraphQL의 핵심 원칙
-
필요한 것만 요청하고 정확히 받아오기:
클라이언트는 서버에 필요한 필드만 명시한 쿼리를 보내어 과다 조회(over‑fetching)를 방지합니다. -
단일 엔드포인트:
일반적으로 GraphQL API는 모든 요청을 받는 단일 엔드포인트(예:/graphql)를 노출합니다. -
강력한 타입 스키마:
GraphQL API는 스키마에 의해 정의되며, 이는 조회 가능한 데이터의 청사진입니다. 스키마는 데이터 타입, 필드 및 그들 간의 관계를 정의합니다. -
계층적 데이터 조회:
쿼리는 계층 구조로 구성되어 요청하는 데이터의 구조를 그대로 반영합니다. -
뮤테이션과 구독:
뮤테이션은 클라이언트가 데이터를 수정할 수 있게 하고, 구독은 실시간 업데이트를 가능하게 합니다.
GraphQL 실전 예제
우리의 전자상거래 예시를 사용하면, GraphQL API는 일반적으로 단일 엔드포인트를 가집니다. 클라이언트는 필요한 데이터만 정확히 정의한 쿼리를 보냅니다.
제품 이름과 가격에 대한 쿼리
query {
products {
name
price
}
}
응답 (JSON)
{
"data": {
"products": [
{
"name": "Laptop",
"price": 1200.00
},
{
"name": "Keyboard",
"price": 75.00
}
]
}
}
우리는 name과 price 필드만 받아 과다 조회(over‑fetching)를 방지한 것을 확인할 수 있습니다.
특정 제품의 이름, 설명 및 판매자 이름에 대한 쿼리
Product 타입에 seller 필드가 있다고 가정합니다.
query {
product(id: "1") {
name
description
seller {
name
}
}
}
응답 (JSON)
{
"data": {
"product": {
"name": "Laptop",
"description": "Powerful and lightweight laptop.",
"seller": {
"name": "Tech Gadgets Inc."
}
}
}
}
이 쿼리는 관련 데이터(판매자 이름)를 한 번의 라운드‑트립으로 가져와, REST의 부족 조회(under‑fetching) 문제를 해결합니다.
새로운 제품을 생성하는 Mutation
mutation {
createProduct(input: { name: "Mouse", price: 25.00 }) {
id
name
}
}
응답 (JSON)
{
"data": {
"createProduct": {
"id": "3",
"name": "Mouse"
}
}
}
GraphQL의 강점
- Efficient Data Fetching – 클라이언트가 정확히 필요한 데이터를 요청하도록 함으로써 과다 가져오기와 부족 가져오기를 해결합니다.
- Improved Performance – 네트워크 요청 수가 줄어들어 특히 모바일 기기에서 더 빠른 사용자 경험을 제공할 수 있습니다.
- Strongly Typed Schema – 클라이언트와 서버 간에 명확한 계약을 제공하여 도구, 자동 완성 및 검증을 개선합니다.
- Flexibility for Clients – 프런트엔드 개발자가 사소한 조정마다 백엔드 API 변경 없이 데이터 요구 사항을 발전시킬 수 있게 합니다.
- Easier API Evolution – 기존 클라이언트를 깨뜨리지 않고 스키마에 새로운 필드를 추가할 수 있습니다.
GraphQL의 약점
- 복잡성 – REST에 비해 학습 곡선이 가파르며, 특히 쿼리 언어와 스키마 정의에 익숙하지 않은 사람들에게 어려울 수 있습니다.
- 캐싱 – 클라이언트‑사이드 캐싱이 REST의 HTTP 캐싱보다 복잡할 수 있고, 서버‑사이드 캐싱 역시 신중한 전략이 필요합니다.
- 속도 제한 및 보안 – 단일 엔드포인트와 유연한 쿼리 구조 때문에 세밀한 속도 제한 및 보안 조치를 구현하기가 더 까다롭습니다.
- 툴링 성숙도 – 빠르게 개선되고 있지만, 일부 툴링 측면은 잘 확립된 REST 생태계만큼 아직 성숙하지 않습니다.
언제 어떤 것을 선택할까
REST와 GraphQL 중 어느 것을 선택할지는 프로젝트의 구체적인 요구사항, 팀 전문성, 그리고 예상되는 성장에 따라 달라집니다.
REST를 선택해야 할 때
- 고정된 리소스 집합과 예측 가능한 데이터 요구사항을 가진 간단한 API를 구축하고 있을 때.
- 팀이 이미 REST 원칙과 도구에 매우 능숙할 때.
- 기존 HTTP 캐싱 메커니즘을 활용하는 것이 중요한 우선순위일 때.
- 과다 조회(over‑fetching)와 부족 조회(under‑fetching)가 크게 문제되지 않는 소규모~중간 규모 애플리케이션일 때.
- RESTful 관례에 크게 의존하는 기존 시스템과 통합해야 할 때.
GraphQL을 선택해야 할 때
- 애플리케이션이 복잡한 데이터 요구사항과 리소스 간 관계를 가지고 있을 때.
- 특히 모바일 클라이언트나 다양한 데이터 요구를 가진 앱에서 네트워크 성능을 최적화해야 할 때.
- 프론트엔드 개발자가 백엔드와 지속적인 협업 없이 UI와 데이터 조회를 빠르게 반복할 수 있도록 하고 싶을 때.
- 데이터 요구사항이 자주 변하거나, 클라이언트가 여러 소스에서 데이터를 집계해야 하는 마이크로서비스 아키텍처를 갖추고 있을 때.
- 강력한 타입 기반 API 계약과 그에 수반되는 도구들을 중시할 때.
결론
REST와 GraphQL은 모두 API를 구축하기 위한 강력한 아키텍처 스타일이며, 각각 고유한 장점과 단점이 있습니다.
- REST는 견고하고 널리 채택된 표준으로 남아 있으며, 단순한 API와 HTTP 내장 기능을 활용하는 데 이상적입니다.
- GraphQL은 현대적이고 유연하며 효율적인 접근 방식을 제공하며, 특히 정확한 데이터 가져오기와 성능 최적화가 중요한 복잡한 애플리케이션에 적합합니다.
근본적인 차이를 이해하고 프로젝트의 고유한 상황을 고려함으로써, 성공적인 API 아키텍처를 구축할 수 있는 정보에 입각한 결정을 내릴 수 있으며, 효율적인 데이터 교환과 긍정적인 개발자 경험을 보장합니다.
