GraphQL

GraphQL은 API를 위한 쿼리 언어이자 서버 측에서 데이터를 효율적으로 가져오기 위한 런타임 환경이다. 2012년 Facebook에서 내부적으로 개발되었고, 2015년에 공개적으로 출시되었다.

주요 특징:

GraphQL Vs REST

REST API와 비교했을 때 GraphQL의 주요 차이점:

특성GraphQLREST
엔드포인트단일 엔드포인트다중 엔드포인트
데이터 가져오기필요한 것만 정확히 요청과도하거나 부족한 데이터 전송 가능
버전 관리점진적 진화 가능일반적으로 명시적 버전 필요
상태 코드항상 200 OK (오류는 응답 내에)다양한 HTTP 상태 코드
캐싱복잡함 (별도 솔루션 필요)HTTP 캐싱 활용

GraphQL의 핵심 개념

스키마와 타입 시스템

GraphQL API는 스키마를 통해 정의된다. 스키마는 사용 가능한 모든 데이터 타입과 관계, 작업을 설명한다.

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

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

3.2 주요 작업 유형

GraphQL에는 세 가지 주요 작업 유형이 있다:

  1. Query: 데이터 읽기(GET)
  2. Mutation: 데이터 생성/수정/삭제(POST, PUT, DELETE)
  3. Subscription: 실시간 데이터 업데이트(WebSocket)
실제 작업 예시
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 쿼리 예시
query {
  user(id: "123") {
    name
    email
    posts {
      title
    }
  }
}

# 뮤테이션 예시
mutation {
  createPost(title: "GraphQL 소개", content: "GraphQL은 API를 위한 쿼리 언어입니다.", authorId: "123") {
    id
    title
  }
}

GraphQL의 장단점

장점

단점

GraphQL의 고급 기능

N+1 문제 해결 - DataLoader

GraphQL에서는 중첩된 필드를 가져올 때 N+1 쿼리 문제가 발생할 수 있다. 이를 해결하기 위해 DataLoader를 사용한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// DataLoader 예제
const DataLoader = require('dataloader');

const userLoader = new DataLoader(async (userIds) => {
  // 한 번의 데이터베이스 호출로 여러 사용자 가져오기
  const users = await database.getUsers(userIds);
  
  // DataLoader는 반환되는 배열이 요청된 ID와 동일한 순서여야 함
  return userIds.map(id => users.find(user => user.id === id));
});

// 리졸버에서 사용
const resolvers = {
  Post: {
    author: async (post, _, context) => {
      return context.userLoader.load(post.authorId);
    }
  }
};

디렉티브 (Directives)

디렉티브는 필드나 프래그먼트에 추가적인 처리 지시를 제공한다.

1
2
3
4
5
6
7
8
9
query GetUserWithDirective($withPosts: Boolean!) {
  user(id: "1") {
    name
    email
    posts @include(if: $withPosts) {
      title
    }
  }
}

인터페이스와 유니온 타입

복잡한 데이터 구조를 모델링하기 위해 인터페이스와 유니온 타입을 사용할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
interface Node {
  id: ID!
}

type User implements Node {
  id: ID!
  name: String!
}

type Post implements Node {
  id: ID!
  title: String!
}

union SearchResult = User | Post

type Query {
  node(id: ID!): Node
  search(query: String!): [SearchResult!]!
}

GraphQL 구현 및 도구

서버 측 구현

다양한 언어로 GraphQL 서버를 구현할 수 있다:

클라이언트 측 구현

클라이언트에서 GraphQL을 사용하기 위한 라이브러리:

개발 도구

GraphQL 개발을 돕는 도구들:

GraphQL 구현 예시 (Node.js)

간단한 GraphQL 서버 구현 예시 (Apollo Server 사용):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 필요한 패키지 임포트
const { ApolloServer, gql } = require('apollo-server');

// 스키마 정의
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
  }
  
  type Query {
    users: [User]
    user(id: ID!): User
  }
`;

// 가상 데이터베이스
const users = [
  { id: '1', name: '김철수', email: 'kim@example.com' },
  { id: '2', name: '이영희', email: 'lee@example.com' },
];

// 리졸버 함수 정의
const resolvers = {
  Query: {
    users: () => users, // 모든 사용자 반환
    user: (_, args) => users.find(user => user.id === args.id) // ID로 사용자 찾기
  },
};

// 서버 생성 및 시작
const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

용어 정리

용어설명

참고 및 출처