GraphQL(GraphQL)

•
2026년 10월 09일 발행•2026년 10월 09일 수정
요약

클라이언트가 필요한 데이터의 구조를 직접 기술하여 요청하고 서버가 스키마에 따라 정확히 그 구조로 응답하는 API 질의 언어이자 실행 런타임

초급
HTTP와 JSON, REST API의 기본 개념과 클라이언트-서버 구조를 이해해야 하며, 데이터베이스 조회와 캐싱 개념을 알면 N+1 문제·캐싱·보안 부분을 이해하는 데 도움이 된다.

#개념

GraphQL은 클라이언트가 필요한 데이터의 구조를 직접 기술하여 요청하고 서버가 정확히 그 구조대로 응답하는 API(Application Programming Interface) 질의 언어이자, 그 질의를 실행하는 서버 측 런타임이다. 2012년 Facebook이 모바일 앱의 데이터 요청 문제를 해결하기 위해 내부적으로 개발했고 2015년 명세와 참조 구현을 공개했으며, 2019년부터는 리눅스 재단 산하 GraphQL Foundation이 명세를 관리하는 개방형 표준이 되었다.
GraphQL이 등장한 배경에는 REST API(Representational State Transfer API)의 한계가 있다. REST는 자원마다 엔드포인트를 두고 서버가 응답의 형태를 정하므로, 화면에 필요한 필드 몇 개를 위해 거대한 객체 전체를 받는 오버페칭(Over-fetching)과, 한 화면을 그리기 위해 여러 엔드포인트를 순차적으로 호출해야 하는 언더페칭(Under-fetching)이 함께 발생한다. 모바일 환경에서 이 비효율은 체감 성능에 직결되었고, 클라이언트 종류가 늘어날수록 화면별 전용 엔드포인트가 증식하는 문제도 심각했다.
GraphQL의 중심에는 스키마(Schema)와 타입 시스템(Type System)이 있다. 스키마는 서버가 제공할 수 있는 모든 데이터와 연산을 스키마 정의 언어(SDL)로 선언한 문서로, 클라이언트와 서버 사이의 강타입 계약 역할을 하며, 모든 요청은 실행 전에 스키마에 대해 검증된다. 주요 타입은 다음과 같다.
GraphQL의 주요 타입
  • 객체 타입(Object Type): type Product { id: ID!, name: String!, price: Int, reviews: [Review!]! }처럼 필드와 그 타입의 집합으로 정의되는 기본 단위이며, 필드가 다른 객체 타입을 가리켜 그래프 구조를 이룬다.
  • 스칼라와 열거형: Int·Float·String·Boolean·ID의 내장 스칼라와 DateTime 같은 사용자 정의 스칼라가 잎 노드를 이루고, 열거형(enum)은 허용되는 값의 집합을 고정한다.
  • 인터페이스와 유니온: 인터페이스는 여러 객체 타입이 공유해야 하는 필드를 규정하고, 유니온은 서로 다른 타입 중 하나가 반환될 수 있음을 나타내어 검색 결과처럼 이질적인 데이터를 표현한다.
  • 입력 타입과 수식어: 입력 타입(input)은 뮤테이션 인자로 전달되는 복합 값을 정의하며, !는 널이 될 수 없음을, [ ]는 리스트임을 나타낸다.
스키마의 또 다른 장점은 인트로스펙션(Introspection)이다. 클라이언트가 서버에 스키마 자체를 질의할 수 있으므로 GraphiQL이나 Apollo Studio 같은 도구가 자동 완성과 문서를 제공하고, 코드 생성기가 클라이언트 타입을 만들어 낸다. 필드를 삭제하는 대신 @deprecated 디렉티브로 표시하고 새 필드를 추가하는 방식으로 스키마를 점진적으로 진화시키므로 REST의 v1·v2 같은 버전 관리가 원칙적으로 필요 없다.
클라이언트가 보내는 요청은 세 가지 연산으로 나뉜다.
GraphQL의 세 가지 연산
  • 쿼리(Query): 데이터를 읽는 연산으로, { product(id: "1") { name price reviews { rating } } }처럼 원하는 필드를 중첩하여 기술하면 응답 JSON이 정확히 같은 모양으로 돌아온다. 명세상 쿼리의 최상위 필드는 병렬로 실행될 수 있다.
  • 뮤테이션(Mutation): 데이터를 생성·수정·삭제하는 연산이며, 부수 효과가 있으므로 최상위 필드가 선언된 순서대로 직렬 실행되고, 변경된 객체를 응답으로 함께 돌려받아 클라이언트 캐시를 갱신한다.
  • 서브스크립션(Subscription): 서버의 이벤트를 지속적으로 수신하는 연산으로, 보통 WebSocket이나 서버 전송 이벤트(SSE) 위에서 동작하며 실시간 알림, 주문 상태 변경, 채팅 메시지 전달에 쓰인다.
연산 안에서는 변수로 값을 매개화하고, 별칭(alias)으로 같은 필드를 다른 인자로 여러 번 요청하며, 프래그먼트(fragment)로 재사용 가능한 필드 집합을 정의하고, @include·@skip 디렉티브로 필드 포함 여부를 조건부로 제어할 수 있다. 응답은 항상 요청한 필드만 담은 data와 실행 중 발생한 오류 목록인 errors로 구성되며, 일부 필드만 실패해도 나머지 데이터는 정상적으로 반환되는 부분 성공이 가능하다.
서버에서 요청을 실제 데이터로 바꾸는 것은 리졸버(Resolver)다. 리졸버는 스키마의 각 필드에 대응하는 함수로, 상위 객체·인자·요청 컨텍스트·실행 정보를 받아 그 필드의 값을 반환하며, 실행 엔진은 요청의 선택 집합을 루트부터 깊이 우선으로 순회하면서 필드마다 리졸버를 호출한다. 리졸버 함수가 없는 필드는 상위 객체의 같은 이름 속성을 그대로 돌려주는 기본 리졸버가 적용되므로, 개발자는 데이터베이스 조회나 외부 API 호출이 필요한 필드만 구현하면 된다.
구현 방식은 SDL 문서를 먼저 작성하고 리졸버를 연결하는 스키마 우선(schema-first)과, 파이썬 Strawberry의 데이터클래스나 Graphene 클래스처럼 코드로 타입을 정의하면 스키마가 생성되는 코드 우선(code-first)으로 나뉜다. 리졸버는 데이터 출처를 가리지 않으므로 GraphQL 서버는 데이터베이스(Database), 캐시, 검색 엔진, 기존 REST 서비스를 함께 감싸는 통합 계층이 된다.
필드 단위 리졸버 구조는 N+1 문제라는 성능 함정을 만든다. 상품 100개와 각 상품의 판매자를 요청하면 상품 목록을 조회하는 쿼리 1번 뒤에 판매자 리졸버가 상품마다 한 번씩, 총 100번의 데이터베이스 조회를 수행하기 때문이다. 이를 해결하는 표준 도구가 Facebook이 공개한 DataLoader 패턴이다. 리졸버는 DataLoader에 키만 넘기고, DataLoader는 같은 이벤트 루프 틱 동안 모인 키를 하나의 배치로 묶어 WHERE id IN (...) 형태의 단일 조회를 수행하며, 요청 범위 안에서 같은 키의 결과를 캐싱하여 중복 조회를 막는다.
REST와 GraphQL은 서로를 완전히 대체하는 관계가 아니며, 주요 차이는 다음과 같다.
REST와 GraphQL의 비교
  • 엔드포인트와 응답 형태: REST는 자원별 다수의 URL과 서버가 정한 응답 구조를 쓰고, GraphQL은 단일 엔드포인트에서 클라이언트가 응답 구조를 결정한다.
  • 타입과 문서화: REST는 OpenAPI 명세를 별도로 유지해야 하지만, GraphQL은 스키마 자체가 강타입 계약이자 문서이며 인트로스펙션으로 항상 최신 상태를 보장한다.
  • 캐싱: REST는 URL 단위의 HTTP 캐시와 CDN을 그대로 활용하지만, GraphQL은 대개 POST 요청이므로 클라이언트 정규화 캐시나 별도 서버 캐시 전략이 필요하다.
  • 적합한 상황: 단순 CRUD, 파일 전송, 공개 API처럼 캐시 효율이 중요한 경우에는 REST가, 여러 클라이언트가 복잡하게 연결된 데이터를 다양한 형태로 소비하는 경우에는 GraphQL이 유리하다.
캐싱(Caching)은 GraphQL 도입 시 가장 먼저 다시 설계해야 하는 영역이다. 클라이언트 측에서는 Apollo Client와 Relay가 응답 객체를 __typename과 id로 식별하여 정규화된 캐시에 저장하므로, 서로 다른 쿼리가 같은 객체를 받으면 한 곳에서 갱신되고 화면 전체가 일관되게 유지된다. 서버 측에서는 자주 쓰이는 쿼리를 해시로 등록해 두는 영속 쿼리(Persisted Queries)를 GET 요청으로 보내 CDN 캐시를 적용하고, 리졸버 수준에서 결과를 Redis 같은 캐시에 저장하는 방식이 함께 쓰인다.
클라이언트가 응답 형태를 결정한다는 유연성은 보안 측면에서는 공격 표면이 된다. { product { reviews { author { products { reviews ... } } } } }처럼 순환 관계를 깊게 중첩한 쿼리는 서버에 기하급수적인 부하를 일으키며, Hartig와 Pérez(2018)는 GraphQL 쿼리의 응답 크기가 중첩 깊이에 대해 지수적으로 커질 수 있음을 형식적으로 증명하고 실행 전에 응답 크기를 다항 시간에 추정하는 방법을 제시했다.
이에 따라 운영 환경에서는 쿼리 깊이 제한(Depth Limiting)과 필드마다 비용을 부여해 합산하는 복잡도 분석(Complexity Analysis)으로 임계치를 넘는 쿼리를 실행 전에 거부하고, 리스트 필드의 최대 개수 인자를 강제하며, 실행 시간 제한을 둔다.
운영 환경에서는 또한 인트로스펙션을 비활성화하거나 인증된 사용자에게만 허용하고, 영속 쿼리 허용 목록(allowlist)으로 임의 쿼리를 차단하며, 배치 요청으로 속도 제한을 우회하는 공격을 요청 수가 아니라 비용 기준으로 막는다. 인가는 엔드포인트 단위가 아니라 필드·객체 단위로 적용해야 하므로 리졸버 또는 디렉티브에서 권한을 검사하고, 모든 응답이 HTTP 200으로 돌아오기 때문에 errors 배열을 기준으로 별도의 모니터링과 추적을 구성해야 한다.
조직이 커지면 하나의 거대한 스키마를 한 팀이 관리하기 어려워진다. 초기에는 여러 GraphQL 서비스의 스키마를 게이트웨이에서 병합하는 스키마 스티칭(Schema Stitching)이 쓰였고, 이를 발전시킨 페더레이션(Federation)은 각 팀이 자신의 도메인에 해당하는 서브그래프(Subgraph)를 독립적으로 소유·배포하면서도 클라이언트에게는 하나의 통합 그래프로 보이게 한다.
상품 서브그래프가 @key(fields: "id")로 Product 엔티티를 정의하면 리뷰 서브그래프가 같은 엔티티를 확장하여 reviews 필드를 추가할 수 있고, 라우터가 쿼리를 분석하여 각 서브그래프에 필요한 부분을 보낸 뒤 결과를 조립한다. Apollo Federation이 사실상의 표준이며, 서브그래프 간 호환성을 검사하는 스키마 레지스트리와 함께 운영된다.
데이터 플랫폼과 머신러닝(Machine Learning) 영역에서도 GraphQL은 통합 조회 계층으로 활용된다. Hasura와 PostGraphile은 데이터베이스 스키마로부터 GraphQL API를 자동 생성하고, 데이터 카탈로그 DataHub는 데이터셋·계보·소유자 정보를 GraphQL API로 노출하여 메타데이터 조회의 표준 인터페이스로 삼는다. 모델 서빙 측면에서는 추천·검색 결과와 상품·가격·재고 정보를 하나의 그래프로 묶는 BFF(Backend for Frontend) 계층으로 쓰여, 클라이언트가 추천 모델이 반환한 상품 ID 목록과 각 상품의 표시 정보를 한 번의 요청으로 받을 수 있다.
또한 피처 스토어(Feature Store)나 실험 플랫폼의 조회 API를 GraphQL로 감싸면 필요한 특징과 지표만 선택적으로 가져올 수 있고, 서브스크립션으로 학습 작업 상태나 실시간 지표 변화를 대시보드에 전달할 수 있다. 다만 추론 서버 자체는 보통 gRPC나 REST로 직접 호출하며, GraphQL은 그 앞단에서 여러 모델과 데이터 소스를 조합하는 역할을 맡는다.
GraphQL은 만능이 아니다. 캐싱·보안·N+1 문제를 처음부터 설계해야 하고, 파일 업로드 같은 바이너리 전송은 별도 규약이 필요하며, 리졸버가 여러 데이터 소스를 감추기 때문에 느린 필드 하나가 전체 응답의 지연 시간(Latency)을 좌우하여 추적 도구 없이는 원인을 찾기 어렵다. 단일 클라이언트가 단순한 자원을 소비한다면 REST가 더 적은 비용으로 같은 효과를 낸다.
결론적으로 GraphQL은 "서버가 무엇을 줄 수 있는가"를 스키마로 선언하고 "클라이언트가 무엇을 원하는가"를 쿼리로 기술하게 함으로써 데이터 요청의 주도권을 소비자에게 넘긴 API 패러다임이다. 그 유연성을 안전하게 운영하려면 DataLoader, 복잡도 제한, 정규화 캐시, 페더레이션 같은 보완 기법이 반드시 함께 설계되어야 하며, 이 조건이 갖추어질 때 GraphQL은 복잡한 데이터 플랫폼과 서비스 전면의 통합 조회 계층으로 강력한 선택지가 된다.

#관련 용어

API
소프트웨어 구성 요소가 서로 데이터와 기능을 주고받기 위해 정의된 인터페이스 규약
REST API
자원별 URL과 HTTP 메서드로 데이터를 주고받는 API 설계 방식으로, GraphQL이 등장한 배경이자 비교 대상
스키마
서버가 제공하는 모든 타입·필드·연산을 SDL로 선언하여 클라이언트와 서버 사이의 강타입 계약 역할을 하는 문서
리졸버
스키마의 각 필드에 대응하여 상위 객체·인자·컨텍스트를 받아 실제 데이터를 반환하는 서버 측 함수
캐싱
조회 결과를 임시 저장하여 반복 요청의 비용을 줄이는 기법으로, GraphQL에서는 정규화 캐시와 영속 쿼리 형태로 적용
지연 시간
요청을 보낸 뒤 응답을 받기까지 걸리는 시간으로, 느린 리졸버 하나가 전체 GraphQL 응답을 지연시키는 원인

#직무 연관도

DA
Data Analyst
낮음
데이터 카탈로그와 내부 도구의 GraphQL API로 메타데이터와 지표를 조회할 때 쿼리 작성법을 알면 도움이 된다
DS
Data Scientist
낮음
추천·검색 결과를 소비하는 서비스 API의 구조와 모델 출력이 어떻게 노출되는지 이해하는 데 유용하지만 모델링 작업과의 직접적인 관련은 적다
DE
Data Engineer
밀접
API 게이트웨이·BFF 설계, 데이터 플랫폼 조회 계층 구축, N+1·캐싱·보안·페더레이션 운영 등 서비스 개발과 운영에 직접 활용된다

#사용 사례

전자상거래인터넷 서비스미디어금융통신게임
개요
GraphQL은 모바일·웹 등 다양한 클라이언트를 위한 통합 API 계층, 마이크로서비스 위의 BFF와 게이트웨이, 데이터베이스와 데이터 카탈로그의 조회 API, 추천·검색 결과와 상품 정보를 조합하는 서빙 계층, 실시간 알림과 상태 갱신을 위한 서브스크립션 등에 활용된다.
사례
패션 전자상거래 앱의 상품 상세 화면에서 추천 모델이 반환한 연관 상품 ID 목록과 각 상품의 이름·가격·할인·재고·대표 이미지를 GraphQL BFF 계층이 하나의 쿼리로 조립하여 응답한다. 판매자와 리뷰 정보는 DataLoader로 배치 조회하고, 상품·리뷰·추천 서브그래프를 페더레이션으로 통합하며, 쿼리 깊이와 복잡도 제한으로 과도한 요청을 차단한다.

#참고 자료

#추천 포스트

© 2024 diki All rights reserved.