개발환경

GraphQL 개념 정리와 활용법

ZZJJing 2025. 5. 11. 16:22
반응형

GraphQL 기본 개념

GraphQL은 Facebook이 2015년에 공개한 API 쿼리 언어이자 런타임.

모바일 앱 성능 향상을 위해 만들어졌다고 함.

핵심 아이디어는 클라이언트가 필요한 데이터를 정확히 요청하고 그에 맞게 정확히 응답받는 것.

# 쿼리 예시
query {
  user(id: "123") {
    name
    email
    posts {
      title
    }
  }
}

REST API와 달리 단일 엔드포인트(/graphql)로 모든 요청을 처리하는 방식. 


REST vs GraphQL 차이점

특성  ------------------------REST API ----------------------------------------GraphQL ------------------

엔드포인트 여러 개 단일
데이터 요청 여러 요청 필요할 수 있음 단일 요청으로 해결
응답 구조 서버가 결정 클라이언트가 지정
버전 관리 명시적 (/v1/, /v2/) 점진적 진화
오버페칭 흔히 발생 방지 가능

REST는 여러 리소스에 대해 각각 요청해야 함:

GET /api/users/123
GET /api/users/123/posts

GraphQL은 하나의 쿼리로 해결:

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

핵심 개념

1. 스키마와 타입 시스템

GraphQL의 강력한 타입 시스템으로 API의 모든 데이터 구조를 명확하게 정의.

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]
}

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

!는 non-nullable 필드, []는 배열을 의미.

2. 작업 유형

  • 쿼리(Query): 데이터 읽기 작업
  • 뮤테이션(Mutation): 데이터 생성/수정/삭제
  • 구독(Subscription): 실시간 업데이트

3. 리졸버(Resolver)

각 필드의 데이터를 어떻게 가져올지 정의하는 함수. 데이터베이스, REST API, 다른 GraphQL API 등 다양한 소스에서 데이터 가져올 수 있음.

const resolvers = {
  Query: {
    user: (_, { id }) => database.findUser(id)
  },
  User: {
    posts: (parent) => database.findPostsByUser(parent.id)
  }
};

간단한 GraphQL 서버 만들기

Apollo Server로 구현한 간단한 예시:

const { ApolloServer, gql } = require('apollo-server');

// 타입 정의
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    posts: [Post!]
  }
  
  type Post {
    id: ID!
    title: String!
    author: User!
  }
  
  type Query {
    user(id: ID!): User
    posts: [Post!]!
  }
`;

// 임시 데이터
const users = [
  { id: '1', name: '김개발' }
];

const posts = [
  { id: '1', title: 'GraphQL 시작하기', authorId: '1' }
];

// 리졸버 
const resolvers = {
  Query: {
    user: (_, { id }) => users.find(user => user.id === id),
    posts: () => posts
  },
  User: {
    posts: (parent) => posts.filter(post => post.authorId === parent.id)
  },
  Post: {
    author: (parent) => users.find(user => user.id === parent.authorId)
  }
};

// 서버 실행
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
  console.log(`서버 실행 중: ${url}`);
});

생각보다 구현은 간단했음. 스키마 정의하고 리졸버 구현하는 방식이 직관적.

 


GraphQL이 유용한 상황

  1. 모바일 앱:
    • 대역폭 제한 환경에서 필요한 데이터만 요청 가능
    • 네트워크 요청 횟수 감소
  2. 복잡한 UI/대시보드:
    • 한 번의 요청으로 여러 데이터 조합 가능
    • 프론트엔드 변경 시 백엔드 수정 불필요
  3. 마이크로서비스:
    • API 게이트웨이 역할
    • 여러 서비스의 데이터 통합 지점
  4. 실제 사용 기업: GitHub, Shopify, Airbnb, Twitter 등

 

장점

  • 필요한 데이터만 요청 가능 (오버페칭 방지)
  • 여러 API 호출을 하나로 통합
  • 강력한 타입 시스템
  • 프론트엔드 개발 독립성 향상

단점

  • 학습 곡선이 있음
  • 캐싱이 REST보다 복잡
  • 파일 업로드 별도 구현 필요
  • N+1 쿼리 문제 해결 필요

도입 시 고려사항

N+1 쿼리 문제

많은 리졸버가 실행되면서 데이터베이스 쿼리가 중복 발생:

{
  posts {  # 1번 쿼리
    title
    author { # 각 post마다 쿼리 발생 (N번)
      name
    }
  }
}

DataLoader로 해결 가능:

const userLoader = new DataLoader(async (userIds) => {
  const users = await database.findUsersByIds(userIds);
  return userIds.map(id => users.find(user => user.id === id));
});

보안 고려사항

  • 쿼리 복잡성/깊이 제한 설정
  • 실행 시간 제한
  • 접근 제어 구현

GraphQL 생태계 도구

  • 서버: Apollo Server, Express GraphQL, GraphQL Yoga
  • 클라이언트: Apollo Client, Relay, urql
  • 개발 도구: GraphiQL, GraphQL Playground, Apollo Studio

 

REST API와 완전히 다른 패러다임이지만 적절한 상황에서는 매우 유용한 도구라고 느낌.

특히 다양한 클라이언트에서 각기 다른 데이터 요구사항이 있을 때 유연성이 좋음.

모든 프로젝트에 적합한 것은 아니며, 간단한 CRUD 작업에는 REST가 여전히 좋은 선택일 수 있음.

두 기술을 상황에 맞게 혼합해서 사용하는 게 가장 효율적일 듯.

다음에는 Apollo Client나 Relay와 React에서 GraphQL 사용법에 대해 더 공부해봐야겠음.

참고 자료

  • GraphQL 공식 웹사이트
  • Apollo GraphQL 문서
  • How to GraphQL 튜토리얼
728x90
반응형