REST vs GraphQL 완벽 비교: 장단점, 데이터 모델링, 유지보수성부터 실무 선택 기준까지

REST vs GraphQL 완벽 비교: 장단점, 데이터 모델링, 유지보수성부터 실무 선택 기준까지

“REST와 GraphQL의 차이는?”, “왜 GraphQL을 도입하나요?”, “Over-fetching이 뭔가요?” — REST API의 한계를 보완하기 위해 등장한 GraphQL은 면접에서도 REST와의 비교 질문으로 자주 출제된다.


1. REST API 요약

1.1 REST의 핵심 특징

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────────────────────┐
│                       REST 핵심                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  REST = 리소스 중심의 API 설계 방식                                 │
│                                                                     │
│  • 리소스를 URL로 표현  : /api/users/1                              │
│  • HTTP 메서드로 행위   : GET, POST, PUT, DELETE                    │
│  • 엔드포인트마다 고정된 응답 구조                                  │
│  • 서버가 응답 형태를 결정                                          │
│                                                                     │
│  예:                                                                │
│  GET  /api/users       → 전체 사용자 목록                          │
│  GET  /api/users/1     → 1번 사용자 상세                           │
│  POST /api/users       → 사용자 생성                               │
│  GET  /api/users/1/orders → 1번 사용자의 주문 목록                 │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.2 REST의 한계

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
┌─────────────────────────────────────────────────────────────────────┐
│                    REST의 한계                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ① Over-fetching (과다 조회)                                        │
│     클라이언트가 이름만 필요한데 서버가 전체 필드를 반환            │
│                                                                     │
│     GET /api/users/1                                                │
│     응답: { id, name, email, phone, address, createdAt, ... }      │
│     실제 필요: name만                                               │
│     → 불필요한 데이터 전송 = 네트워크 낭비                          │
│                                                                     │
│  ② Under-fetching (부족 조회)                                       │
│     하나의 화면에 필요한 데이터를 여러 번 요청해야 함               │
│                                                                     │
│     사용자 프로필 페이지:                                           │
│     1) GET /api/users/1          → 사용자 기본 정보                │
│     2) GET /api/users/1/orders   → 주문 목록                      │
│     3) GET /api/users/1/reviews  → 리뷰 목록                      │
│     → 3번의 API 호출 = 네트워크 라운드트립 증가                     │
│                                                                     │
│  ③ 엔드포인트 폭증                                                  │
│     화면별로 다른 데이터가 필요하면 엔드포인트가 계속 늘어남        │
│     /api/users/simple, /api/users/detail, /api/users/for-admin     │
│                                                                     │
│  ④ API 버전 관리                                                    │
│     /api/v1/users, /api/v2/users → 버전이 늘어남                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2. GraphQL이란

2.1 핵심 개념

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────────────────┐
│                     GraphQL 핵심                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  GraphQL = 클라이언트가 필요한 데이터를 직접 요청하는 쿼리 언어     │
│                                                                     │
│  • 단일 엔드포인트 : POST /graphql                                  │
│  • 클라이언트가 응답 형태를 결정                                    │
│  • 스키마(Schema)로 API 타입 정의                                   │
│  • 필요한 필드만 선택적으로 요청                                    │
│                                                                     │
│  Facebook이 2015년 공개 (모바일 앱 최적화 목적)                     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.2 기본 동작

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
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────────────┐
│                  GraphQL 요청/응답 예시                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [요청 - 클라이언트가 필요한 필드만 지정]                           │
│                                                                     │
│  POST /graphql                                                      │
│  {                                                                  │
│    query {                                                          │
│      user(id: 1) {                                                  │
│        name                                                         │
│        email                                                        │
│        orders {                                                     │
│          product                                                    │
│          price                                                      │
│        }                                                            │
│      }                                                              │
│    }                                                                │
│  }                                                                  │
│                                                                     │
│  [응답 - 요청한 필드만 반환]                                        │
│                                                                     │
│  {                                                                  │
│    "data": {                                                        │
│      "user": {                                                      │
│        "name": "김개발",                                            │
│        "email": "dev@example.com",                                  │
│        "orders": [                                                  │
│          { "product": "키보드", "price": 89000 },                   │
│          { "product": "마우스", "price": 45000 }                    │
│        ]                                                            │
│      }                                                              │
│    }                                                                │
│  }                                                                  │
│                                                                     │
│  → 한 번의 요청으로 사용자 + 주문 데이터를 함께 조회               │
│  → 불필요한 phone, address, createdAt 등은 응답에 포함되지 않음    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3. GraphQL 핵심 구성 요소

3.1 Schema, Query, Mutation, Subscription

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
32
33
34
35
36
┌─────────────────────────────────────────────────────────────────────┐
│                 GraphQL 핵심 구성 요소                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [Schema - 타입 정의]                                               │
│  type User {                                                        │
│    id: ID!                                                          │
│    name: String!                                                    │
│    email: String!                                                   │
│    orders: [Order!]!                                                │
│  }                                                                  │
│                                                                     │
│  type Order {                                                       │
│    id: ID!                                                          │
│    product: String!                                                 │
│    price: Int!                                                      │
│  }                                                                  │
│                                                                     │
│  [Query - 읽기 (GET과 유사)]                                       │
│  type Query {                                                       │
│    user(id: ID!): User                                              │
│    users: [User!]!                                                  │
│  }                                                                  │
│                                                                     │
│  [Mutation - 쓰기 (POST/PUT/DELETE와 유사)]                        │
│  type Mutation {                                                    │
│    createUser(name: String!, email: String!): User!                 │
│    deleteUser(id: ID!): Boolean!                                    │
│  }                                                                  │
│                                                                     │
│  [Subscription - 실시간 (WebSocket 기반)]                           │
│  type Subscription {                                                │
│    orderCreated(userId: ID!): Order!                                │
│  }                                                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 Resolver

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
┌─────────────────────────────────────────────────────────────────────┐
│                    Resolver 역할                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Resolver = 스키마의 각 필드에 대해 데이터를 가져오는 함수          │
│                                                                     │
│  클라이언트 요청                                                    │
│       │                                                             │
│       ▼                                                             │
│  GraphQL 엔진 (쿼리 파싱)                                           │
│       │                                                             │
│       ▼                                                             │
│  ┌──────────────────────────────────────────┐                       │
│  │ Resolver: user(id)                       │                       │
│  │   → UserService.findById(id)             │                       │
│  │                                          │                       │
│  │ Resolver: user.orders                    │                       │
│  │   → OrderService.findByUserId(userId)    │                       │
│  └──────────────────────────────────────────┘                       │
│       │                                                             │
│       ▼                                                             │
│  응답 조립 → 클라이언트에 반환                                      │
│                                                                     │
│  → REST의 Controller + Service 역할을 Resolver가 담당               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4. REST vs GraphQL 핵심 비교

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
32
33
┌─────────────────────────────────────────────────────────────────────┐
│              REST vs GraphQL 비교표                                   │
├──────────────────┬──────────────────────┬────────────────────────────┤
│ 항목             │ REST                 │ GraphQL                    │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 엔드포인트       │ 리소스마다 별도 URL  │ 단일 엔드포인트            │
│                  │ /users, /orders      │ /graphql                   │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 데이터 결정권    │ 서버가 결정          │ 클라이언트가 결정          │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ Over-fetching    │ 발생 (전체 필드 반환)│ 없음 (필요 필드만)         │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ Under-fetching   │ 발생 (여러 번 호출)  │ 없음 (한 번에 조합)        │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ HTTP 메서드      │ GET/POST/PUT/DELETE  │ 주로 POST                  │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 상태 코드        │ 200/201/404/500 등   │ 거의 항상 200              │
│                  │ (의미 있는 코드)     │ (에러도 200 + errors 필드) │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 캐싱             │ HTTP 캐싱 자연스러움 │ 캐싱 어려움 (POST 기반)    │
│                  │ (GET 캐싱, CDN)      │ 별도 캐싱 전략 필요        │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 파일 업로드      │ multipart/form-data  │ 별도 처리 필요             │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 버전 관리        │ /v1/, /v2/           │ 스키마 확장 (필드 추가)    │
│                  │ URL 버전 관리 필요   │ 버전 없이 진화 가능        │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 학습 곡선        │ 낮음                 │ 보통~높음                  │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 에러 처리        │ HTTP 상태 코드       │ errors 배열 in 응답 body   │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 도구/생태계      │ 매우 성숙            │ 빠르게 성장 중             │
└──────────────────┴──────────────────────┴────────────────────────────┘

5. GraphQL의 장단점

5.1 장점

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
┌─────────────────────────────────────────────────────────────────────┐
│                   GraphQL 장점                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ① Over-fetching / Under-fetching 해결                              │
│     • 필요한 필드만 정확히 요청 → 네트워크 효율                     │
│     • 한 번의 요청으로 여러 리소스 조합 가능                        │
│                                                                     │
│  ② 강타입 스키마 (Type System)                                      │
│     • 스키마가 곧 API 문서 (Self-documenting)                       │
│     • Introspection으로 스키마 자동 탐색                            │
│     • 프론트-백 간 계약(Contract)이 명확                            │
│                                                                     │
│  ③ API 버전 관리 불필요                                              │
│     • 기존 필드 유지 + 새 필드 추가 (@deprecated로 폐기 알림)       │
│     • 클라이언트는 필요한 필드만 요청하므로 호환성 유지             │
│                                                                     │
│  ④ 프론트엔드 생산성 향상                                           │
│     • 백엔드 API 변경 없이 프론트에서 자유롭게 데이터 조합         │
│     • 화면별 맞춤 API 불필요                                        │
│                                                                     │
│  ⑤ 실시간 지원 (Subscription)                                       │
│     • WebSocket 기반 실시간 데이터 푸시 내장                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

5.2 단점

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
┌─────────────────────────────────────────────────────────────────────┐
│                   GraphQL 단점                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ① HTTP 캐싱 어려움                                                 │
│     • 모든 요청이 POST /graphql → URL 기반 캐싱 불가               │
│     • CDN 캐싱이 REST보다 복잡                                      │
│     • Apollo Cache 등 별도 캐싱 전략 필요                           │
│                                                                     │
│  ② N+1 문제                                                         │
│     • 중첩된 Resolver가 개별 쿼리를 실행                            │
│     • DataLoader로 배치 처리해야 함 (추가 구현 필요)                │
│                                                                     │
│  ③ 복잡한 서버 구현                                                  │
│     • Resolver 설계, 스키마 관리, 인증/인가 처리                    │
│     • 에러 처리가 REST보다 복잡 (HTTP 상태 코드 미사용)            │
│                                                                     │
│  ④ 보안 이슈                                                        │
│     • 클라이언트가 자유롭게 쿼리 작성 → 악의적 깊은 쿼리 가능     │
│     • Query Depth Limiting, Cost Analysis 필요                      │
│     • Introspection 운영 환경에서 비활성화 필요                     │
│                                                                     │
│  ⑤ 파일 업로드 불편                                                  │
│     • GraphQL 스펙에 파일 업로드 미포함                              │
│     • multipart 요청 별도 처리 필요                                 │
│                                                                     │
│  ⑥ 학습 곡선                                                        │
│     • 스키마 설계, Resolver 패턴, DataLoader 등                     │
│     • 팀 전체가 학습해야 함                                         │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

6. GraphQL의 N+1 문제와 DataLoader

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
┌─────────────────────────────────────────────────────────────────────┐
│              GraphQL N+1 문제                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  요청:                                                              │
│  {                                                                  │
│    users {         ← 1번 쿼리: SELECT * FROM users                 │
│      name                                                           │
│      orders {      ← N번 쿼리: SELECT * FROM orders WHERE user_id=?│
│        product     ← 유저 10명이면 10번 추가 쿼리 실행!            │
│      }                                                              │
│    }                                                                │
│  }                                                                  │
│                                                                     │
│  → 총 1 + 10 = 11번 쿼리 (N+1)                                     │
│                                                                     │
│  ──────────────────── 해결: DataLoader ────────────────────         │
│                                                                     │
│  DataLoader는 같은 틱(tick)의 요청을 모아서 배치 처리               │
│                                                                     │
│  개별 호출:                                                         │
│  SELECT * FROM orders WHERE user_id = 1                             │
│  SELECT * FROM orders WHERE user_id = 2                             │
│  ... (10번)                                                         │
│                                                                     │
│  DataLoader 배치:                                                   │
│  SELECT * FROM orders WHERE user_id IN (1, 2, 3, ..., 10)          │
│  → 1번의 쿼리로 해결!                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

7. 데이터 모델링 차이

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
32
33
34
35
36
37
38
39
40
41
┌─────────────────────────────────────────────────────────────────────┐
│           REST vs GraphQL 데이터 모델링                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [REST - 리소스 중심 모델링]                                        │
│                                                                     │
│  각 리소스가 독립적인 엔드포인트                                    │
│  • GET /users/1                → User 객체                         │
│  • GET /users/1/orders         → Order 배열                        │
│  • GET /users/1/orders/5       → 특정 Order 객체                   │
│                                                                     │
│  → URL 구조 = 리소스 계층 구조                                      │
│  → 리소스 간 관계는 URL 경로로 표현                                 │
│  → 각 엔드포인트가 고정된 응답 형태                                 │
│                                                                     │
│  ──────────────────────────────────────────────────                 │
│                                                                     │
│  [GraphQL - 그래프 중심 모델링]                                     │
│                                                                     │
│  데이터를 연결된 그래프(노드 + 관계)로 바라봄                       │
│  • User → Orders → Products → Reviews                              │
│  • 클라이언트가 그래프를 탐색하며 필요한 부분만 선택                │
│                                                                     │
│  {                                                                  │
│    user(id: 1) {                                                    │
│      name                                                           │
│      orders {                                                       │
│        product {              ← 깊은 탐색 가능                      │
│          name                                                       │
│          reviews {                                                  │
│            rating                                                   │
│          }                                                          │
│        }                                                            │
│      }                                                              │
│    }                                                                │
│  }                                                                  │
│                                                                     │
│  → 한 번의 요청으로 깊은 관계까지 탐색                              │
│  → 화면에 필요한 데이터를 정확히 가져옴                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

8. 유지보수성 비교

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────────┐
│              유지보수 관점에서의 비교                                  │
├──────────────────┬──────────────────────┬────────────────────────────┤
│ 항목             │ REST                 │ GraphQL                    │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 새 필드 추가     │ 기존 API 수정 또는   │ 스키마에 필드 추가만       │
│                  │ 새 버전 생성         │ (기존 클라이언트 영향 X)   │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 필드 삭제        │ 새 버전 필요         │ @deprecated 표시 후        │
│                  │                      │ 점진적 제거                │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ API 문서         │ Swagger/OpenAPI 별도  │ 스키마 자체가 문서         │
│                  │ 작성 및 동기화 필요  │ (Introspection)            │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 프론트 요구사항  │ 새 엔드포인트 추가   │ 프론트가 쿼리만 수정       │
│ 변경 시          │ 또는 기존 수정       │ (백엔드 변경 최소화)       │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 팀 협업          │ API 설계 합의 필요   │ 스키마 설계 합의 필요      │
│                  │ (엔드포인트 + 응답)  │ (타입 + 관계)              │
├──────────────────┼──────────────────────┼────────────────────────────┤
│ 모니터링         │ 엔드포인트별 모니터링│ 쿼리별 분석 필요           │
│                  │ 간단                 │ (쿼리가 자유형이라 복잡)   │
└──────────────────┴──────────────────────┴────────────────────────────┘

9. 선택 기준

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
┌─────────────────────────────────────────────────────────────────────┐
│              REST vs GraphQL 선택 기준                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  REST가 적합한 경우:                                                │
│  • 단순한 CRUD API                                                  │
│  • HTTP 캐싱이 중요한 서비스 (CDN 활용)                             │
│  • 파일 업로드/다운로드가 많은 서비스                               │
│  • 마이크로서비스 간 통신 (서비스 to 서비스)                        │
│  • 팀이 REST에 익숙하고 빠른 개발이 필요할 때                       │
│  • 공개 API (외부 개발자용)                                         │
│                                                                     │
│  GraphQL이 적합한 경우:                                             │
│  • 다양한 클라이언트 (웹, 모바일, 태블릿)가 다른 데이터 필요       │
│  • 화면별 데이터 요구사항이 복잡하고 자주 변경                      │
│  • Over-fetching/Under-fetching이 성능 병목                         │
│  • 프론트엔드 주도 개발 (BFF 패턴 대체)                             │
│  • 관계가 복잡한 데이터 (소셜 그래프 등)                            │
│  • API 버전 관리를 최소화하고 싶을 때                               │
│                                                                     │
│  혼용도 가능:                                                       │
│  • 메인 API → GraphQL                                              │
│  • 파일 업로드, 웹훅, 결제 콜백 → REST                             │
│  • 내부 서비스 간 통신 → gRPC                                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10. Spring for GraphQL 예시

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 1. 스키마 정의 (resources/graphql/schema.graphqls)
// type User {
//   id: ID!
//   name: String!
//   email: String!
//   orders: [Order!]!
// }
// type Query {
//   user(id: ID!): User
//   users: [User!]!
// }
// type Mutation {
//   createUser(name: String!, email: String!): User!
// }

// 2. Controller (Resolver 역할)
@Controller
public class UserController {

    private final UserService userService;
    private final OrderService orderService;

    @QueryMapping
    public User user(@Argument Long id) {
        return userService.findById(id);
    }

    @QueryMapping
    public List<User> users() {
        return userService.findAll();
    }

    // 중첩 필드 Resolver (User.orders)
    @SchemaMapping(typeName = "User")
    public List<Order> orders(User user) {
        return orderService.findByUserId(user.getId());
    }

    @MutationMapping
    public User createUser(@Argument String name, @Argument String email) {
        return userService.create(name, email);
    }
}

// 3. DataLoader로 N+1 해결
@Controller
public class UserController {

    @BatchMapping
    public Map<User, List<Order>> orders(List<User> users) {
        // 한 번의 쿼리로 모든 유저의 주문을 조회
        List<Long> userIds = users.stream()
            .map(User::getId).toList();
        Map<Long, List<Order>> orderMap =
            orderService.findByUserIds(userIds);

        return users.stream()
            .collect(Collectors.toMap(
                user -> user,
                user -> orderMap.getOrDefault(user.getId(), List.of())
            ));
    }
}

면접에서 자주 묻는 질문

Q1. REST와 GraphQL의 가장 큰 차이는?

REST는 서버가 엔드포인트별로 고정된 응답 구조를 반환하고, GraphQL은 클라이언트가 필요한 필드를 직접 지정하여 단일 엔드포인트에서 유연하게 데이터를 가져옵니다. REST는 리소스 중심(URL = 리소스), GraphQL은 그래프 중심(노드와 관계를 탐색)으로 설계합니다.

Q2. Over-fetching과 Under-fetching이 뭔가요?

Over-fetching은 클라이언트가 필요하지 않은 데이터까지 서버가 응답하는 것입니다. 이름만 필요한데 전체 프로필을 반환하는 경우입니다. Under-fetching은 한 화면에 필요한 데이터를 얻기 위해 여러 엔드포인트를 호출해야 하는 것입니다. GraphQL은 필요한 필드만 선택하고 한 번의 요청으로 여러 리소스를 조합할 수 있어 두 문제를 모두 해결합니다.

Q3. GraphQL의 단점은?

HTTP 캐싱이 어렵습니다. 모든 요청이 POST이므로 URL 기반 CDN 캐싱이 불가능합니다. N+1 문제가 발생하기 쉬워 DataLoader 같은 배치 처리가 필요합니다. 클라이언트가 자유롭게 쿼리를 작성할 수 있어 보안(악의적 깊은 쿼리)에 주의해야 하고, 파일 업로드 처리가 불편하며, 학습 곡선이 REST보다 높습니다.

Q4. GraphQL에서 N+1 문제는 어떻게 해결하나요?

DataLoader(배치 로딩) 패턴으로 해결합니다. 같은 이벤트 루프 틱에서 발생하는 개별 조회 요청을 모아서 WHERE id IN (1, 2, 3, ...) 같은 하나의 배치 쿼리로 실행합니다. Spring for GraphQL에서는 @BatchMapping 어노테이션으로 간단히 구현할 수 있습니다.

Q5. GraphQL은 API 버전 관리를 어떻게 하나요?

GraphQL은 버전 관리가 필요 없는 것이 장점입니다. 새 필드를 추가해도 기존 클라이언트는 해당 필드를 요청하지 않으므로 영향이 없습니다. 기존 필드를 제거할 때는 @deprecated(reason: "...") 디렉티브로 표시한 후, 클라이언트가 전환할 시간을 준 뒤 점진적으로 제거합니다. 스키마를 확장해 나가는 진화적 설계입니다.

Q6. 언제 REST를 선택하고 언제 GraphQL을 선택하나요?

REST는 단순한 CRUD, HTTP 캐싱이 중요한 서비스, 파일 처리가 많은 경우, 서비스 간 통신에 적합합니다. GraphQL은 다양한 클라이언트가 서로 다른 데이터를 필요로 하거나, 화면 요구사항이 자주 변하거나, 관계가 복잡한 데이터를 다룰 때 적합합니다. 실무에서는 메인 API를 GraphQL로, 파일 업로드나 웹훅은 REST로 혼용하기도 합니다.

Q7. Introspection이 뭔가요?

GraphQL 서버에 스키마 자체를 질의할 수 있는 기능입니다. 어떤 타입, 필드, 쿼리가 존재하는지 프로그래밍적으로 탐색할 수 있어, 개발 도구(GraphiQL, Apollo Studio)가 자동 완성과 문서화를 제공합니다. 단, 운영 환경에서는 보안상 비활성화해야 합니다. 공격자가 전체 API 구조를 파악하는 데 악용될 수 있기 때문입니다.


핵심 정리: REST는 리소스 중심의 성숙한 아키텍처로 HTTP 캐싱과 단순함이 강점이고, GraphQL은 클라이언트 주도의 유연한 데이터 조회로 Over/Under-fetching을 해결한다. 어느 쪽이 우월한 게 아니라 프로젝트 특성에 맞는 선택이 중요하며, 혼용도 실무에서 흔하다.