GraphQL은 클라이언트가 필요한 데이터의 모양을 쿼리로 적어 보내면 서버가 정확히 그 모양으로 응답하는 API 쿼리 언어(query language)다. Facebook이 2012년 내부에서 만들어 2015년 공개했고, 지금은 GraphQL Foundation이 관리한다.
탄생 배경
모바일 앱 전환기에 REST로 복잡하게 얽힌 화면 데이터를 받으면 두 문제가 생겼다.
- over-fetching: 이름과 사진만 필요한데 사용자 객체 전체를 받는다. 느린 모바일 망에서 비싸다
- under-fetching: 한 화면에 필요한 데이터가 여러 리소스에 흩어져 요청을 여러 번 보낸다
- 클라이언트마다 맞춤 엔드포인트와 API 버전이 늘어났다. 결론은 "데이터 모양은 클라이언트가 정하자"였다
query {
post(id: "1") {
title
author { name profileImage }
comments(first: 3) { text }
}
}특징
- 강타입 스키마(strongly typed schema): 모든 타입과 필드를 스키마로 정의하고, 쿼리는 실행 전에 스키마로 검증된다. 서버와 클라이언트 사이의 계약이다
- 인트로스펙션(introspection): API가 자기 스키마를 쿼리로 알려 준다. 문서·탐색 도구·타입 생성(codegen)이 여기서 나온다
- 단일 엔드포인트(single endpoint): 보통
/graphql하나. 요청은 Query(조회), Mutation(변경), Subscription(실시간 구독) - 버전 없는 진화: 필드를 추가해도 기존 쿼리는 그대로다. 없앨 필드는
@deprecated로 표시하고 사용량을 보며 지운다 - 리졸버(resolver): 필드마다 데이터를 가져오는 함수를 붙인다. 필드별로 다른 DB·서비스에서 모아 오는 통합 계층으로도 쓴다
트레이드오프
- 캐싱이 어렵다: URL 단위 HTTP 캐시가 자연스럽지 않아 클라이언트의 정규화 캐시(normalized cache)에 기댄다(브라우저 캐시, TanStack Query)
- N+1이 서버로 옮겨 간다: 리졸버를 순진하게 짜면 목록의 항목마다 쿼리가 나간다. DataLoader로 묶어 처리한다(N+1 문제)
- 복잡도 제어: 클라이언트가 깊은 중첩 쿼리를 만들 수 있어 깊이·비용 제한이 필요하다
- 리소스가 단순하고 클라이언트가 하나면 REST가 더 간단하다
NestJS는 코드 우선(code-first)·스키마 우선(schema-first) 방식 모두를 지원한다. 내부 서비스 간 통신이라면 gRPC가 대안이다.
출처: GraphQL 학습 문서 · Introspection · Performance: The N+1 Problem · Facebook Engineering: GraphQL: A data query language (2015)