Post

GraphQL

GraphQL이란?

API의 쿼리 언어이자 데이터에 대한 요청을 실행하는 런타임

REST와 달리 GraphQL은 여러 엔드포인트를 노출하는 대신 정확히 필요한 데이터를 쿼리할 수 있는 단일 엔드포인트를 노출함

GraphQL의 주요 개념

  1. 스키마 정의 언어 (Schema Definition Language, SDL)
    • 스키마 (Schema)
      • GraphQL 스키마는 API에서 유형과 그들 간의 관계를 정의함
      • 클라이언트와 서버 간의 계약으로 작용함
    • 유형: 스키마의 기본 단위
      • 객체 타입 (Object Types): 특정 객체의 필드
      • 스칼라 타입 (Scalar Types): 기본 데이터 타입(예: String, Int, Float, Boolean, ID).
      • 열거형 타입 (Enum Types): 미리 정의된 값 집합을 나열한 필드
      • 인터페이스 (Interfaces): 여러 타입에서 공통적으로 사용되는 필드
      • 유니온 (Unions): 여러 객체 타입 중 하나를 반환할 수 있는 타입
      • 입력 타입 (Input Types): 뮤테이션에서 인자로 사용되는 객체 타입
  2. 쿼리 (Query)
    • 클라이언트가 데이터를 요청하는 방법
    • 필요한 데이터와 구조를 정확히 지정하여 클라이언트가 단일 요청에서 필요한 모든 데이터를 가져올 수 있음
    • 예시 쿼리:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      {
        user(id: "1") {
          name
          age
          posts {
            title
          }
        }
      }
      
  3. 뮤테이션 (Mutation)
    • 클라이언트가 서버의 데이터를 수정하는 방법
    • 데이터를 생성, 업데이트 또는 삭제할 수 있음
    • 예시 뮤테이션:
      1
      2
      3
      4
      5
      6
      
      mutation {
        createUser(input: { name: "John", age: 28 }) {
          id
          name
        }
      }
      
  4. 리졸버 (Resolver)
    • 스키마의 각 필드에 대한 데이터 검색 로직을 처리하는 함수
    • 쿼리에 따라 필드의 값을 해결함
  5. 구독 (Subscription)
    • 서버와의 지속적인 연결을 유지하여 서버가 실시간 업데이트를 클라이언트에게 보낼 수 있게 함

GraphQL의 장점

  1. 클라이언트 지정 쿼리
    • 클라이언트는 필요한 데이터를 정확하게 요청할 수 있으므로 over-fetching과 under-fetching를 피할 수 있음
  2. Strongly Typed Schema
    • 스키마와 유형을 통해 쿼리가 유형 시스템에 대해 유효성을 검사하므로 오류가 줄어들고 개발자 경험이 향상됨
  3. 단일 엔드포인트
    • REST와 달리 GraphQL은 모든 작업에 대해 단일 엔드포인트를 사용하므로 API를 단순화할 수 있음
  4. 유연성과 효율성
    • 클라이언트는 서버와 독립적으로 진화할 수 있으며 개발자는 기존 쿼리에 영향을 주지 않고 새로운 필드와 유형을 추가할 수 있음
  5. Introspection
    • GraphQL API는 자체 문서화되므로 클라이언트는 유형 및 작업을 이해하기 위해 스키마 자체를 쿼리할 수 있음

예시 스키마 정의

** 쿼리 (Query)**

1
2
3
4
5
6
7
{
  user(id: "1") {
    id
    name
    age
  }
}

뮤테이션 (Mutation)

1
2
3
4
5
6
7
mutation {
  createUser(name: "Alice", age: 30) {
    id
    name
    age
  }
}

구독 (Subscription)

1
2
3
4
5
6
7
subscription {
  userCreated {
    id
    name
    age
  }
}

프래그먼트 (Fragments)

재사용 가능한 쿼리 조각

1
2
3
4
5
6
7
8
9
10
11
fragment userFields on User {
  id
  name
  age
}

{
  user(id: "1") {
    ...userFields
  }
}

변수 (Variables)

쿼리나 뮤테이션에서 동적 값을 전달하는 데 사용됨

1
2
3
4
5
6
7
query getUser($id: ID!) {
  user(id: $id) {
    id
    name
    age
  }
}
  • 쿼리 호출 시 변수 제공:
    1
    2
    3
    
    {
      "id": "1"
    }
    

디렉티브 (Directives)

쿼리의 실행 방식을 제어하는 데 사용됨

  • @include: 조건에 따라 필드 포함
    1
    2
    3
    4
    5
    6
    7
    
    query getUser($withEmail: Boolean!) {
      user(id: "1") {
        id
        name
        email @include(if: $withEmail)
      }
    }
    
  • @skip: 조건에 따라 필드 생략
    1
    2
    3
    4
    5
    6
    7
    
    query getUser($withoutEmail: Boolean!) {
      user(id: "1") {
        id
        name
        email @skip(if: $withoutEmail)
      }
    }
    

에일리어스 (Aliases)

에일리어스를 사용하여 동일한 필드를 여러 번 요청할 때 별칭을 부여할 수 있음

1
2
3
4
5
6
7
8
9
10
{
  user1: user(id: "1") {
    id
    name
  }
  user2: user(id: "2") {
    id
    name
  }
}

내장 타입 (Scalar Types)

  • Int: 정수
  • Float: 부동 소수점 숫자
  • String: 문자열
  • Boolean: 참/거짓 값
  • ID: 고유 식별자

커스텀 타입 (Custom Types)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type User {
  id: ID!
  name: String!
  age: Int
  email: String
  posts: [Post]
}

type Post {
  id: ID!
  title: String!
  content: String
  author: User
}

인풋 타입 (Input Types)

뮤테이션 인자로 사용되는 객체 타입

1
2
3
4
5
6
7
8
input CreateUserInput {
  name: String!
  age: Int
}

type Mutation {
  createUser(input: CreateUserInput): User
}

인터페이스 (Interfaces)

여러 타입에 공통으로 사용되는 필드를 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Character {
  id: ID!
  name: String!
}

type Human implements Character {
  id: ID!
  name: String!
  starships: [Starship]
}

type Droid implements Character {
  id: ID!
  name: String!
  primaryFunction: String
}

유니온 타입 (Union Types)

여러 타입 중 하나를 반환할 수 있음

1
2
3
4
5
union SearchResult = Human | Droid

type Query {
  search(text: String!): [SearchResult]
}

스키마 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

type Query {
  user(id: ID!): User
  allUsers: [User]
}

type Mutation {
  createUser(name: String!, age: Int): User
}

type Subscription {
  userCreated: User
}

REST와 비교

  • 유연성: REST 엔드포인트는 고정된 데이터 구조를 반환하지만 GraphQL 쿼리는 요청된 데이터만 반환함
  • 효율성: GraphQL은 클라이언트가 관련 데이터를 단일 요청에서 가져오도록 하므로 네트워크 요청 수를 최소화함
  • 개발 속도: GraphQL의 유형 시스템과 자체 문서화 특성은 개발 및 디버깅을 가속화됨
This post is licensed under CC BY 4.0 by the author.