notes
sql-GraphQL
SQL과 GraphQL의 데이터 조회 방식·스키마·API 설계 차이 비교.
1. GraphQL이란?
GraphQL은 API를 위한 쿼리 언어이자, 런타임으로 쿼리를 처리하기 위한 서버사이드 런타임입니다. Facebook에서 개발했으며, 2015년에 오픈소스로 공개되었습니다.
1.1 주요 특징
- 선언적 데이터 페칭: 필요한 데이터만 정확히 요청
- 단일 엔드포인트: 모든 요청이 하나의 엔드포인트로 처리
- 강력한 타입 시스템: 스키마 기반의 타입 안전성
- 실시간 데이터: Subscription을 통한 실시간 업데이트
- 자동 문서화: 스키마 기반의 자동 문서 생성
1.2 REST API와의 비교
2. GraphQL 기본 개념
2.1 스키마 (Schema)
GraphQL 스키마는 API의 타입 시스템을 정의합니다.
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
2.2 타입 시스템
- 스칼라 타입: Int, Float, String, Boolean, ID
- 객체 타입: 사용자 정의 타입
- 리스트 타입: [Type]
- NonNull 타입: Type!
- 인터페이스: 공통 필드 정의
- 유니온 타입: 여러 타입 중 하나
- 열거형: enum
2.3 쿼리 (Query)
데이터를 조회하는 작업입니다.
query GetUser {
user(id: "1") {
name
email
posts {
title
content
}
}
}
2.4 뮤테이션 (Mutation)
데이터를 변경하는 작업입니다.
mutation CreatePost {
createPost(input: {
title: "새 포스트"
content: "내용"
}) {
id
title
content
}
}
2.5 서브스크립션 (Subscription)
실시간 데이터 업데이트를 위한 기능입니다.
subscription OnPostCreated {
postCreated {
id
title
content
}
}
3. GraphQL 스키마 디자인
3.1 타입 정의
# 스칼라 타입 정의
scalar DateTime
scalar Upload
# 인터페이스 정의
interface Node {
id: ID!
}
# 유니온 타입 정의
union SearchResult = User | Post | Comment
# 열거형 정의
enum UserRole {
ADMIN
USER
GUEST
}
# 객체 타입 정의
type User implements Node {
id: ID!
name: String!
email: String!
role: UserRole!
createdAt: DateTime!
updatedAt: DateTime!
}
3.2 입력 타입
input CreateUserInput {
name: String!
email: String!
password: String!
role: UserRole = USER
}
3.3 인자와 변수
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
4. GraphQL 클라이언트
4.1 Apollo Client
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: '<http://localhost:4000/graphql>',
cache: new InMemoryCache()
});
4.2 쿼리 실행
import { gql, useQuery } from '@apollo/client';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
`;
function UserProfile({ userId }) {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id: userId }
});
if (loading) return <Loading />;
if (error) return <Error error={error} />;
return <div>{data.user.name}</div>;
}
4.3 뮤테이션 실행
import { gql, useMutation } from '@apollo/client';
const CREATE_POST = gql`
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
content
}
}
`;
function CreatePostForm() {
const [createPost, { loading, error }] = useMutation(CREATE_POST);
const handleSubmit = async (input) => {
try {
const { data } = await createPost({
variables: { input }
});
// 성공 처리
} catch (error) {
// 에러 처리
}
};
return <form onSubmit={handleSubmit}>...</form>;
}
5. GraphQL 서버
5.1 서버 구현
import { ApolloServer } from 'apollo-server';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
// 컨텍스트 설정
auth: req.headers.authorization
})
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
5.2 리졸버 구현
const resolvers = {
Query: {
user: (parent, args, context) => {
return context.db.users.find(user => user.id === args.id);
}
},
Mutation: {
createUser: (parent, args, context) => {
const user = context.db.users.create(args.input);
return user;
}
},
Subscription: {
userCreated: {
subscribe: (parent, args, context) => {
return context.pubsub.asyncIterator(['USER_CREATED']);
}
}
}
};
6. GraphQL 모범 사례
6.1 스키마 설계
- 명확하고 일관된 네이밍 컨벤션 사용
- 복잡한 타입은 인터페이스로 추상화
- Nullable 필드 신중하게 사용
- 문서화 주석 활용
6.2 성능 최적화
- 데이터 로더 사용
- 페이지네이션 구현
- 캐싱 전략 수립
- 쿼리 복잡도 제한
6.3 보안
- 인증/인가 구현
- 쿼리 깊이 제한
- 비용 제한
- 입력값 검증
6.4 에러 처리
const resolvers = {
Query: {
user: async (parent, args, context) => {
try {
const user = await context.db.users.find(args.id);
if (!user) {
throw new UserNotFoundError();
}
return user;
} catch (error) {
throw new ApolloError(
'사용자를 찾을 수 없습니다.',
'USER_NOT_FOUND',
{ userId: args.id }
);
}
}
}
};
7. GraphQL 도구
7.1 개발 도구
- GraphiQL: 대화형 GraphQL IDE
- Apollo Studio: API 관리 및 모니터링
- GraphQL Code Generator: 타입 생성
- GraphQL Playground: API 테스트
7.2 테스트 도구
import { createTestClient } from 'apollo-server-testing';
const { query, mutate } = createTestClient(server);
test('사용자 조회', async () => {
const res = await query({
query: gql`
query GetUser($id: ID!) {
user(id: $id) {
name
}
}
`,
variables: { id: '1' }
});
expect(res.data.user.name).toBe('John Doe');
});
8. GraphQL 배포 및 운영
8.1 모니터링
- 쿼리 성능 추적
- 에러 모니터링
- 사용량 분석
- 스키마 변경 추적
8.2 버전 관리
- 스키마 진화
- 하위 호환성 유지
- 점진적 롤아웃
- 클라이언트 마이그레이션
9. 결론
GraphQL은 현대적인 API 개발에 많은 이점을 제공합니다:
- 개발자 경험
- 타입 안전성
- 자동 문서화
- 강력한 도구 지원
- 성능
- 필요한 데이터만 요청
- 효율적인 캐싱
- 실시간 업데이트
- 유지보수성
- 단일 진실 공급원
- 명확한 스키마
- 쉬운 버전 관리
- 확장성
- 마이크로서비스 통합
- 다양한 데이터 소스
- 커스텀 확장
GraphQL을 도입할 때는 팀의 상황과 프로젝트 요구사항을 고려하여 점진적으로 적용하는 것이 좋습니다.