Reactive GraphQL — 기본 개념·Schema

2026-05-03확률과 통계 마스터 노트

Reactive GraphQL 마스터 노트 시리즈 1편. GraphQL이 REST의 한계를 푸는 방식, Over-fetching·Under-fetching 문제 해결, Schema-first 설계와 SDL 문법, 8 기본 타입과 Custom 타입, Query·Mutation·Subscription 3 작업, Resolver의 역할, REST vs GraphQL 결정적 차이까지.

이 글은 Reactive GraphQL 마스터 노트 시리즈의 첫 번째 편입니다. REST의 over-fetching·under-fetching 한계. GraphQL이 해결책. Facebook이 만들고 오픈소스화 → 모던 API 표준 중 하나.

이 시리즈 7편은 기본 개념·Query/Mutation·Subscription·Spring GraphQL·Reactive·Security/Testing·고급까지. 1편의 목표 — GraphQL이 무엇이고 왜 손에 잡히게.

📚 학습 노트

이 시리즈는 GraphQL 공식 사양, Spring for GraphQL 가이드, GraphQL Java 문서 등 공개 자료를 참고해 한국어 학습 노트로 풀어쓴 자료입니다.

Spring Boot 프로젝트에 spring-boot-starter-graphql 추가하고 GraphiQL UI로 첫 쿼리 실행해 보면 흐름이 한 번에 잡혀요. 30분이면 첫 GraphQL API가 손에 들어옵니다.

처음 GraphQL이 어렵게 느껴지는 이유

처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, REST와 어떻게 다른지 막연합니다. 둘째, Schema·Resolver·Type 단어들이 한 번에 등장합니다.

해결법은 한 가지예요. "GraphQL = 클라이언트가 원하는 데이터 정확히 명시" 한 줄. 사용자가 SQL 같은 쿼리. 서버는 정확히 그 데이터만. Over·Under-fetching X.

REST의 한계

Over-fetching (과조회)

GET /users/123

# 응답
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com",
  "phone": "010-1234-5678",
  "address": "...",
  "preferences": {...},
  "meta": {...}
}

이름만 필요해도 전체 받음. 모바일 환경 = 데이터 낭비.

Under-fetching (과소조회)

GET /users/123          # 사용자
GET /users/123/posts    # 게시물
GET /users/123/comments # 댓글

3 호출 = 3 RTT. 모바일 = 느림.

응답 형태 고정

GET /users/123 → 항상 같은 형태
새 필드 필요 → 새 엔드포인트 또는 versioning

여기서 정말 중요한 시험 함정 — REST 한계 해결 = GraphQL의 등장 배경. Facebook 모바일 앱에서 출발 (2012·오픈소스 2015).

GraphQL — 클라이언트가 명시

query {
  user(id: 123) {
    name
    posts {
      title
      comments {
        text
      }
    }
  }
}

응답:

{
  "data": {
    "user": {
      "name": "Alice",
      "posts": [
        {
          "title": "Hello",
          "comments": [{ "text": "Great!" }]
        }
      ]
    }
  }
}

핵심:

  • 클라이언트가 필드 명시
  • 단일 요청에 여러 데이터 (User + Posts + Comments)
  • 응답 = 요청 형태 그대로

Schema-first

type Query {
  user(id: ID!): User
  users: [User!]!
}

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  content: String
  author: User!
}

여기서 정말 중요한 시험 함정 — Schema = 계약 (Contract). 서버·클라이언트의 약속. 변경 시 양쪽 모두 의식. REST의 OpenAPI/Swagger와 비슷하지만 더 강력.

SDL — Schema Definition Language

# 기본 타입
type User {
  id: ID!              # ID, !는 non-null
  name: String!        # 필수
  email: String        # nullable
  age: Int
  active: Boolean!
  joinedAt: String
}

# 리스트
type Query {
  users: [User]        # 리스트 (null 가능)
  posts: [Post!]       # 리스트, 항목 non-null
  comments: [Comment!]! # 리스트 자체 non-null + 항목 non-null
}

8 Scalar 타입

ID       — 고유 식별자 (String 비슷)
String   — 텍스트
Int      — 32-bit 정수
Float    — 부동 소수점
Boolean  — true/false
Custom   — 사용자 정의 (Date·BigDecimal·UUID 등)

Enum

enum Role {
  ADMIN
  USER
  GUEST
}

Interface

interface Node {
  id: ID!
}

type User implements Node {
  id: ID!
  name: String!
}

Union

union SearchResult = User | Post | Comment

type Query {
  search(query: String!): [SearchResult!]!
}

여러 타입 중 하나.

Input

input CreateUserInput {
  name: String!
  email: String!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
}

Mutation 인자용. 입력 객체.

3 Operation Types

type Query {        # 조회 (GET 비슷)
  users: [User!]!
}

type Mutation {     # 변경 (POST/PUT/DELETE 비슷)
  createUser(input: CreateUserInput!): User!
  deleteUser(id: ID!): Boolean!
}

type Subscription { # 실시간 구독 (WebSocket)
  userAdded: User!
}

자세한 건 2·3편.

Resolver — 데이터 가져오는 함수

@Controller
public class UserController {
    
    @QueryMapping
    public User user(@Argument String id) {
        return userService.findById(id);
    }
    
    @SchemaMapping(typeName = "User", field = "posts")
    public List<Post> posts(User user) {
        return postService.findByAuthor(user.getId());
    }
}

Schema의 각 필드 = Resolver 함수. 필드 요청될 때 호출.

여기서 정말 중요한 시험 함정 — Resolver는 필드 단위. user.posts 요청 시 별도 Resolver 호출. 모든 필드가 자기 데이터 가져옴. N+1 문제 발생 → DataLoader (7편).

REST vs GraphQL

측면 REST GraphQL
엔드포인트 다수 (/users·/posts) 단일 (/graphql)
데이터 형태 서버 결정 클라이언트 결정
Over-fetching 자주 없음
Under-fetching 자주 없음
응답 형태 고정 쿼리 형태
캐싱 HTTP 표준 어려움
학습 곡선 낮음 중간
도구 많음 매우 많음

여기서 정말 중요한 시험 함정 — GraphQL ≠ REST 대체. 둘 사용처 다름. 클라이언트별 다양한 데이터 = GraphQL / 캐싱·표준화 = REST. 둘 결합도 OK.

사용 사례

GraphQL 적합

  • 모바일 앱 (over-fetching 회피)
  • 다양한 클라이언트 (웹·모바일·태블릿)
  • 복잡한 데이터 그래프 (사용자→게시물→댓글)
  • 빠른 변경 API
  • 공개 API (외부 개발자)

GraphQL 부적합

  • 단순 CRUD
  • 파일 업로드 (REST가 자연스러움)
  • HTTP 캐싱 활용 (REST가 우월)
  • 복잡한 분석 쿼리 (SQL이 더 강력)

GraphiQL·Apollo Studio

http://localhost:8080/graphiql

GraphQL 전용 IDE. Schema 자동 발견·자동완성·문서. 개발 표준.

첫 GraphQL — Spring Boot

의존성

implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-webflux'

Schema (resources/graphql/schema.graphqls)

type Query {
  hello: String!
}

Resolver

@Controller
public class HelloController {
    
    @QueryMapping
    public String hello() {
        return "Hello, GraphQL!";
    }
}

쿼리

POST /graphql
Content-Type: application/json

{
  "query": "{ hello }"
}

# 응답
{
  "data": { "hello": "Hello, GraphQL!" }
}

Introspection

{
  __schema {
    types {
      name
      fields { name type { name } }
    }
  }
}

GraphQL 자체에 스키마 조회 기능. GraphiQL이 자동 활용.

여기서 시험 함정이 하나 있어요. 운영 환경 Introspection OFF. 보안 — 스키마 노출 = 공격 표면. 개발만.

시험 직전 한 번 더 — 자주 헷갈리는 함정 모음

여기까지가 1편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.

  • GraphQL = 클라이언트가 원하는 데이터 명시 (SQL 비슷)
  • REST 한계 해결 — Over-fetching / Under-fetching / 형태 고정
  • Facebook 2012·오픈소스 2015
  • 단일 엔드포인트 (/graphql)
  • 응답 = 요청 형태 그대로
  • Schema-first = 계약 (Contract)
  • SDL (Schema Definition Language)
  • 8 Scalar — ID·String·Int·Float·Boolean·Custom
  • ! = non-null·필수
  • 리스트 — [User] (널 가능) / [User!]! (양쪽 non-null)
  • Enum·Interface·Union·Input 추가 타입
  • 3 Operations — Query·Mutation·Subscription
  • Resolver = 필드 단위 함수
  • N+1 문제 → DataLoader (7편)
  • REST vs GraphQL — 사용처 다름·결합 OK
  • 적합 — 모바일·다양한 클라이언트·복잡 그래프·공개 API
  • 부적합 — 단순 CRUD·파일 업로드·HTTP 캐싱
  • GraphiQL = 전용 IDE (자동완성·문서)
  • Spring Boot — spring-boot-starter-graphql
  • Schema = resources/graphql/*.graphqls
  • Resolver = @QueryMapping·@SchemaMapping
  • Introspection = 스키마 조회 (운영 OFF 권장)

시리즈 다른 편

공식 문서: GraphQL Specification / Spring for GraphQL 에서 더 깊이.

다음 글(2편)에서는 Query·Mutation — 조회·변경 작업 패턴, 인자·변수·alias·fragment까지 풀어 갑니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!