일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 리덕스
- 4주 프로젝트
- HTTP
- 알고리즘
- 2주 프로젝트
- codestates
- 리액트
- LeetCode
- python
- 손에 익히며 배우는 네트워크 첫걸음
- til
- 회고
- SQL 고득점 Kit
- 타임어택
- 파이썬
- 코드스테이츠
- 타입스크립트
- 자바스크립트
- programmers
- 렛츠기릿 자바스크립트
- javascript
- js
- 제로초
- 프로그래머스
- 코어 자바스크립트
- 토익
- 백준
- 리트코드
- 정재남
- 타입스크립트 올인원
- Today
- Total
Jerry
#6. REST API vs GraphQL 본문
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 |