콘텐츠로 바로가기

GraphQL vs REST

두 API 패러다임의 설계 철학 차이. 선택은 팀 규모·클라이언트 다양성·쿼리 복잡도에 달려 있다. GraphQL의 리졸버 구조상 관계 데이터 조회 시 N+1 쿼리 발생 위험.

Article
M

Me

hyunyoun's Blog

software-engineering-devopssoftware-engineeringdev-opsarchitecturedesigngraph-ql-vs-restdocumentationlearning10 min read

1. Overview

REST와 GraphQL의 역학(GraphQL vs REST)은 프론트엔드가 백엔드의 API 규격에 끌려다니던 고전적인 클라이언트-서버 종속성을 부수고, 클라이언트가 스스로 원하는 데이터의 생김새(Shape)를 결정하는 데이터 질의(Query)의 권력 역전을 해부합니다.

학습자는 화면 하나를 그리기 위해 User, Post, Comment API를 3번이나 호출해야 하는 REST의 **언더페칭(Under-fetching)**과, 이름만 필요한데 프로필 사진 URL까지 다 가져오는 **오버페칭(Over-fetching)**의 네트워크 비효율성을 뜯어봅니다. 나아가 단 하나의 엔드포인트(/graphql)를 뚫어놓고 프론트엔드가 정확히 필요한 필드만 조립해서 가져가는 GraphQL의 혁명적 자유도를 장악합니다. 마지막으로, 그 자유도의 대가로 백엔드 서버가 감당해야 하는 N+1 쿼리 문제와 캐싱(Caching)의 어려움을 방어하는 트레이드오프(Trade-off) 엔지니어링 역량을 확보합니다.

2. Scope & Boundaries

In-Scope

  • RESTful API Physics: 자원(Resource) 중심의 URI 설계, HTTP 메서드(GET, POST)와 상태 코드의 물리적 의미.
  • GraphQL Physics: 스키마(Schema) 주도 설계, 단일 엔드포인트, Query와 Mutation.
  • Over/Under Fetching: 네트워크 페이로드(Payload) 낭비와 폭포수(Waterfall) 네트워크 호출 문제.
  • The N+1 Problem: GraphQL이 DB를 찢어발기는 최악의 쿼리 지연 문제와 DataLoader(배치/캐싱) 방어술.

Out-of-Scope

  • Apollo Client/Server 구현체: 특정 JS 라이브러리의 상세 사용법 \rightarrow 언어/프레임워크 개별 문서로 위임.
  • gRPC와 Protobuf: 서버 간 고성능 통신 방식 \rightarrow HTTP/2 기반 내부 통신이므로 08. Network 시스템 아키텍처 파트로 분리.

Boundaries

  • REST vs GraphQL: "GraphQL이 REST보다 무조건 좋다"는 주니어의 환상을 부수어 버립니다. REST는 HTTP 표준 캐싱(CDN)을 100% 활용할 수 있고 설계가 직관적이지만 통신 횟수가 낭비됩니다. GraphQL은 프론트엔드의 생산성을 극대화하지만 백엔드의 복잡도(N+1 방어, CDN 캐싱 불가)를 극한으로 끌어올립니다. API를 '자원의 위치(REST)'로 볼 것인가, '데이터의 질의(GraphQL)'로 볼 것인가 하는 패러다임의 경계를 명확히 긋습니다.

3. Counterexample

  • 언더페칭의 폭포수 (REST): 모바일 앱 메인화면에 유저 정보와 최신 작성 글 5개, 달린 댓글 10개를 띄워야 합니다. REST API를 쓰다 보니 GET /users/1, 그 응답을 기다렸다가 GET /users/1/posts, 또 기다렸다가 GET /posts/5/comments를 3번 순차적으로 호출합니다. 네트워크를 3번 왕복(Waterfall)하느라 로딩이 5초나 걸립니다. 클라이언트가 여러 자원을 조합할 때 REST가 가진 태생적 비효율성입니다.
  • 데이터베이스 폭파 (GraphQL N+1): 주니어가 GraphQL을 도입하고 자랑스러워합니다. 프론트엔드가 게시글 10개와 각각의 작성자 정보를 쿼리했습니다. 백엔드는 게시글을 가져오는 쿼리 1번, 그리고 각 게시글의 작성자를 가져오기 위해 쿼리를 10번(총 11번) 날립니다. 만약 게시글이 10,000개였다면 DB에 10,001번의 쿼리가 난사되어 DB가 폭사(OOM)합니다. 자유도의 뒷면에 숨겨진 N+1의 파국입니다.

4. Prerequisites

  • HTTP 프로토콜 (Basic): HTTP Request/Response 구조, 메서드, 상태 코드. (08. Network)
  • 관계형 데이터베이스 (Basic): N+1 문제의 개념과 Join 메커니즘. (06. Data Management)

5. Learning Map

Sequence Core Cluster Objective & Description Evidence (BoK)
1 The Anatomy of REST 모든 것은 명사(자원)로 존재하며 동사(행위)는 HTTP 메서드에 위임하는, 가장 널리 쓰이는 표준 아키텍처의 철학을 쥡니다. P1
2 The Pain of Fetching "데이터가 너무 많거나(Over), 너무 부족해서(Under) API를 3번 더 호출해야 하는" 모바일 시대 REST의 물리적 한계를 뜯어봅니다. P5
3 GraphQL: Shift of Power 서버가 주는 대로 먹는 시대에서, 클라이언트가 "나는 이름과 이메일만 줘"라고 주문서를 써서 1번 만에 다 받아오는 권력 역전을 장악합니다. Industry
4 The Cost of Freedom (N+1) 자유를 준 대가로 백엔드 DB가 1만 번 쿼리 폭격을 맞아 죽는 현상을 막기 위해, DataLoader(배치/캐싱)라는 방어막을 확보합니다. Industry

6. Learning Topics

Basic

Core Topic 01: 명사와 동사의 엄격한 분리 (The Anatomy of REST)

  • Why to Learn: URL에 GET /getUserInfoPOST /deleteUser처럼 동사(행위)를 떡칠하는 근본 없는 API 설계를 부수고, 우주 공통의 규칙(HTTP Method)을 준수하는 아름다운 아키텍처를 짜기 위함입니다.
  • What to Learn:
    • Concepts: REST (REpresentational State Transfer), Resource (자원, 명사), HTTP Methods (GET, POST, PUT, DELETE), Stateless.
    • Skills: "사용자 번호 5번의 정보를 수정한다"는 요구사항을 POST /updateUser?id=5가 아니라 PUT /users/5라는 완벽한 RESTful URI로 번역하기.
  • How to Learn:
    • 1단계: 자원(Resource): REST에서 가장 중요한 건 '명사'입니다. URL은 오직 명사들의 계층 구조만 나타내야 합니다. /users/5/orders/2. "5번 유저의 2번 주문"이라는 리소스의 정확한 위치(Location)를 가리킵니다.
    • 2단계: 행위(Verb): 이 자원을 어떻게 할 것인가는 URL에 적지 않습니다. HTTP 프로토콜이 제공하는 메서드(동사)에 100% 위임합니다. 조회(GET), 생성(POST), 전체 수정(PUT), 삭제(DELETE).
    • 3단계: Self-descriptive (자기 서술성): 잘 짠 REST API는 설명서가 필요 없습니다. DELETE /users/5만 봐도 우주인조차 "5번 유저를 삭제하려무나"라고 이해할 수 있는 완벽한 규약의 아름다움을 해부합니다.
  • Implement: 엉망진창 API \rightarrow RESTful API 리팩터링 모사. Before: GET /makeComment?postId=3&text=hello (동사 떡칠, POST를 GET으로 쓰는 만행). Action: 명사 계층 구조와 HTTP 메서드 매핑. After: POST /posts/3/comments (Body: { "text": "hello" }). 완벽한 의미론적(Semantic) 분리로 프론트엔드의 예측 가능성을 극대화하는 시각화.

Core Topic 02: 페칭의 고통 (The Pain of Fetching)

  • Why to Learn: 잘 만들어진 REST API가, 화면이 복잡해지는 모바일 앱 환경에서 어떻게 '데이터 낭비'와 '로딩 속도 지연'의 주범으로 전락하는지 물리적 한계를 깨닫기 위함입니다.
  • What to Learn:
    • Concepts: Over-fetching (오버페칭), Under-fetching (언더페칭), Waterfall Network Requests (폭포수 네트워크 요청).
    • Skills: 프론트엔드가 겪는 "필요 없는 데이터를 버리는 낭비"와 "필요한 데이터를 모으기 위해 API를 5번 연달아 호출하는 고통"을 트래픽(Bytes)과 지연 시간(Latency)으로 수치화하여 증명하기.
  • How to Learn:
    • 1단계: Over-fetching: 홈 화면에 유저의 '이름'만 띄우면 됩니다. 그래서 GET /users/1을 호출했더니, 백엔드가 이름뿐 아니라 비밀번호 암호문, 집 주소, 가입일 등 50개의 컬럼을 무식하게 던져줍니다. 모바일 데이터 20KB가 낭비되었습니다.
    • 2단계: Under-fetching: 이번엔 유저의 최근 '주문 목록' 3개도 띄워야 합니다. GET /users/1 응답에는 주문 목록이 없습니다. 프론트엔드는 어쩔 수 없이 GET /users/1/orders를 두 번째로 호출합니다.
    • 3단계: Waterfall (폭포수): 만약 각 주문의 '상품명'을 알려면? GET /orders/1/items를 또 호출해야 합니다. 앞선 API가 끝나야만 다음 API를 부를 수 있는 직렬 호출이 네트워크 지연(Latency)을 3배로 늘려버리는 구조적 참사를 뜯어봅니다.
  • Implement: 모바일 화면 로딩 폭포수 모델링. 요구화면: "내 프로필(이름) + 최신 주문 1건(상품명)". REST의 늪: GET /me (300ms 대기) \rightarrow 받은 userId로 GET /users/{id}/orders?limit=1 (300ms 대기) \rightarrow 받은 orderId로 GET /orders/{id}/items (300ms 대기). 총 900ms 소요. 유저가 화면을 볼 때까지 1초나 걸리는 모바일 시대의 UX 붕괴 렌더링.

Practical

Core Topic 03: 권력의 역전 (GraphQL: Shift of Power)

  • Why to Learn: 백엔드가 주는 대로 받아먹어야 했던 프론트엔드의 설움을 끝내고, 프론트엔드가 마치 DB에 SQL을 날리듯 정확히 필요한 뼈대(Shape)만 조립해서 1번 만에 긁어오는 혁명적 자유도를 쥐기 위함입니다.
  • What to Learn:
    • Concepts: GraphQL, 단일 엔드포인트(Single Endpoint), Schema (스키마), Query (조회), Mutation (변경).
    • Skills: REST API 3개를 써야 했던 화면을 단 1개의 GraphQL 엔드포인트(/graphql)에 쿼리를 날려, 오버페칭과 언더페칭을 0으로 만드는 코드 설계하기.
  • How to Learn:
    • 1단계: 단일 엔드포인트: GraphQL은 URL이 /graphql 딱 하나입니다. 더 이상 자원의 계층(/users/1/orders)을 URL로 표현하지 않습니다.
    • 2단계: 주문서 작성 (Query): 클라이언트는 POST /graphql의 Body에 주문서(Query)를 씁니다. "나는 유저 이름(name)이랑, 걔의 주문 목록 중 첫 번째 거(orders(first<1>))의 상품명(itemName)만 줘."
    • 3단계: 맞춤형 응답 (Shape): 백엔드는 정확히 그 주문서대로만 조립해서 JSON으로 줍니다. 900ms 걸리던 폭포수 네트워크 통신이 단 1번(300ms)으로 끝납니다. 오버페칭(데이터 낭비)과 언더페칭(추가 호출)이 수학적으로 소멸하는 위대한 패러다임 전환을 해부합니다.
  • Implement: GraphQL Query vs REST Response 비교 렌더링. Query 작성:
    CODE
    query { user(id: 1) { name, orders(first: 1) { items { name } } } }
    서버의 응답: 정확히 요청한 형태 그대로 반환. 불필요한 addresspassword 필드는 단 1바이트도 내려오지 않음. 네트워크 페이로드(Payload) 크기를 1/10로 줄여 모바일 환경을 구원하는 시각화.

Advanced

Core Topic 04: 자유의 대가와 N+1 쿼리 (The Cost of Freedom)

  • Why to Learn: 프론트엔드에게 무한한 쿼리의 자유를 주었더니, 백엔드 서버가 DB를 1만 번씩 폭격(N+1 문제)하다 OOM으로 장렬하게 전사하는 참사를 막기 위함입니다.
  • What to Learn:
    • Concepts: The N+1 Problem, Resolver (리졸버 트리), DataLoader (배치/캐싱), HTTP Caching (CDN).
    • Skills: GraphQL 리졸버가 멍청하게 DB 쿼리를 N번 날리는 걸 가로채어(DataLoader), 1번의 IN (...) 쿼리로 묶어서(Batching) DB를 구원하는 백엔드 방어술 구축하기.
  • How to Learn:
    • 1단계: N+1의 재앙: 유저 10명의 정보를 가져오고(쿼리 1번), 각 유저의 프로필 사진을 가져옵니다. 프론트엔드는 쿼리 1번으로 보냈지만, 백엔드의 GraphQL 엔진(리졸버)은 유저 10명 각각에 대해 "프로필 사진 가져와" 쿼리를 10번 실행(N번)합니다. DB가 1+10=11번 비명을 지릅니다.
    • 2단계: 방어막 (DataLoader): 페이스북이 만든 DataLoader를 끼워 넣습니다. 리졸버들이 프로필 사진을 달라고 10번 아우성치면, 곧바로 DB에 가지 않고 잠깐(수 밀리초) 기다렸다가 ID 10개를 묶어서(Batch) SELECT * FROM profile WHERE user_id IN (1,2...10) 딱 1번의 쿼리로 해결합니다.
    • 3단계: 캐싱의 저주: REST는 URL이 다르기 때문에 브라우저나 CDN 단에서 GET 요청을 기가 막히게 캐싱(Caching)합니다. 하지만 GraphQL은 무조건 POST /graphql 1개뿐입니다. HTTP 표준 캐싱이 완전히 박살 납니다. 이 트레이드오프를 인정하고 인메모리 캐시(Apollo Cache)로 우회하는 엔지니어링의 타협점을 뜯어봅니다.
  • Implement: N+1 쿼리 폭주 \rightarrow DataLoader 방어 렌더링. Before: SELECT * FROM user LIMIT 5 (1번) \rightarrow SELECT * FROM post WHERE user_id = 1 ... user_id = 5 (5번). (총 6회 쿼리). Action: DataLoader Batching 적용. After: SELECT * FROM user LIMIT 5 (1번) \rightarrow SELECT * FROM post WHERE user_id IN (1,2,3,4,5) (1번). (총 2회 쿼리로 압축). 자유도를 허용하면서도 DB의 생명을 연장하는 경이로운 백엔드 아키텍처 시각화.

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry) Flags(core)
REST (Representational State Transfer) 모든 것을 자원(명사)으로 보고, 이를 조작하는 행위는 HTTP 메서드(GET, POST 등)에 100% 위임하는 웹의 가장 아름답고 보편적인 아키텍처 스타일입니다. 기본 자원 중심의 URL 설계 및 상태(State) 전송 HTTP Method / Stateless GraphQL (데이터 질의) REST는 단순한 'API 설계 규칙'이 아니라, 인터넷이 굴러가는 HTTP 표준과 캐시(CDN) 인프라를 100% 활용하기 위한 철학임 P1:CS2023 core
Over-fetching / Under-fetching (오버페칭/언더페칭) 오버페칭은 이름만 필요한데 50개의 컬럼을 다 받아오는 '낭비'이고, 언더페칭은 한 번의 호출로 부족해 연달아 API를 3번 부르는 '폭포수 지연'입니다. 권장 REST의 물리적 한계 및 네트워크 비효율성 지적 Payload / Latency N+1 Problem (백엔드 관점) 둘 다 모바일(저전력/느린 네트워크) 환경에서 배터리와 로딩 속도를 갉아먹는 주범이며, GraphQL이 탄생하게 된 직접적 원인임 P5:SFIA core
GraphQL "주는 대로 먹어"라는 백엔드의 통제권을 빼앗아, 프론트엔드가 정확히 필요한 데이터의 형태(Shape)를 쿼리로 작성해 단 1번 만에 받아오는 질의어(Query Language)입니다. 실무 단일 엔드포인트 기반의 맞춤형 데이터 패칭 Schema / Resolver / Mutation SQL (데이터베이스 쿼리) SQL이 DB에 날리는 쿼리라면, GraphQL은 프론트엔드가 웹서버(API)에 날리는 쿼리임. 백엔드는 이 쿼리를 파싱해서 다시 진짜 DB SQL로 바꿔야 함 Industry core
DataLoader (데이터 로더) GraphQL 특유의 깊은 트리(Tree) 구조 탐색 때문에 발생하는 최악의 DB 쿼리 폭격(N+1 문제)을 1개의 IN 쿼리로 묶어서(Batching) 방어하는 구원자입니다. 심화 N+1 쿼리 해결 및 백엔드 성능(OOM) 방어 N+1 Problem / Batching / Caching Lazy Loading (지연 로딩) ORM의 지연 로딩 때문에 N+1이 터지는데, DataLoader는 이 지연 로딩 요청들을 찰나의 시간(수 ms) 동안 모아서 한방에 쏘는 '모아 쏘기' 기술임 Industry core

8. References

Primary

  • [P1] CS2023 - Software Engineering (SE) - Web and Mobile Development (REST APIs and GraphQL)
  • [P5] SFIA - System Design (SYMD) - API design and interface specification

Secondary

  • [RESTful Web Services] Leonard Richardson - Resource-Oriented Architecture
  • [Learning GraphQL] Eve Porcello, Alex Banks - Graph Theory, Resolvers, DataLoader

Industry

  • [GraphQL.org] - Thinking in Graphs, DataLoader
  • [Roy Fielding's Dissertation] - Architectural Styles and the Design of Network-based Software Architectures (REST)

9. Final Checklist

Primary

  • 사용자 정보를 조회할 때 URL에 GET /getUserInfo라고 명명하는 것이 왜 REST의 원칙(명사와 동사의 분리)을 파괴하는 행위인지 지적하고, 이를 완벽한 RESTful URI 형식으로 리팩터링할 수 있는가?
  • 모바일 앱 화면 하나를 그리기 위해 REST API를 직렬로 3번 호출하는 폭포수(Waterfall) 모델의 한계(Under-fetching)와, 불필요한 50개 컬럼을 통째로 받아오는 낭비(Over-fetching)를 네트워크 대역폭 관점에서 설명할 수 있는가?

Secondary

  • REST API가 여러 개의 엔드포인트(/users, /posts)를 가지는 반면, GraphQL은 단일 엔드포인트(/graphql)만을 제공하여 프론트엔드가 필요한 필드만 쿼리(Query)로 선별해 네트워크 페이로드(Payload)를 압축하는 권력의 역전을 논증할 수 있는가?
  • HTTP 표준 캐싱 시스템(CDN, 브라우저 캐시)이 URL 리소스 단위로 동작하는 REST API에서는 완벽하게 기능하지만, 오직 POST 메서드로 단일 엔드포인트를 사용하는 GraphQL에서는 캐싱 인프라가 무력화되는 치명적 트레이드오프를 설명할 수 있는가?

Industry

  • 프론트엔드 개발자가 자유롭게 작성한 GraphQL 쿼리가 백엔드의 리졸버(Resolver) 트리 탐색을 거치며 1개의 메인 쿼리와 N개의 서브 쿼리(N+1 문제)로 폭증해 데이터베이스를 마비시키는 연쇄 장애 원리를 추적할 수 있는가?
  • N+1 쿼리 문제를 방어하기 위해 Facebook이 고안한 DataLoader 아키텍처를 적용하여, N번의 개별 DB 조회 요청을 수 밀리초 동안 대기시켰다가 1번의 IN (...) 배치(Batching) 쿼리로 묶어 처리하는 성능 최적화 파이프라인을 설계할 수 있는가?

Architecture & Documentation

4 / 5