← 블로그로 돌아가기

GraphQL이란 무엇인가

GraphQL은 클라이언트가 필요한 데이터만 효율적으로 요청하게 해 주지만, 캐싱과 성능 측면에서 고려할 점도 있습니다.

GraphQL은 클라이언트와 서버가 통신하는 여러 방식 중 하나입니다. RESTful API만큼 널리 사용되지는 않지만 뚜렷한 장점 덕분에 다양한 곳에서 활용되고 있습니다.

먼저 GraphQL이 REST API와 어떻게 다른지 살펴보겠습니다. 일반적인 REST API는 GET, POST, PUT, DELETE 메서드를 사용합니다.

# GET 요청 예시(사용자 정보 조회)
GET /api/users/123

# POST 요청 예시(새 사용자 생성)
POST /api/users
{
	"name": "John Doe",
	"email": "john@example.com"
}

# PUT 요청 예시(사용자 정보 수정)
PUT /api/users/123
{
	"name": "Jane Smith",
	"email": "jane@example.com"
}

# DELETE 요청 예시(사용자 삭제)
DELETE /api/users/123

반면 GraphQL은 모든 요청에 POST 메서드를 사용합니다.

# 하나의 엔드포인트에서 모든 작업 처리
POST /graphql
{
  "query": `
    query {
      user(id: 123) {
        name
        email
      }
    }
  `
}

# 사용자 생성 Mutation
POST /graphql
{
  "query": `
    mutation {
      createUser(name: "John Doe", email: "john@example.com") {
        id
        name
        email
      }
    }
  `
}

# 사용자 정보 수정 Mutation
POST /graphql
{
  "query": `
    mutation {
      updateUser(id: 123, name: "Jane Smith", email: "jane@example.com") {
        id
        name
        email
      }
    }
  `
}

# 사용자 삭제 Mutation
POST /graphql
{
  "query": `
    mutation {
      deleteUser(id: 123) {
        success
      }
    }
  `
}

GraphQL은 POST 요청의 query 본문에 수행할 작업과 응답으로 받을 값을 함께 담아 전송합니다. 예를 들어 아래 쿼리는 ID가 123인 사용자를 지정하고 그 사용자의 이름과 이메일을 응답으로 요청합니다.

{
  "query": `
    query {
      user(id: 123) {
        name
        email
      }
    }
  `
}

GraphQL이 해결하려는 문제

1. Over-fetching: 서버에서 너무 많은 데이터 가져오기

사용자의 ID만 필요한데 REST API가 이름, 이메일, 주소까지 모두 반환한다면 클라이언트는 불필요한 데이터까지 받게 됩니다. GraphQL에서는 쿼리에 필요한 ID만 작성해 원하는 데이터만 가져올 수 있습니다.

2. Under-fetching: 서버에서 너무 적은 데이터 가져오기

사용자 테이블의 특정 사용자와 그 사용자가 속한 그룹의 이름을 함께 가져오려면 REST API에서는 보통 두 번 요청해야 합니다. 하나의 API에서 전부 반환하게 만들 수도 있지만, 대규모 프로젝트에서 모든 조합의 REST API를 일일이 작성하는 것은 비효율적입니다. GraphQL을 사용하면 원하는 데이터를 한 번의 요청으로 가져올 수 있습니다.

{
  "query": `
    query {
      user(id: 123) {
        id
        groups {
          name
        }
      }
    }
  `
}

실제로 over-fetching과 under-fetching은 함께 발생하는 경우가 많습니다. 사용자 ID만 필요한데 사용자 정보를 전부 받고, 그룹 이름만 필요한데 그룹 정보를 전부 받으면서도 원하는 결과를 얻기 위해 API를 두 번 호출해야 하는 식입니다.

GraphQL 작업 유형

  1. Query: 데이터를 조회합니다.
  2. Mutation: 데이터를 생성, 수정, 삭제합니다.
  3. Subscription: 데이터가 변경될 때 실시간 알림을 받습니다.

채팅 애플리케이션에서 새 메시지가 도착했을 때 실시간으로 알림을 받는 상황이 Subscription의 대표적인 예입니다. REST API에서는 클라이언트가 서버에 주기적으로 데이터를 요청해야 하지만, GraphQL에서는 데이터가 바뀔 때 서버가 클라이언트에 알릴 수 있습니다.

Subscription은 대략 다음과 같이 구현합니다.

  1. 서버: 스키마에 Subscription 타입을 정의하고 resolver를 구현한 뒤 WebSocket 등으로 클라이언트를 연결합니다.
  2. 클라이언트: Subscription 쿼리와 WebSocket 연결을 설정하고 서버에서 받은 실시간 업데이트를 처리합니다.

GraphQL의 단점

GraphQL은 클라이언트가 원하는 데이터만 가져올 수 있고 서버가 요구사항마다 별도 API를 만들 필요가 없다는 장점이 있습니다. 하지만 다음과 같은 단점도 있습니다.

  1. 캐싱이 어렵습니다. 요청마다 형태가 달라지는 복잡한 쿼리로 작성되기 때문입니다.
  2. 성능 문제가 생길 수 있습니다. 복잡하고 다양한 쿼리를 해석하는 과정이 서버에 부담을 줄 수 있습니다.

GraphQL에 적합한 서비스

  1. 데이터 모델이 복잡하고 방대한 서비스: over-fetching과 under-fetching이 자주 발생한다면 GraphQL이 유리할 수 있습니다.
  2. 클라이언트가 데이터 요청을 폭넓게 제어해야 하는 서비스: 여러 형태의 요청이 필요할 때 효율적으로 대응할 수 있습니다.
  3. 데이터 변경이 잦고 실시간 응답이 필요한 서비스: Subscription으로 실시간 업데이트를 받을 수 있습니다.

주요 기업의 활용 사례

  • Facebook: 다양한 기기와 네트워크 환경에서 성능 최적화
  • Shopify: 여러 스토어 테마와 앱에 맞춘 데이터 제공
  • GitHub: 저장소, 이슈, Pull Request 등 복잡한 데이터 구조를 유연하게 조회

GraphQL을 사용해 만든 축구팀 관리 프로그램