2025-08-24 13:52
-
GraphQL은 클라이언트가 필요한 데이터만 정확하게 요청할 수 있도록 페이스북이 개발한 API 쿼리 언어입니다.
-
기존 REST API의 ‘오버페칭(Over-fetching)‘과 ‘언더페칭(Under-fetching)’ 문제를 해결하여 네트워크 효율성을 극대화합니다.
-
강력한 타입 시스템과 스키마를 기반으로 하여 서버와 클라이언트 간의 명확한 소통과 안정적인 개발을 가능하게 합니다.
GraphQL 완벽 정복 핸드북 API의 미래를 만나다
오늘날 우리가 사용하는 대부분의 애플리케이션은 데이터를 주고받기 위해 API(Application Programming Interface)에 의존합니다. 오랫동안 API 세계의 표준은 REST(Representational State Transfer)였습니다. 하지만 모바일 시대가 도래하고, 다양한 디바이스와 네트워크 환경이 등장하면서 REST 방식은 몇 가지 뚜렷한 한계를 보이기 시작했습니다. 바로 이 지점에서 페이스북의 고민이 시작되었고, 그 결과물로 GraphQL이 탄생했습니다. 이 핸드북에서는 GraphQL이 왜 만들어졌으며, 어떤 구조를 가지고 있고, 어떻게 사용하는지, 그리고 REST와는 무엇이 다른지 포괄적으로 알아보겠습니다.
1. GraphQL의 탄생 배경 필요한 것만 골라 담는 똑똑한 장바구니
2012년, 페이스북은 모바일 앱으로의 전환에 박차를 가하고 있었습니다. 하지만 당시 사용하던 REST API는 모바일 환경에 최적화되어 있지 않았습니다. REST는 서버가 미리 정해놓은 ‘자원(Resource)’ 단위로만 데이터를 제공할 수 있었습니다.
이를 식당에 비유해 볼까요? REST는 정해진 ‘세트 메뉴’와 같습니다. 저는 스테이크와 샐러드만 먹고 싶은데, 세트 메뉴에는 원하지 않는 수프와 디저트까지 포함되어 있습니다. 결국 저는 필요 없는 음식까지 모두 받고, 그 비용을 지불해야 합니다. 이것이 바로 오버페칭(Over-fetching), 즉 필요 이상의 데이터를 한꺼번에 받아오는 문제입니다. 모바일처럼 네트워크 속도가 느리고 데이터 사용량이 중요한 환경에서는 치명적인 비효율이었습니다.
반대의 경우도 있었습니다. 사용자의 프로필 정보와 그가 작성한 최근 게시물 3개를 가져오고 싶다고 가정해 봅시다. REST 방식에서는 /users/1
엔드포인트에 요청해서 사용자 정보를 받고, 다시 /users/1/posts
엔드포인트에 요청해서 게시물 목록을 받아와야 합니다. 원하는 정보를 얻기 위해 여러 번 요청을 보내야 하는 것이죠. 이를 **언더페칭(Under-fetching)**이라고 합니다. 여러 번의 왕복은 앱의 로딩 속도를 저하 시키는 주범이었습니다.
페이스북은 이 문제를 해결하기 위해 새로운 접근법을 고안했습니다. “서버가 세트 메뉴를 정해주는 대신, 클라이언트(손님)가 원하는 메뉴만 골라 담을 수 있는 똑똑한 장바구니를 주자!” 이것이 바로 GraphQL의 핵심 아이디어입니다. 클라이언트는 하나의 요청에 자신이 필요한 데이터의 구조를 명확하게 담아 보냅니다. 그러면 서버는 그 요청 구조에 맞춰 정확하게 데이터를 채워서 응답합니다. 더 이상 필요 없는 데이터를 받지도, 여러 번 요청을 보낼 필요도 없어진 것입니다.
2. GraphQL의 핵심 구조 설계도와 요청서
GraphQL은 크게 세 가지 핵심 요소로 구성됩니다. 바로 스키마(Schema), 쿼리(Query), 그리고 **뮤테이션(Mutation)**입니다.
2.1. 스키마 (Schema) API의 청사진
스키마는 GraphQL API의 모든 기능을 정의하는 ‘설계도’ 또는 ‘타입 시스템’입니다. 서버가 어떤 데이터를 제공할 수 있는지, 그리고 클라이언트가 어떤 데이터를 요청할 수 있는지를 엄격한 타입(Type)으로 정의합니다.
예를 들어, 블로그 애플리케이션의 스키마를 상상해 봅시다.
# 사용자를 나타내는 타입
type User {
id: ID!
name: String!
email: String
}
# 게시물을 나타내는 타입
type Post {
id: ID!
title: String!
content: String!
author: User! # 게시물 작성자는 User 타입
}
# 클라이언트가 데이터를 요청(Query)할 때 사용할 수 있는 진입점
type Query {
post(id: ID!): Post
allUsers: [User!]
}
-
type
키워드로 데이터 객체의 종류를 정의합니다. -
User
와Post
는 각각 사용자 정보와 게시물 정보를 담는 객체 타입입니다. -
id: ID!
,name: String!
처럼 각 필드(field)는 이름과 타입을 가집니다.!
는 ‘반드시 값이 있어야 한다(Non-nullable)‘는 뜻입니다. -
Query
타입은 클라이언트가 데이터를 읽기 위해 사용할 수 있는 API의 ‘진입점(Entry Point)‘을 정의합니다.post(id: ID!)
는 ID를 받아 특정 게시물을 반환하는 API이고,allUsers
는 모든 사용자 목록을 반환하는 API입니다.
이처럼 스키마는 서버와 클라이언트 간의 강력한 계약서 역할을 합니다. 클라이언트는 스키마를 통해 어떤 데이터를 요청할 수 있는지 명확히 알 수 있고, 서버는 스키마에 정의된 대로만 응답하면 되므로 개발 효율성과 안정성이 크게 향상됩니다.
2.2. 쿼리 (Query) 데이터 요청서
쿼리는 클라이언트가 서버로부터 데이터를 읽기(Read) 위해 보내는 요청입니다. 앞서 정의한 스키마 구조를 따라, 원하는 필드만 콕 집어서 요청할 수 있습니다.
예를 들어, ID가 “1”인 게시물의 제목과 그 게시물을 작성한 사람의 이름만 가져오고 싶다면 다음과 같이 쿼리를 작성합니다.
요청 (Request):
query {
post(id: "1") {
title
author {
name
}
}
}
응답 (Response):
{
"data": {
"post": {
"title": "GraphQL 완벽 정복 핸드북",
"author": {
"name": "홍길동"
}
}
}
}
놀랍지 않나요? 요청의 구조와 응답의 구조가 정확히 일치합니다. post
의 content
나 author
의 email
처럼 요청하지 않은 데이터는 응답에 포함되지 않습니다. 이것이 바로 GraphQL이 오버페칭 문제를 해결하는 방식입니다.
2.3. 뮤테이션 (Mutation) 데이터 변경서
데이터를 읽는 것이 쿼리라면, 데이터를 생성(Create), 수정(Update), 삭제(Delete) 하는 역할은 뮤테이션이 담당합니다. REST에서 POST
, PUT
, DELETE
메서드가 하던 일이죠.
새로운 사용자를 생성하는 뮤테이션을 예로 들어보겠습니다.
요청 (Request):
mutation {
createUser(name: "이순신", email: "lee@example.com") {
id
name
}
}
응답 (Response):
{
"data": {
"createUser": {
"id": "2",
"name": "이순신"
}
}
}
뮤테이션 역시 쿼리처럼, 작업이 완료된 후 반환받고 싶은 데이터를 직접 지정할 수 있습니다. 위 예시에서는 새로 생성된 사용자의 id
와 name
을 바로 응답으로 받아 클라이언트에서 즉시 활용할 수 있습니다.
3. GraphQL vs REST 무엇이 다른가?
GraphQL과 REST는 API를 설계하는 서로 다른 패러다임입니다. 둘 중 어느 하나가 절대적으로 우월하다기보다는, 상황에 맞는 도구를 선택하는 것이 중요합니다.
특징 | REST | GraphQL |
---|---|---|
엔드포인트 | 여러 개의 엔드포인트 (e.g., /users , /posts/:id ) | 단 하나의 엔드포인트 (e.g., /graphql ) |
데이터 요청 | 서버가 정의한 자원 구조대로 전체 데이터를 받음 | 클라이언트가 필요한 데이터의 구조를 직접 정의하여 요청 |
오버페칭/언더페칭 | 발생 가능성이 높음 | 근본적으로 해결 |
요청 방식 | HTTP 메서드(GET, POST, PUT, DELETE)로 동작 구분 | query (읽기)와 mutation (쓰기)으로 동작 구분 |
타입 시스템 | 별도의 타입 시스템이 없음 (OpenAPI 등으로 보완) | 강력한 타입 시스템(스키마)을 내장하여 안정성 확보 |
문서화 | 별도의 도구(e.g., Swagger) 필요 | 스키마 자체가 API 문서 역할을 하여 자동 문서화 가능 |
언제 GraphQL을 선택해야 할까?
-
다양한 클라이언트 환경: 모바일, 웹, 태블릿 등 여러 플랫폼에서 각기 다른 데이터가 필요할 때 GraphQL은 빛을 발합니다.
-
마이크로서비스 아키텍처: 여러 서비스에 흩어져 있는 데이터를 하나의 API 게이트웨이를 통해 조합하여 제공해야 할 때 유용합니다.
-
네트워크 효율성이 중요할 때: 느린 네트워크 환경이나 데이터 사용량에 민감한 모바일 앱에 적합합니다.
-
빠른 프론트엔드 개발: 프론트엔드 개발자가 백엔드의 변경을 기다릴 필요 없이 필요한 데이터를 직접 구성하여 개발 속도를 높일 수 있습니다.
4. 심화 내용 더 깊이 알아보기
4.1. 리졸버 (Resolver)
리졸버는 GraphQL 스키마의 각 필드에 대한 데이터를 실제로 가져오는 함수입니다. 클라이언트가 쿼리를 보내면, GraphQL 서버는 쿼리의 각 필드에 해당하는 리졸버를 실행하여 데이터를 채워 넣습니다. 리졸버는 데이터베이스에 직접 쿼리하거나, 다른 REST API를 호출하거나, 정적인 데이터를 반환하는 등 어떤 로직이든 수행할 수 있습니다.
4.2. 서브스크립션 (Subscription)
쿼리와 뮤테이션이 단발성 요청이라면, 서브스크립션은 실시간 업데이트를 위한 기능입니다. 클라이언트가 특정 이벤트를 ‘구독’하고 있으면, 서버에서 해당 이벤트가 발생할 때마다 클라이언트에게 데이터를 푸시(push)해줍니다. 채팅 앱의 새 메시지 알림이나, 주식 시세 변동 같은 실시간 기능 구현에 사용됩니다.
5. 결론 API의 새로운 가능성
GraphQL은 REST를 완전히 대체하기 위한 기술이 아닙니다. 오히려 복잡해지는 현대 애플리케이션의 요구사항에 대응하기 위해 등장한 강력한 대안입니다. 클라이언트 중심의 데이터 요청 방식, 강력한 타입 시스템, 그리고 뛰어난 네트워크 효율성은 개발자에게 더 나은 개발 경험과 사용자에게 더 빠른 애플리케이션을 제공할 수 있는 새로운 가능성을 열어주었습니다.
이 핸드북을 통해 GraphQL의 기본 개념과 철학을 이해하셨기를 바랍니다. 이제 직접 작은 프로젝트에 GraphQL을 도입하여, 필요한 데이터만 쏙쏙 골라 담는 똑똑한 API의 매력을 직접 경험해 보시는 것은 어떨까요?