2025-08-30 01:11

  • GraphQL은 클라이언트가 필요한 데이터만 정확하게 요청할 수 있게 해, REST API의 오버페칭 및 언더페칭 문제를 해결합니다.

  • 스키마 정의 언어(SDL)를 통해 API의 구조를 강력한 타입 시스템으로 정의하고, 모든 요청은 단일 엔드포인트로 처리됩니다.

  • 쿼리(데이터 조회), 뮤테이션(데이터 변경), 서브스크립션(실시간 업데이트)을 통해 모든 데이터 상호작용을 명확하게 관리합니다.

GraphQL 완벽 핸드북 REST API를 넘어선 새로운 시대

우리가 웹과 앱을 통해 매일 소비하는 데이터는 어디서 오는 걸까요? 대부분은 서버에 저장된 데이터를 ‘API’라는 통신 규칙을 통해 가져옵니다. 오랫동안 이 분야의 왕은 ‘REST API’였습니다. 하지만 모바일 시대가 열리고, 다양한 디바이스에서 더 효율적인 데이터 통신이 필요해지면서 새로운 강자가 등장했습니다. 바로 GraphQL입니다.

GraphQL은 단순히 새로운 기술이 아니라, 데이터를 요청하고 다루는 방식에 대한 근본적인 생각의 전환을 제시합니다. 이 핸드북에서는 GraphQL이 왜 만들어졌는지, 어떤 구조로 이루어져 있으며 어떻게 사용하는지, 그리고 REST API와는 무엇이 다른지 A부터 Z까지 상세하게 파헤쳐 보겠습니다.

1. 문제의 시작 그리고 GraphQL의 탄생 배경

모든 위대한 발명은 ‘필요’와 ‘문제 해결’에서 시작됩니다. GraphQL도 마찬가지였습니다. 2012년, 페이스북은 웹 중심의 서비스를 모바일로 전환하는 과정에서 심각한 문제에 직면했습니다.

REST API의 한계점

당시 표준처럼 사용되던 REST API는 페이스북의 복잡한 모바일 뉴스피드를 효율적으로 처리하기에 몇 가지 뚜렷한 한계가 있었습니다.

1. 오버페칭 (Over-fetching)

‘오버페칭’은 필요 이상으로 많은 데이터를 받아오는 문제입니다. 예를 들어, 사용자의 이름만 필요한데 REST API는 /users/{id} 엔드포인트에서 이름, 이메일, 주소, 생년월일 등 모든 사용자 정보를 통째로 보내줍니다. 모바일처럼 네트워크 속도와 데이터 사용량이 중요한 환경에서는 이는 엄청난 낭비였습니다.

2. 언더페칭 (Under-fetching)

‘언더페칭’은 반대로 필요한 데이터를 한 번에 다 받지 못하는 문제입니다. 페이스북 뉴스피드를 생각해보죠. 게시글 정보, 작성자 정보, ‘좋아요’를 누른 사람들의 목록, 그리고 각 댓글 정보가 필요합니다. REST API 환경에서는 이걸 한 번에 가져올 방법이 없습니다.

  • GET /posts/{id}: 게시글 정보 가져오기

  • GET /users/{userId}: 작성자 정보 가져오기

  • GET /posts/{id}/likes: ‘좋아요’ 목록 가져오기

  • GET /posts/{id}/comments: 댓글 목록 가져오기

이렇게 여러 번 서버에 요청을 보내야만 화면 하나를 완성할 수 있습니다. 이는 앱의 로딩 속도를 현저히 저하시키는 원인이었습니다.

해결사 GraphQL의 등장

페이스북은 이 문제를 해결하기 위해 내부적으로 새로운 쿼리 언어를 개발했고, 2015년에 이를 오픈소스로 공개했습니다. 이것이 바로 GraphQL(Graph Query Language)입니다.

GraphQL의 핵심 철학은 간단합니다. “서버가 데이터 구조를 정의하되, 클라이언트가 필요한 것만 정확하게 요청한다.” 서버가 메뉴판(메뉴, 가격, 원산지) 전체를 던져주는 것이 아니라, 클라이언트가 “스테이크랑 와인 가격만 알려줘”라고 콕 집어 물어보면 서버가 정확히 그 두 정보만 알려주는 방식입니다. 이로써 오버페칭과 언더페칭 문제가 동시에 해결되었습니다.

2. GraphQL의 핵심 구조 이해하기

GraphQL은 어떻게 이런 마법 같은 일을 가능하게 할까요? 그 비밀은 강력한 타입 시스템과 스키마에 있습니다.

스키마 정의 언어 (Schema Definition Language, SDL)

GraphQL API의 중심에는 ‘스키마(Schema)‘가 있습니다. 스키마는 API가 제공할 수 있는 모든 데이터의 종류와 그 관계를 정의한 ‘청사진’ 또는 ‘설계도’입니다. 이 스키마는 SDL이라는 특별한 문법으로 작성됩니다.

# 사용자를 나타내는 타입입니다.
type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]
}

# 게시글을 나타내는 타입입니다.
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}

# 데이터를 조회하는 방법을 정의합니다.
type Query {
  allPosts: [Post!]
  post(id: ID!): Post
  user(id: ID!): User
}

위 스키마를 통해 우리는 이 API가 UserPost라는 두 가지 데이터 타입을 다루고, 모든 게시글을 조회하거나 특정 ID로 게시글 또는 사용자를 조회할 수 있다는 것을 명확히 알 수 있습니다.

핵심 구성 요소

  1. 타입 (Type): API 데이터의 기본 단위입니다. User, Post처럼 객체 형태를 정의할 수 있습니다.

  2. 필드 (Field): 타입 안에 들어가는 속성입니다. User 타입의 id, name 등이 필드에 해당합니다.

  3. 스칼라 타입 (Scalar Types): 더 이상 쪼갤 수 없는 기본 데이터 타입입니다.

    • Int: 정수

    • Float: 실수

    • String: 문자열

    • Boolean: true 또는 false

    • ID: 고유 식별자. 문자열로 직렬화되지만, 고유함을 보장하는 특별한 타입입니다.

  4. 쿼리, 뮤테이션, 서브스크립션 (Query, Mutation, Subscription)

    • Query: 데이터를 읽거나 조회할 때 사용합니다. (REST의 GET)

    • Mutation: 데이터를 생성, 수정, 삭제할 때 사용합니다. (REST의 POST, PUT, PATCH, DELETE)

    • Subscription: 실시간으로 데이터 변경을 감지하고 받아올 때 사용합니다. (채팅, 실시간 알림 등)

  5. 리졸버 (Resolver) 스키마가 API의 ‘무엇(What)‘을 정의한다면, 리졸버는 ‘어떻게(How)‘를 담당합니다. 리졸버는 특정 필드에 대한 요청이 왔을 때, 실제로 데이터베이스에서 데이터를 가져오거나 다른 API를 호출하는 등의 로직을 수행하는 함수입니다. 스키마의 각 필드는 대응하는 리졸버 함수를 가집니다.

3. GraphQL 사용법 요청과 응답

GraphQL의 이론을 알았으니, 이제 실제로 어떻게 사용하는지 살펴보겠습니다. GraphQL의 가장 큰 특징 중 하나는 모든 요청이 단 하나의 엔드포인트(예: /graphql)로 보내진다는 것입니다.

쿼리 (Query) - 데이터 조회하기

사용자 ID가 “1”인 사람의 이름과 그가 작성한 게시글의 제목만 가져오고 싶다고 가정해봅시다.

요청 (Request):

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

응답 (Response):

{
  "data": {
    "user": {
      "name": "John Doe",
      "posts": [
        {
          "title": "GraphQL 입문"
        },
        {
          "title": "REST API와의 비교"
        }
      ]
    }
  }
}

보시다시피, 요청한 구조와 동일한 모양으로 JSON 응답이 돌아옵니다. 이메일이나 게시글 내용처럼 요청하지 않은 데이터는 전혀 포함되지 않았습니다. 이것이 GraphQL의 가장 강력한 장점입니다.

뮤테이션 (Mutation) - 데이터 변경하기

새로운 게시글을 작성하는 뮤테이션을 실행해 보겠습니다.

요청 (Request):

mutation {
  createPost(title: "새로운 게시글", content: "내용입니다.", authorId: "1") {
    id
    title
    author {
      name
    }
  }
}

응답 (Response):

{
  "data": {
    "createPost": {
      "id": "3",
      "title": "새로운 게시글",
      "author": {
        "name": "John Doe"
      }
    }
  }
}

뮤테이션은 데이터를 변경한 후, 변경된 객체의 특정 필드를 다시 조회하여 반환할 수 있습니다. 덕분에 클라이언트는 데이터 생성 후 화면을 갱신하기 위해 별도의 요청을 보낼 필요가 없습니다.

4. 심화 GraphQL vs REST

GraphQL과 REST는 API를 설계하는 서로 다른 패러다임입니다. 어느 하나가 절대적으로 우월하다기보다는, 상황에 맞는 장단점을 가집니다.

특징GraphQLREST
엔드포인트단일 엔드포인트 (e.g., /graphql)다중 엔드포인트 (e.g., /users, /posts)
데이터 요청클라이언트가 필요한 데이터 구조를 명시서버가 정의한 고정된 데이터 구조
데이터 페칭오버페칭/언더페칭 문제 해결오버페칭/언더페칭 문제 발생 가능
HTTP 메서드주로 POST 하나만 사용GET, POST, PUT, DELETE 등 명시적 사용
스키마/문서화강력한 타입 시스템 내장, 스키마 자체가 문서별도의 문서화 도구 필요 (e.g., Swagger)
버전 관리일반적으로 필요 없음 (필드 추가/제거로 대응)엔드포인트에 버전 명시 (e.g., /v1/users)
캐싱복잡함 (HTTP 캐싱 활용 어려움)단순함 (URL 기반의 HTTP 캐싱 활용 용이)

언제 GraphQL을 사용해야 할까?

  • 다양한 클라이언트 환경: 모바일, 웹, 데스크톱 등 여러 플랫폼에서 각기 다른 데이터가 필요할 때.

  • 복잡한 데이터 관계: 소셜 네트워크처럼 데이터 모델이 복잡하고 중첩된 구조를 가질 때.

  • 네트워크 최적화가 중요할 때: 모바일 앱처럼 데이터 사용량을 최소화해야 할 때.

언제 REST가 더 나을까?

  • 간단한 CRUD API: 복잡한 관계없이 단순한 리소스를 다루는 경우.

  • HTTP 캐싱을 적극 활용해야 할 때: 공개 API나 정적 콘텐츠가 많아 캐싱이 매우 중요한 경우.

  • 파일 업로드/다운로드: HTTP의 기능을 직접적으로 활용하는 것이 더 직관적일 수 있습니다.

5. GraphQL 생태계와 미래

GraphQL은 단순히 쿼리 언어가 아니라 거대한 생태계를 이루고 있습니다.

  • Apollo: GraphQL을 위한 가장 인기 있는 플랫폼입니다. 서버와 클라이언트 라이브러리, 캐싱, 상태 관리 등 GraphQL 개발에 필요한 모든 것을 제공합니다.

  • Relay: 페이스북이 만든 또 다른 클라이언트 라이브러리로, 성능과 데이터 일관성에 중점을 둡니다.

  • DataLoader: N+1 문제를 해결하기 위한 유틸리티입니다. 데이터베이스에 대한 중복 쿼리를 배치(batch) 처리하여 성능을 최적화합니다.

결론

GraphQL은 현대 애플리케이션 개발의 복잡성에 대한 우아한 해답입니다. 클라이언트에게 데이터 요청의 주도권을 넘겨줌으로써, 개발 속도를 높이고 네트워크 효율을 극대화하며, 강력한 타입 시스템을 통해 예측 가능하고 안정적인 API를 구축할 수 있게 해줍니다.

물론 GraphQL이 모든 문제의 만병통치약은 아닙니다. 학습 곡선이 있고, REST에 비해 캐싱 전략이 복잡하다는 단점도 존재합니다. 하지만 마이크로서비스 아키텍처, 모바일 우선 접근법이 대세가 된 오늘날, GraphQL이 제공하는 유연성과 효율성은 개발자들에게 거부할 수 없는 매력으로 다가오고 있습니다. REST의 시대를 지나, 이제 우리는 GraphQL의 시대를 맞이하고 있습니다.

레퍼런스(References)