GraphQL이란?

업데이트: 2026년 10월

GraphQL은 API를 위한 쿼리 언어이자, 각 클라이언트가 요구한 필드만 한 번의 요청으로 정확히 돌려주는 서버 측 런타임입니다. 이 이중성이 중요합니다. 언어는 어떤 클라이언트든 사용할 수 있는 명세이고, 런타임은 기존 데이터(어떤 데이터베이스든, 어떤 서비스든) 위에 정의한 타입 시스템에 대해 그 쿼리를 실행합니다. GraphQL은 데이터베이스가 아니며, 스토리지를 대체하지도 않고, 반드시 REST API를 대체하는 것도 아닙니다.

핵심 요약

질문답변
본질명세가 관리하는 쿼리 언어 + 실행 런타임. 스토리지에 구애받지 않음
대표 동작응답 모양이 쿼리 모양을 그대로 반영. 필드를 요청하면 그 필드만 받음
세 가지 오퍼레이션쿼리(읽기) · 뮤테이션(쓰기) · 서브스크립션(실시간 푸시)
구성 요소스키마(SDL 계약) · 타입 · 리졸버(필드별 조회 함수)
실제 비용캐싱 전략, N+1 배칭, 비용 기반 제한, 보안 강화

대표 데모: 쿼리와 응답

모든 설명이 결국 수렴하는 데모입니다. 이것이 곧 GraphQL의 아이디어이기 때문입니다. 응답은 쿼리를 채워 넣은 것입니다:

# 요청                                 # 응답
{                                      {
  post(id: "8fk2") {                     "data": {
    title                                  "post": {
    author {                                 "title": "Hello GraphQL",
      username                               "author": {
    }                                          "username": "ada"
    comments(first: 2) {                     },
      text                                   "comments": [
    }                                          { "text": "Nice." },
  }                                            { "text": "Ship it." }
}                                            ]
                                           }
                                         }
                                       }

요청 한 번, 연관 리소스 세 개, 요청하지 않은 필드 0개. 오버페칭과 언더페칭 쌍이 단번에 사라집니다. 실제 클라이언트에서 호출하는 것은 평범한 HTTP입니다:

// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
  method: 'POST',
  headers: {
    'X-Parse-Application-Id': APP_ID,
    'X-Parse-Client-Key': CLIENT_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
  }),
});
const { data } = await res.json(); // shaped exactly like the query

스키마, 쿼리, 리졸버: 함께 일하는 삼총사

설명 글은 쿼리를 보여 주지만, 그 뒤의 장치를 하나의 일관된 그림으로 보여 주는 글은 거의 없습니다. 스키마는 SDL로 작성된 타입 있는 계약입니다:

type Post {
  title: String!          # ! = null 불가
  author: User!
  comments(first: Int): [Comment!]
}

type Query {              # 읽기 진입점
  post(id: ID!): Post
}

type Mutation {           # 쓰기 진입점
  createPost(title: String!): Post!
}

리졸버는 런타임의 나머지 절반입니다. 필드당 함수 하나이며, 각각 어디에서든 자유롭게 데이터를 가져옵니다:

const resolvers = {
  Query: {
    post: (_, { id }) => db.posts.findById(id),
  },
  Post: {
    author: (post) => db.users.findById(post.authorId),   // 게시물마다 호출!
  },
};

실행은 파이프라인입니다. 쿼리를 파싱하고, 스키마에 대해 검증하며(유효하지 않은 오퍼레이션은 데이터에 접근하기 전에 거부됩니다), 그다음 선택 집합을 순회하며 리졸버를 호출하고 쿼리를 반영한 JSON을 조립합니다. 이 필드별 리졸버 호출은 유연성의 대가이기도 합니다. // 게시물마다 호출!에 주목해야 하는데, 이것이 아래에서 다루는 N+1 문제가 됩니다. 아무도 설명하지 않는 이름의 유래는 이렇습니다. 데이터는 타입 있는 객체의 그래프를 이루고, 쿼리는 루트 필드에서 출발해 그 그래프를 순회합니다. 다만 진정한 그래프 쿼리 언어처럼 임의로 순회하는 것이 아니라 스키마가 노출한 경로를 따라서만 순회합니다.

출처를 짧게 정리하면, 2012년 Facebook(현 Meta)이 모바일 앱을 위해 만들었고, 2015년 오픈소스로 공개되었으며, 2018년부터 Linux Foundation 산하 GraphQL Foundation이 관리합니다. 현재 명세 판은 2021년 10월판이며, GraphQL-over-HTTP 초안이 전송 관례를 표준화하고 있습니다.

쿼리, 뮤테이션, 서브스크립션

GraphQL 실행 파이프라인클라이언트 오퍼레이션(쿼리, 뮤테이션, 서브스크립션)이 단일 엔드포인트에 도착해 파싱되고 스키마로 검증된 뒤, 요청된 필드마다 리졸버를 호출해 데이터베이스나 서비스에 대해 실행되고, 요청 모양을 반영한 JSON으로 반환됩니다.

클라이언트 오퍼레이션
쿼리 · 뮤테이션 · 서브스크립션

단일 엔드포인트
/graphql

파싱 + 스키마로
검증

실행:
필드별 리졸버

데이터베이스,
API, 서비스

쿼리 모양을 반영한
JSON

클라이언트 오퍼레이션(쿼리, 뮤테이션, 서브스크립션)이 단일 엔드포인트에 도착해 파싱되고 스키마로 검증된 뒤, 요청된 필드마다 리졸버를 호출해 데이터베이스나 서비스에 대해 실행되고, 요청 모양을 반영한 JSON으로 반환됩니다.

쿼리는 읽습니다. 뮤테이션은 쓰면서 결과에서 필드를 선택하므로, 클라이언트는 같은 왕복 안에서 쓰기 이후의 상태를 받습니다. 서브스크립션은 연결을 열어 두고(실무에서는 WebSocket) 이벤트가 발생할 때마다 푸시합니다. 이것이 GraphQL의 실시간 해법이며, 활성 서브스크립션 하나하나가 서버가 유지하는 상태라는 단서가 붙습니다. 셋은 스키마, 타입 시스템, 도구를 공유합니다. 계약 하나에 시제 셋입니다.

GraphQL vs. REST

CriterionGraphQLREST
엔드포인트하나(/graphql)리소스마다 하나
응답 모양쿼리마다 클라이언트가 구성엔드포인트마다 고정
오버/언더페칭HTTP 계층에서 해결파라미터로 완화
HTTP 캐싱기본적으로 상실(단일 POST)네이티브. 최대 강점
타이핑과 인트로스펙션계약에 내장OpenAPI로 선택적 적용
버전 관리버전 없는 진화 + @deprecated/v1, /v2 관례
실시간명세 안의 서브스크립션범위 밖
최적 영역다양한 클라이언트, 중첩 데이터리소스 CRUD, 캐시 가능한 읽기

REST가 단순히 더 나은 선택인 경우까지 포함한 전체 논의는 별도의 GraphQL vs. REST 항목에서 다룹니다.

프로덕션에서 GraphQL 운영하기: 솔직한 비용

벤더 설명 글이 순화하는 섹션입니다. 캐싱: POST 엔드포인트 하나는 URL을 키로 하는 HTTP 및 CDN 캐싱을 포기한다는 뜻입니다. 대체 수단은 id와 __typename을 키로 하는 클라이언트 측 정규화 캐시, 그리고 전송 캐싱을 일부 되찾기 위한 영속 쿼리(허용 목록에 등록된 해시 오퍼레이션을 GET으로 보내는 방식)입니다. N+1: 단순하게 구현한 리졸버는 게시물 20개 목록을 1 + 20번의 데이터베이스 읽기로 바꿉니다. REST 클라이언트가 HTTP 위에서 겪는 같은 문제가 리졸버 계층으로 자리를 옮긴 것이며, DataLoader 같은 배칭 로더로 그 자리에서 고칩니다. 오류: GraphQL은 errors 배열을 담은 200 OK를 반환합니다. 상태 코드 기반 모니터링은 따로 설정하지 않으면 오류를 감지하지 못합니다. 속도 제한: 쿼리 하나가 관계 열 개를 중첩할 수 있으니 요청마다 비용이 같지 않습니다. 성숙한 API는 요청 수가 아니라 쿼리 비용(깊이와 복잡도 분석)을 측정합니다. 보안: 프로덕션에서는 인트로스펙션을 비활성화하고, 깊이와 복잡도 제한을 강제하며, 인가는 리졸버 아래 비즈니스 계층에 두어야 합니다. 단일 엔드포인트는 URL 기반 WAF 규칙도 무력화하므로, 검증은 GraphQL 계층 자체로 옮겨 갑니다.

일반적인 사용 사례

  • 제약된 네트워크의 모바일 앱 – GraphQL이 탄생한 계기가 된 사용 사례입니다. 정확한 필드, 최소 바이트, 더 적은 왕복.
  • 다중 클라이언트 제품 – 워치 앱, 폰 앱, 웹 대시보드가 각각 하나의 스키마에 대해 자기 응답을 구성합니다.
  • 백엔드 포 프론트엔드 집계 – UI가 소비하도록 여러 내부 서비스를 조합하는 GraphQL 계층 하나.
  • 빠르게 진화하는 프론트엔드 – 새 화면이 새 엔드포인트를 기다리지 않고 새 필드를 선택합니다.
  • 엔드 투 엔드 타입 계약 – 스키마 인트로스펙션으로 타입 있는 클라이언트를 생성해, 컴파일 시점에 API와 UI의 불일치를 잡아냅니다.

GraphQL을 사용해야 할까? 결정 매트릭스

GraphQL의 장치가 제값을 하는 경우…REST를 선택해야 하는 경우…
클라이언트마다 필요한 데이터가 다름클라이언트 유형 하나, 안정적인 화면
화면이 중첩된 관계형 데이터를 읽음리소스가 엔드포인트에 깔끔하게 대응됨
여러 백엔드 소스를 집계함서비스 하나가 데이터를 소유함
대역폭이 귀중함(모바일 우선)HTTP/CDN 캐싱이 읽기 부하를 감당할 수 있음
플랫폼이 스키마를 대신 생성해 줌팀이 전부 직접 만들고 강화해야 함

한계와 트레이드오프

  • 유연성의 비용은 서버가 치릅니다. 임의의 클라이언트 쿼리는 서버가 어떤 모양에서도 안전해야 한다는 뜻입니다. 배칭, 비용 제한, 깊이 가드는 마감 작업이 아니라 전제 조건입니다.
  • 캐싱이 직접 맡아야 할 프로젝트가 됩니다. HTTP가 REST에 공짜로 주던 것을 GraphQL 팀은 클라이언트 캐시와 영속 쿼리로 다시 구현합니다.
  • 관측 가능성을 다시 배워야 합니다. 엔드포인트 하나, 항상 200인 응답, 필드별 타이밍에는 GraphQL을 이해하는 도구가 필요합니다.
  • 파일 업로드와 바이너리 데이터가 어색합니다. 보통 그래프 옆의 별도 업로드 엔드포인트에 위임합니다.
  • 스키마 거버넌스는 조직의 문제입니다. 팀을 가로지르는 공유 계약에는 소유권 규칙이 필요합니다. 페더레이션(팀이 소유한 서브그래프를 하나의 슈퍼그래프로 조합)이 확장의 답이며, 그 자체로 또 하나의 규율입니다.

Back4app에서의 GraphQL

Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 차별점은 스키마가 어디에서 오는가에 있습니다. 데이터 모델을 정의하면 플랫폼이 GraphQL API를 생성합니다. 타입 있는 객체 타입, 쿼리와 뮤테이션 필드, 위의 posts → author 예시 같은 관계를 순회하는 커넥션까지, 작성할 리졸버 없이 제공됩니다. Back4app이 데이터베이스에 대해 리졸버를 구현하고 요청마다 권한을 적용하기 때문입니다. 코드 탭이 클라이언트 쪽 이야기 전부를 보여 줍니다. 앱 키를 담아 /graphql로 보내는 POST 한 번입니다. 내장 GraphQL 콘솔이 탐색을 담당하고, 캐시 친화적인 읽기를 위해 같은 데이터에 REST가 그대로 제공되며, 커스텀 로직은 Cloud Code 함수로 스키마에 합류합니다. 위에서 다룬 솔직한 비용은 대부분 사용자가 아니라 플랫폼이 부담합니다.

자주 묻는 질문

GraphQL을 쉽게 설명하면 무엇입니까?

클라이언트가 중첩된 관계까지 포함해 꼭 필요한 필드만 한 번의 요청으로 API에 요구할 수 있게 하는 쿼리 언어이며, 그 요청을 기존 데이터 소스에서 처리하는 서버 런타임이기도 합니다. 어떤 데이터베이스나 서비스 앞에도 놓을 수 있으며, 그 자체가 데이터베이스는 아닙니다.

GraphQL이 REST보다 낫습니까?

어느 쪽도 항상 낫지는 않습니다. GraphQL은 다양한 클라이언트, 대역폭 제약, 여러 소스에서 집계되는 데이터에서 유리하고, REST는 리소스 형태의 CRUD에서 HTTP 캐싱, 단순성, 도구 성숙도로 유리합니다. 프로덕션에서 지배적인 패턴은 실용적입니다. REST나 RPC 내부 서비스 위에 프론트엔드용 GraphQL 계층을 두는 방식입니다.

GraphQL은 데이터베이스입니까, 아니면 SQL 같은 것입니까?

아니요. 설계상 스토리지에 구애받지 않는 애플리케이션 계층의 API 언어입니다. 리졸버는 어떤 데이터베이스, 다른 API, 파일에서든 읽을 수 있습니다. 또한 이름과 달리 SPARQL 같은 범용 그래프 쿼리 언어가 아니며, 스키마가 노출한 경로를 따라서만 그래프를 탐색합니다.

쿼리, 뮤테이션, 서브스크립션은 무엇입니까?

세 가지 오퍼레이션 유형입니다. 쿼리는 데이터를 읽고, 뮤테이션은 데이터를 쓰면서 같은 왕복 안에서 새 상태를 돌려주므로 클라이언트는 추가 조회 없이 상태를 갱신할 수 있습니다. 서브스크립션은 보통 WebSocket 기반의 지속 연결로 실시간 갱신을 푸시합니다. 셋 모두 같은 스키마로 검증됩니다.

GraphQL 스키마란 무엇입니까?

스키마 정의 언어(SDL)로 작성된, 클라이언트와 서버 사이의 타입 있는 계약입니다. 객체 타입과 그 필드, 그리고 루트 Query, Mutation, Subscription 진입점으로 구성됩니다. 들어오는 모든 오퍼레이션은 실행 전에 스키마로 검증되며, 도구는 스키마를 인트로스펙션해 문서와 타입 있는 클라이언트를 생성합니다.

리졸버란 무엇입니까?

필드 하나의 값을 데이터베이스, 다른 API, 혹은 어디에서든 가져오는 서버 측 함수입니다. 런타임은 각 쿼리를 순회하며 요청된 모든 필드에 대해 리졸버를 호출합니다. 이것이 GraphQL의 유연성이자, 리스트 항목의 리졸버가 각각 따로 조회할 때 생기는 N+1 문제의 근원입니다.

GraphQL은 HTTP POST로만 동작합니까?

명세상 GraphQL은 전송 방식에 구애받지 않습니다. 실무에서는 관례상 /graphql이라는 단일 HTTP 엔드포인트에서 제공되며, 보통 JSON 본문을 담은 POST를 쓰고, 쿼리에는 GET이 허용되며, 서브스크립션은 WebSocket이 실어 나릅니다. 현재 GraphQL-over-HTTP 명세가 이런 관례를 표준화하고 있습니다.

GraphQL을 쓰지 말아야 할 때는 언제입니까?

균일한 클라이언트를 둔 단순한 리소스 CRUD, HTTP와 CDN 캐싱이 흡수할 수 있는 읽기 트래픽, 파일 위주의 전송, 그리고 리졸버 배칭, 쿼리 비용 제한, 스키마 거버넌스를 감당할 의지가 없는 소규모 팀입니다. 이런 경우에는 잘 설계된 REST API로 더 단순하게 같은 결과를 얻을 수 있습니다.

관련 용어

비교 대상

참고 자료

백엔드를 구축할 준비가 되셨나요?

몇 분 안에 Back4app에서 프로젝트를 시작하세요. 데이터베이스, 인증, API, Cloud Code가 포함되어 있으며 신용카드는 필요 없습니다.

작성 및 검수: Back4app Engineering, Back4app Engineering · 게시일 2026-10-28