GraphQL
그래프큐엘 (Graph Query Language)
GraphQL은 Facebook이 개발한 API 쿼리 언어로, 클라이언트가 필요한 데이터만 정확히 요청할 수 있습니다. REST의 Over-fetching과 Under-fetching 문제를 해결하며, 강력한 타입 시스템과 인트로스펙션을 제공합니다.
그래프큐엘 (Graph Query Language)
GraphQL은 Facebook이 개발한 API 쿼리 언어로, 클라이언트가 필요한 데이터만 정확히 요청할 수 있습니다. REST의 Over-fetching과 Under-fetching 문제를 해결하며, 강력한 타입 시스템과 인트로스펙션을 제공합니다.
GraphQL은 2012년 Facebook 내부에서 개발되어 2015년 오픈소스로 공개된 API 쿼리 언어이자 런타임입니다. 모바일 앱의 느린 네트워크 환경에서 최적화된 데이터 페칭을 위해 탄생했으며, REST API의 고질적인 문제들을 해결합니다. 현재 GitHub, Shopify, Twitter 등 대형 서비스에서 채택하고 있습니다.
핵심 개념은 스키마(Schema)입니다. SDL(Schema Definition Language)로 타입과 필드를 정의하면, 클라이언트는 그 스키마 안에서 원하는 필드만 선택해서 요청합니다. Over-fetching(불필요한 데이터 받기)과 Under-fetching(여러 번 요청해야 함)이 사라지고, 네트워크 효율이 크게 개선됩니다.
세 가지 연산 타입이 있습니다: Query(읽기), Mutation(쓰기/수정/삭제), Subscription(실시간 구독). Query는 데이터 조회, Mutation은 데이터 변경, Subscription은 WebSocket을 통한 실시간 업데이트를 담당합니다. 단일 엔드포인트(/graphql)에서 모든 연산을 처리합니다.
인트로스펙션(Introspection)은 GraphQL의 강력한 개발자 경험 기능입니다. 스키마 자체를 쿼리할 수 있어, GraphiQL이나 Apollo Sandbox 같은 도구가 자동완성, 문서화, 쿼리 테스트를 제공합니다. 프론트엔드와 백엔드 간 계약(Contract)이 명확해져 협업이 수월해집니다.
# GraphQL 스키마 정의 (SDL)
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
published: Boolean!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
publishPost(id: ID!): Post!
}
# 클라이언트 쿼리 예시 - 필요한 필드만 요청
query GetUserWithPosts($userId: ID!) {
user(id: $userId) {
id
name
posts {
id
title
published
}
}
}
# Mutation 예시
mutation CreateNewPost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
author {
name
}
}
}
// JavaScript 클라이언트 (Apollo Client)
import { gql, useQuery, useMutation } from '@apollo/client';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
id
title
}
}
}
`;
function UserProfile({ userId }) {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id: userId },
});
if (loading) return <p>로딩 중...</p>;
if (error) return <p>에러: {error.message}</p>;
return (
<div>
<h1>{data.user.name}</h1>
<ul>
{data.user.posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}