관리 메뉴

Jerry

#6. REST API vs GraphQL 본문

CS/Terminology

#6. REST API vs GraphQL

juicyjerry 2025. 3. 5. 16:37

 

REST API와 GraphQL은 클라이언트(web app)와 서버가 데이터를 주고 받는 방식다.


REST API란?

REST는 Representational State Transfer의 약자로,

웹 어플리케이션에서 리소스를 구조화하고 접근하는 아키텍처 스타일(방식)이다. 

 

이런 방식(아키텍처 스타일)이지만, 단순한 API 규격이 아니라 여러 원칙을 따르는 방식이다

- REST의 원칙으로 Uniform Interface, Stateless, Cacheable, Layered System 등이 존재

Uniform Interface
- 일관된 인터페이스를 통해 클라이언트와 서버 간의 상호작용 단순화
- HTTP 메서드를 통한 리소스 조작 방식이 일관적이어야 한다
=> RESTful Api는 간단하고 예측 가능한 구조로 설계됨

Stateless
- 각 요청은 독립적이어야 함
- 서버는 클라이언트 상태를 저장하지 않음
=> 클라이언트가 서버에 요청을 보낼 때마다 필요한 모든 정보를 요청에 포함해야 하며, 서버는 이런 클라이언트의 이전 요청에 대한 정보를 기억할 필요없음 
=>> 서버 부담 줄어들고 확장성 높아짐

Cacheable
- RESTful 응답은 캐시 가능해야 하며, 클라이언트는 서버에서 받은 데이터를 캐시할 수 있음
=> 서버 부하 감소, 데이터 변하지 않았다면 빠르게 응답 가능

Layered System
- REST 아케텍처는 여러 계층으로 구성
- 클라이언트는 중간 서버(로드 블런서, 캐시 서버 등)에 의존할 수 있으며, 각 서버는 독립적 기능 수행
=> 시스템의 확장성과 보안 향상

 

 

 

HTTP 기반으로 동작하며, 특정 HTTP 메서드를 사용하여 서버와 클라이언트 간 데이터를 주고 받는다.

예를 들어,

GET -> 데이터 요청

POST -> 데이터 생성

PUT/PATCH -> 데이터 갱신

DELETE -> 데이터 제거

 

* PUT과 PATCH 차이점

- PUT : 전체 리소스를 대체 (모든 필드 덮어쓰기)

- PATCH : 일부 필드만 업데이트 (부분 수정)

 

 

[Example] REST API : 요청(Request)

GET /users/1

 

[Example] REST API : 응답 (Response)

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "posts": [1, 2, 3] 
}

 

 

[Example] REST API : post 관련 정보가 포함되지 않아 추가 요청을 아래와 같이 해야한다 (Under-fetching)

GET /posts/1

 

 

 

REST의 주요 특징 

REST는 "리소스(Resource)" 중심으로 

/user, /products와 같이 각기 다른 리소스 요청에 대한 endpoint(URL)를 사용한다 

 

상태를 유지하지 않는 무상태(stateless) 아키텍처를 따른다

- 각 요청(Request)은 독립적이고, 서버에 저장된 클라이언트 상태(state) 정보를 요구하지 않는다

- 즉, 클라이언트의 이전 요청에 대한 정보를 서버가 기억하지 않는다

- 따라서, 클라이언트는 모든 요청에서 필요한 정보를 포함해야 한다

데이터 교환(exchange)에 대해 JSON 또는 XML을 사용한다

 

 

 

REST의 제한 사항

Over-fetching : 과다 요청

- 사용자가 필요한만큼 이상의 데이터를 받을 수 있다

- 특정 사용자 name만 필요하지만, /users/1을 요청하면 모든 정보(id, email, address 등)를 포함하여 응답하는 경우

 

Under-fetching : 과소 요청

- 하나의 요청에서 원하는 정보를 충분히 받지 못 해 추가 요청이 필요할 수 있다

- /users/1 요청 시, post 정보가 포함되지 않아서, 추가적으로 /posts/1 API를 또 호출해야 하는 경우

 

버전 관리 이슈 : 버전 변경 시 새로운 endpoint를 만들어야 하는 경우가 많음

/api/vi/users vs /api/v2/users

 

즉, API 변경이 생기면 새로운 URL을 만들거나 기존 API를 업데이트해야 하므로 유지보수의 번거로움과 같은 불편함이 존재

 

 

 

"RESTful하다"는 말의 의미

- REST 아키텍처 원칙을 따르다

- REST 스타일에 맞춰 설계되다

REST의 주요 원칙
1. 자원 기반
- 자원은 고유한 URI로 식별
- 작업은 HTTP 메서드를 통해 수행

2. 상태 비저장
- 각 요청은 독립적 처리
- 서버는 클라이언트 상태 저장 안 함

3. 표준 HTTP 메서드 사용
- HTTP 메서드 적절하게 사용하여 자원에 대한 작업 정의

4. 계층화된 시스템
- RESTful 시스템은 여러 계층으로 나눠져 있을 수 있음(유연성, 확장성)
  ex) 클라이언트, API 게이트 웨이, 백엔드 서버, 데이터베이스 서버
- 클라이언트는 서버의 내부 구현에 대해 알지 못 함
- 제공된 API만을 통해 시스템과 상호작용

5. 캐시 가능
- 응답은 캐시 가능해야 함

 

 

 

 

 

 

 


 

 

 

 

 

GraphQL란?

페이스북(현 메타)에서 개발한 APIs를 위한 쿼리 언어이자, 데이터 요청을 처리하는 런타임(runtime)이다.

런타임 
- 프로그램이 실행되는 동안의 환경

GraphQL에서 "런타임"
- 클라이언트의 쿼리를 받아 처리하고, 필요한 데이터를 효율적으로 가져오는 역할

 

 

REST API처럼 고정된 여러 endpoint 대신, 하나의 단일 endpoint(/graphql)로 요청한다

사용자가 원하는 데이터가 무엇인지 정확하게 명시한다

클라이언트가 원하는 데이터만 선택적 요청이 가능해, Over-fetching 문제를 해결한다

REST API는 주로 offset-limit 페이징 방식을 사용하지만, GraphQL에서는 cursor-based pagination을 활용하는 경우가 많다.

offset-limit pagination vs cursor-based pagination

1.  offset-limit pagination 
- 특정 범위의 데이터를 요청할 때 사용
- 페이지 번호와 페이지 크기를 전달하여 데이터를 가져오는 방식
- 간단하지만 데이터의 추가나 삭제가 발생할 때 데이터의 일관성이 깨질 수 있음

2. cursor-based pagination
- 데이터의 "커서"를 기반으로 페이지 나누는 방식
- 커서는 데이터베이스 내에서 특정 위치를 나타내느 식별자(고유 ID;기준 데이터의 ID, *타임스탬프)
- 커서는 특정 항목을 기준으로 데이터를 반환
- 데이터 추가나 삭제해도 일관성을 유지
- 효율적 데이터 페이징 지원
* 특정 사건이나 데이터가 발생한 시간을 기록한 값
ex) 유닉스 타임스탬프 : 1617411465 | ISO 8601 : 2025-03-05T10:30:00Z


{
  "data": [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"}
  ],
  "pageInfo": {
    "hasNextPage": true,
    "cursor": "abc123"
  }
}​

 

 

 

 

GraphQL의 주요 특징

유연한 쿼리 : 클라이언트는 필요한 필드에 한해서 요청이 가능하다

 

중첩된 데이터에 대한 한 가지 요청을 한다 (즉, 여러 번의 REST 요청을 피한다)

- REST API에서는 /users/1을 호출한 후 /posts/1을 추가 호출해야 하지만,GraphQL은 한 번의 요청으로 연관된 데이터를 가져올 수 있다

- REST API에서는 여러 번의 HTTP 요청이 필요하지만, GraphQL은 한 번의 요청으로 데이터를 반환할 수 있다

 

강력한 형식의 스키마를 정의하여 데이터와 관계를 이용할 수 있다

- 스키마는 데이터 구조와 관계를 정의하는 명세

- GraphQL에서는 클라이언트가 요청할 수 있는 데이터 형태를 스키마로 정의

=> 클라이언트와 서버 간의 데이터 계약을 명확히 할 수 있음
      ex) 사용자의 정보와 게시글의 관계를 정의한 스키마

 

 

 

 

GraphQL의 제한 사항

REST와 비교했을 때, 더 복잡하다

GraphQL를 사용하려면 전용 GraphQL 서버가 필요하다

클라이언트가 원하는 데이터를 세밀하게 지정할 수 있는 장점이 있지만, 쿼리가 복잡해질질수록 서버의 부하가 증가할 수 있다

(=복잡한 쿼리문으로 서버에 부하를 줄 가능성이 존재한다) 
이를 방지하기 위해 쿼리 복잡도를 제한(Query Complexity)하거나, Depth Limit(쿼리 깊이 제한) 기법 적용 가능
예를 들어, 쿼리 최적화나 DataLoader 같은 기법을 통해 문제 해결할 수 있음

Query Complexity
- 쿼리 복잡도는 요청되는 쿼리의 복잡성(중첩된 필드나 관계)을 측정하는 것
- 복잡한 쿼리는 서버에 부하를 줄 수 있기 때문에, 서버는 이 복잡도를 제한하여 성능 최적화할 수 있음

Depth Limit
- 쿼리의 중첩 깊이를 제한하는 기법
- 복잡한 쿼리는 서버에 부하를 줄 수 있기 때문에, 서버는 이 복잡도를 제한하여 성능 최적화할 수 있음

DataLoader
- 여러 개의 데이터 요청을 배치(batch)로 묶어 처리하는 기법
- 중복된 데이터 요청 최소화, 효율적 데이터 로드 가능

 

 

중첩된 데이터를 지나치게 많이 요청하면 N + 1 문제(SQL N + 1 Problem)가 발생할 수 있다

[N + 1 문제 예시]

GraphQL에서 관계형 데이터를 요청할 때,
중첩된 데이터를 조회하면 여러 개의 추가적인 데이터 요청이 발생할 수 있음.
예를 들어, 사용자 목록을 조회하면서 각 사용자의 게시글을 가져오면:

1. `users` 쿼리 요청 → 사용자 10명 조회
2. 각 사용자에 대해 `posts` 쿼리 10번 추가 요청 발생 → 총 11번의 요청(N+1 문제)

N + 1 문제 해결을 위해 GraphQL에서는 DataLoader와 같은 배치 로딩 기법을 적용할 수 있다.
* DataLoader는 데이터를 한 번에 모아서 처리하는 방식

 

 

 

 

[Example] GraphQL : 쿼리(query)

{
  user(id: 1) {
    name
    email
    posts {
      title
      content
    }
  }
}

 

 

[Example] GraphQL : 응답(response)

{
  "data": {
    "user": {
      "name": "Alice",
      "email": "alice@example.com",
      "posts": [
        {
          "title": "GraphQL vs REST",
          "content": "GraphQL is more flexible..."
        }
      ]
    }
  }
}

 

 

 

 

그렇다면, 언제 어떻게 쓰는 것이 좋을까?

특징 REST API GraphQL
적합한 모델 간단하고 표준화된 API 유연한 맞춤형 API
데이터 가져오기 여러 요청 필요 한 번의 요청으로 필요한 데이터만
퍼포먼스 비효율적인 측면 가능성 존재(Over-fetching, Under-fetching) 필요한 데이터만 가져와 효율적
버전 관리 API 버전 관리 필요 (/v1, /v2) ****스키마 변경 시 API 자체 변경 없이 사용 가능
캐싱(Caching) * HTTP 캐싱(304 Not Modified, ETag) 가능 ** 요청마다 변동이 많아 기본적인 HTTP 캐싱이 어려움
학습 곡선 직관적이고 배우기 쉬움 *** 초반 학습이 필요하지만 강력한 기능 제공
에러 핸들링 방식 HTTP 상태 코드 활용 응답 데이터 내에서 에러 정보 포함
도입 난이도 직관적이라 바로 사용하기 쉬움 초기 학습 필요하고 서버 구현이 더 복잡함

 

* REST API는 HTTP 기반이므로 브라우저 캐싱 및 CDN(Content Delivery Network)과의 연동이 쉽고, 성능 최적화에 유리

304 Not Modified
- 클라이언트가 서버에 요청을 보낼 때, 이전에 받은 데이터와 비교하여 변경되지 않은 경우,
서버는 304 상태 코드 반환하며, 클라이언트는 데이터를 다시 다운로드하지 않음
- 이를 통해 네트워크 트래픽 줄일 수 있음

ETag
- 리소스 버전을 나타내는 식별자
- 클라이언트는 ETag를 통해 서버에서 리소스가 변경되었는지 확인 가능
 

 

** GraphQL은 별도의 캐싱 레이어(Apollo Client, DataLoader 등)를 적용하면 해결할 수 있음.

즉, GraphQL은 기본적으로 HTTP 캐싱이 어렵지만, Apollo Client나 Relay 같은 도구를 활용하면 캐싱 최적화가 가능

*** GraphQL은 Schema를 직접 정의해야 하고, Resolver를 구현해야 하기 때문에 초기 설정이 REST API보다 복잡할 수 있음 

**** GraphQL에서는 새로운 필드를 추가하거나 기존 필드를 변경해도 클라이언트가 요청하는 필드를 그대로 가져오기 때문에 버전 관리가 필요 없음

 

 

 

 

RESP API와 GraphQL의 보안

- GraphQL의 경우, 클라이언트가 원하는 데이터를 자유롭게 요청할 수 있어서 정보 과다 노출(Excessive Data Exposure) 문제가 발생할 수 있지만 REST API는 여러 endpoint로 나뉘어 관리해, 각 endpoint가 보안에 위협에 노출될 가능성이 있음

 

- GraphQL은 클라이언트가 원하는 데이터 필드를 직접 지정할 수 있어, 인증이 제대로 관리되지 않으면 민감한 정보까지 노출될 위험이 존재

- 이를 방지하기 위해 필드별 접근 제어(Authorization)를 철저히 설정해야 한다

- *Rate Limiting이나 Query Complexity 제한을 적용하면 과부하를 방지할 수 있음

* 속도 제한: 서버나 네트워크 리소스에 대한 과도한 요청 방지, 서비스 안정성과 성능 유지를 위해 특정 시간 동안 요청 제한하는 기법

 

- REST API는 endpoint마다 접근 권한(접근 제어)을 설정할 수 있어서 GraphQL보다 보완 관리가 상대적으로 직관적이다

- endpoint이 많을수록 공격 표면이 넓어지는 측면도 존재

- Open API로 만들면 CSRF(Cross-Site Request Forgery)난 CORS 문제도 신경이 필요

CSRF
- 악의적인 사이트가 사용자 속여 사용자가 의도치 않은 요청을 보냄으로 발생하는 공격
- 인증 토큰을 요청에 포함하거나 동일 출처 정책을 활용하여 방지'

CORS
- 다른 도메인에서 리소스를 요청할 때 발생할 수 있는 보안 문제를 해결하는 방식
-  브라우저는 다른 도메인에서의 요청을 제한할 수 있는데, 이를 해결하려면 서버에서 CORS 헤더를 설정해야 함

 

 

REST API의 경우, OAuth, JWT, API Key 등을 활용한 인증 방식이 일방적

GraphQL의 경우, 필드별 접근 제어Persisted Queries를 활용해 불필요한 쿼리 요청 방지할 수 있음

OAuth
- 사용자 인증 및 권한 부여를 위한 표준 프로토콜
- 사용자가 비밀번호를 공유하지 않고도 애플리케이션에 접근할 수 있도록 함
- 주로 소셜 로그인에서 사용

JWT
- JSON WEB TOKEN
- 인증 정보를 JSON 형태로 안전하게 전송하기 위한 토큰 기반 인증 방식
- 클라이언트와 서버 간의 인증 상태 유지할 수 있음

API Key 
- 애플리케이션이 API에 접근할 수 있도록 고유한 키를 사용하는 인증 방식

Persisted Queries
- 쿼리를 서버에 미리 저장해두고, 클라이언트는 해당 쿼리의 식별자만 전송하여 요청하는 방식
- 쿼리 보안 강화, 네트워크 부하 감소

 

 

 

 

 

 

REST API를 사용하면 좋은 경우

- 단순 CRUD API 만들 때

- API 표준을 따르고, 쉽게 캐싱 적용하고 싶을 경우

- 기존 시스템과 쉽게 통합하고 싶을 때

- 마이크로서비스 아키텍처에서 서비스 간 통신할 때도 적합

마이크로서비스 아키텍처
- 애플리케이션을 작은 독립적인 서비스로 분할하여 관리하는 아케텍처 스타일
- 각 서비스는 독립적으로 배포, 확장, 관리될 수 있음
- 서로 통신하기 위해 REST API 또는 GraphQL 사용 가능
- 대규모 시스템에서 효율적인 확장과 유지보수 가능하게 함

예시1 : 전자상거래 플랫폼
- 주문 서비스, 상품 서비스, 결제 서비스, 회원 서비스, 추천 서비스
- 위 각 서비스는 독립적으로 배포되고, HTTP 기반의 RESTful API나 메시징 시스템을 통해 서로 통신
- 주문 서비스는 결제 서비스와 통신하여 결제 정보를 처리하고, 상품 서비스와 통신하여 재고를 관리함

예시2 : 소셜 네트워크 서비스
- 사용자 서비스, 게시물 서비스, 알림 서비스, 검색 서비스
- 게시물 서비스는 검색 서비스와 통신하여 특정 게시물을 검색하고, 사용자 서비스와 통신하여 사용자의 프로필을 가져옴

장점
- 독립적 배포 : 한 서비스의 변경이 다른 서비스에 영향 미치지 않음
- 확정성 : 각 서비스는 독립적 확장이 가능해, 트래픽이 많은 서비스만 확장 가능
- 기술 스택 독립성 : 각 서비스는 독립적으로 개발되기 때문에, 서로 다른 기술 스택 사용할 수 있음
- 유지보수 용이 : 작은 단위로 분리된 서비스들은 독립적으로 수정 및 테스트 가능

단점
- 서비스 간 통신 및 데이터 일관성 관리
- 배포 및 모니터링에서의 복잡성 증가

 

 

 

 

GraphQL을 사용하면 좋은 경우

- Over-fetching, Under-fetching을 방지하고 싶을 때

- 프론트엔드에서 맞춤형 데이터 요청 필요할 때

- 모바일 앱처럼 네트워크 요청을 최소화해야 할 때

- 여러 클라이언트(Web, Mobile, IoT)에서 다양한 데이터 요구가 있을 때 특히 강점이 될 수 있음

반응형

'CS > Terminology' 카테고리의 다른 글

#8. SSR vs CSR  (0) 2025.03.10
#7. 호이스팅, 전역 컨텍스트, 클로저  (2) 2025.03.06
#4. REST API란?  (0) 2025.02.25
npm init -y 란?  (1) 2021.08.11
#9. Method vs Function in Javascript  (0) 2021.04.09