smob_logo
smob_logo
header-image
GRAPHQLGraphQL Code Generator 도입GraphQL Code Generator를 활용한 타입 관리 및 유효성 검사 자동화2023.12.17

문제

1. GraphQL Query에서 정확하지 않은 반환 타입

현재 저희 회사 모노레포 프로젝트에서 Shared 패키지에 정의된 공통 Schema 타입을 사용하여 데이터 페칭의 반환 타입을 지정하고 있습니다.

// shared
interface Ticket {
	name: string;
	price: number;
	...
	기타 많은 필드들
}

// 클라이언트 쪽 예시
const { tickets } = useTickets<Ticket[]>();

하지만 GraphQL 특성상 페이지나 컴포넌트마다 다른 데이터 필드를 가져오는 경우가 많아, 고정된 타입으로 인해 정의되지 않은 필드 오류가 자주 발생합니다. 예를 들어:

const 판매_페이지_에서_쓰는_TICKET_QUERY = gql`
	query findManyTickets(...) {
		가격
		이름
	}
`;

const 조회_페이지_에서_쓰는_TICKET_QUERY = gql`
	query findManyTickets(...) {
		입장중인지_확인하는_필드
	}
`;

// 둘다 똑같은 타입으로 정의되어 있음.
const { tickets } = useQuery<Ticket[]>(판매_페이지_에서_쓰는_TICKET_QUERY);
const { tickets } = useQuery<Ticket[]>(조회_페이지_에서_쓰는_TICKET_QUERY);

서로 다른 필드를 요청하는 쿼리에서 동일한 타입을 사용하므로, 런타임 시점에서 정의되지 않은 필드 오류를 발견하는 경우가 빈번히 있습니다. 타입을 직접 만들어 컴파일 시점에 타입 검사를 진행하려 했으나, 모든 GraphQL 쿼리마다 일일이 타입을 지정하는 것은 매우 시간이 많이 소요되고, 수정 시 누락되는 경우가 많았습니다. 그로 인해 작업 속도가 느려지고, 오류 발생이 크게 줄어들지 않았습니다.

타입에러 null 값 접근 오류

2. 기존 모노레포 프로젝트와 분리된 프로젝트 간의 타입 동기화 문제

회사의 API, Admin, Web은 하나의 모노레포 프로젝트로 통합되어 있어, Shared 패키지에 정의된 타입을 모두 쉽게 공유하고 사용할 수 있습니다. 하지만 Kiosk, POS App과 같은 별도 프로젝트는 동일한 API를 사용해도, Shared 패키지에 접근할 수 없기 때문에 해당 프로젝트에서 동일한 타입을 재정의해야 하는 문제가 발생합니다.

3. GraphQL Query 유효성 검사의 어려움

백엔드에서 GraphQL 스키마, 리졸버, 인풋 및 아웃풋 스키마 등이 수정되었을 때, 클라이언트 쪽에서 이에 맞는 수정이 필요하지만, 이를 놓치는 경우가 많았습니다. 특히 API 명세 변경 후 클라이언트 코드의 유효성 검사를 자동화하기 어려워, 버그나 비정상적인 동작이 종종 발생했습니다.

// 원래 정의되어 있던 GraphQL Schmea
type Ticket { id: ID! content: String }

// Client 쪽에서 쓰고 잘 쓰고 있던 Graphql Query
const TICKET_QUERY = gql`
	query findTicket() {
		ticket {
			content
		}
	}
`;

// 전날 누군가 아무도 사용하지 않은 줄 알고 Schema 바꿔버림
type Ticket { id: ID! }

// 다음날 프로덕션에서 발견되는 안타까운 에러 메세지
"Cannot query field "content" on type "Ticket"."

Graphql Codegen 라이브러리 사용하기

GraphQL-Codegen

GraphQL Codegen 라이브러리를 사용하면 API에서 정의된 스키마 타입을 자동으로 가져오고, 클라이언트에서 사용할 GraphQL 쿼리에 맞는 타입을 쉽게 생성할 수 있습니다. 이를 통해 작업 속도는 빠르게, 타입 오류는 줄어들게 됩니다.

API에 정의된 Schema 타입 가져오기

먼저, 프로젝트 내에 GraphQL API 경로를 설정하여 해당 API에서 정의된 모든 타입(스키마, 입력, 출력, 열거형 등)을 자동으로 가져올 수 있습니다.

import type { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
   schema: 'https://localhost:4000/graphql',
   ..기타 등등의 설정등
}
export default config

명령어를 실행하여 해당 스키마를 기반으로 타입을 생성합니다.

npx graphql-codegen --config codegen.ts
schema

클라이언트에 정의된 쿼리로 정확한 타입 생성

GraphQL Codegen을 사용하면 클라이언트에서 사용하는 각 GraphQL Query에 맞는 타입을 자동으로 생성할 수 있습니다.

  1. 도메인 로직에 맞는 GraphQL Query 작성:
const 판매페이지에_필요한_TICKET_QUERY = gql`
query SalesTickets(...) {
	tickets {
		가격
		이름
		...기타 등등
	}
}
`;
  1. GraphQL Codegen 명령어 실행: 명령어 실행으로 해당 쿼리에 맞는 타입이 자동으로 생성됩니다.

codegen result Codegen result description

  1. 자동으로 생성된 타입을 클라이언트에서 사용:
import { SalesTicketsQuery, SalesTicketsQueryVariables } from '@/__generated__/graphql';

const { tickets } = useQuery<SalesTicketsQuery, SalesTicketsQueryVariables>(...)

Graphql 시스템에서 자동으로 유효성 검사를 실시 가능

만약, 작성된 쿼리가 API 스키마와 맞지 않다면 GraphQL Codegen이 자동으로 유효성 검사를 수행하여 타입 생성 시 오류를 발생시킵니다. 예를 들어, 잘못된 필드를 포함한 쿼리를 작성했을 때 GraphQL Codegen은 타입을 생성하지 않으며, 오류 메세지를 통해 어디에서 문제가 발생했는지 명확하게 알려줍니다. 이와 같은 자동화된 유효성 검사는 작업하면서 잘못된 Query 작성 시 실시간으로 오류를 감지할 수 있도록 도와줍니다.

const 판매페이지에_필요한_TICKET_QUERY = gql`
query SalesTickets(...) {
	tickets {
		잘못된 필드
		...
	}
}
`;
graphql-validate-error
#graphql
#code-generator
avatar
By. 정규재안녕하세요. 잘 부탁 드립니다.