본문으로 바로가기

GraphQL Codegen: GraphQL 스키마에서 타입 안전 코드를 생성하는 방법

GraphQL Code Generator의 설정과 구성, 생성되는 TypeScript 타입과 타입 지정 도큐먼트 노드, Apollo/urql/graphql-request 통합, 감시 모드와 CI를 통한 자동화를 다루는 실습형 가이드입니다.
업데이트됨 2026년 10월 2일  · 12분 읽기

AI와 탐험해 보세요

ChatGPTClaudePerplexity

GraphQL API를 위해 직접 작성한 TypeScript 타입은 누군가 스키마를 변경하는 순간부터 구식이 됩니다.

예를 들어, 백엔드 개발자가 Course 타입의 title 필드 이름을 name으로 바꿨다고 가정해 보세요. 프런트엔드는 여전히 컴파일됩니다. 수동으로 작성한 Course 인터페이스는 변경 사실을 전혀 모르기 때문이죠. 그래서 버그는 런타임에서야 드러납니다. 실제 애플리케이션에는 수십 개의 쿼리, 뮤테이션, 프래그먼트가 존재하는 경우가 많아 문제가 금방 커질 수 있습니다.

GraphQL Code Generator(줄여서 GraphQL Codegen)는 스키마와 GraphQL 오퍼레이션을 읽어 타입, 쿼리 헬퍼, 클라이언트 통합 코드, 그리고 원래라면 직접 작성해야 할 보일러플레이트로 변환합니다. TypeScript와 Apollo, urql 같은 인기 클라이언트와 함께 가장 많이 쓰입니다. 스키마가 바뀔 때마다 한 번의 명령으로 타입을 다시 동기화할 수 있습니다.

이 글에서는 작은 TypeScript 프로젝트에 GraphQL Codegen을 설정하고 구성하는 방법, 생성된 타입을 사용하는 방법, 그리고 워크플로우에 자동으로 통합하는 방법을 단계별로 살펴보겠습니다.

GraphQL은 REST와 어떻게 다를까요? 차이점과 장점을 알아보려면 Graph QL vs REST: A Complete Guide를 읽어보세요.

GraphQL Codegen이란?

GraphQL Codegen은 GraphQL 정의를 애플리케이션 코드로 바꿔 주는 커맨드라인 도구입니다.

The Guild가 만든 오픈소스 프로젝트이며, 프로젝트에 개발 의존성으로 설치합니다. 이 도구는 세 가지를 연결합니다:

  • GraphQL 스키마: 서버와 클라이언트 간의 계약으로, API가 노출하는 모든 타입, 필드, 인자를 포함합니다
  • GraphQL 오퍼레이션: 애플리케이션이 API에 보내는 쿼리, 뮤테이션, 서브스크립션, 프래그먼트입니다
  • 생성된 코드: Codegen이 스키마와 오퍼레이션을 바탕으로 작성하는 타입과 헬퍼 코드입니다

스키마는 Codegen에 가능한 것을 알려주고, 오퍼레이션은 애플리케이션이 무엇을 요청하는지 알려줍니다.

Codegen은 둘을 결합해 각 생성 타입이 한 오퍼레이션의 응답 모양과 정확히 일치하도록 만듭니다. 또한 모든 오퍼레이션을 스키마와 대조합니다. 서두에서 이름이 변경된 title 필드는 Cannot query field "title" on type "Course"와 같은 오류로 생성을 중단하게 만들며, 코드를 실행하기 전에 문제를 발견할 수 있습니다.

출력은 플러그인과 설정에 따라 달라집니다. Codegen 자체는 TypeScript를 작성하지 않습니다. 플러그인이 합니다. 예를 들어 typescript 플러그인은 스키마 타입을 생성하고, typescript-operations 플러그인은 쿼리와 뮤테이션 타입을 생성합니다. Java, C# 같은 다른 언어용 플러그인도 있지만, 이 글에서는 TypeScript에 집중합니다.

한 가지 기억할 점은 Codegen이 GraphQL 서버를 대체하지 않는다는 것입니다.

Codegen은 쿼리를 해석하거나 데이터를 가져오지 않습니다. 개발 중에 GraphQL 정의를 읽고 파일을 생성할 뿐입니다. 나머지는 서버와 GraphQL 클라이언트가 담당합니다.

GraphQL Codegen의 동작 방식

모든 Codegen 설정은 같은 흐름을 따릅니다: 스키마 + GraphQL 오퍼레이션 -> Codegen 설정 -> 생성 코드.

GraphQL Codegen 워크플로 다이어그램

GraphQL Codegen 워크플로 다이어그램

전체적인 개념은 다음과 같습니다.

  1. 스키마 제공: Codegen이 읽을 수 있도록 로컬 .graphql 파일 또는 실행 중인 GraphQL 엔드포인트(인트로스펙션으로 읽음)를 지정합니다

  2. 오퍼레이션 지점 지정: src/**/*.ts 같은 글롭 패턴을 사용해, 코드에 포함된 쿼리, 뮤테이션, 프래그먼트를 Codegen이 찾도록 합니다

  3. 플러그인과 출력 대상 구성: 어떤 파일을 어디에 작성할지, 각 파일을 생성할 플러그인을 지정합니다

  4. Codegen 실행: npx graphql-codegen을 실행하여 스키마를 로드하고 오퍼레이션을 검증한 뒤 출력을 작성합니다

  5. 생성 아티팩트 사용: 생성된 타입과 도큐먼트 노드를 애플리케이션 코드로 임포트합니다

1~3단계는 보통 codegen.ts라는 하나의 설정 파일에 담습니다. 아래는 강의 카탈로그 프로젝트를 위한 최소 설정입니다:

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

const config: CodegenConfig = {
  // Step 1: Where the schema comes from
  schema: "schema.graphql",
  // Step 2: Where your operations are placed
  documents: ["src/**/*.ts"],
  // Step 3: What to generate and where to write it
  generates: {
    "./src/gql/": { preset: "client" },
  },
};

export default config;

client 프리셋은 스키마 타입, 오퍼레이션 타입, 타입이 지정된 도큐먼트 노드를 한 번에 생성하는 플러그인 묶음입니다. 설정 섹션에서 각각을 자세히 다루겠습니다.

GraphQL Codegen 설정 방법

Codegen을 제대로 이해하는 가장 좋은 방법은 실제 프로젝트에 적용해 보는 것입니다.

이 섹션에서는 스키마(강의와 강사), 하나의 쿼리, 그리고 해당 쿼리를 TypeScript 타입으로 변환하는 Codegen 설정을 갖춘 작은 강의 카탈로그 프로젝트를 처음부터 만들어 보겠습니다. 필요한 것은 Node.js와 npm뿐입니다.

1단계: GraphQL Code Generator 설치

새 폴더를 만들고 npm 프로젝트를 초기화하세요:

mkdir course-catalog && cd course-catalog
npm init -y

Codegen에는 몇 가지 패키지가 필요합니다. 두 개는 일반 의존성에, 나머지는 개발 중에만 실행되므로 개발 의존성으로 설치합니다:

npm install graphql @graphql-typed-document-node/core
npm install -D typescript @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node

각 패키지는 다음 역할을 합니다:

  • graphql: Codegen이 스키마와 오퍼레이션을 파싱하는 데 사용하는 JavaScript용 GraphQL 레퍼런스 구현

  • @graphql-typed-document-node/core: 생성 코드가 임포트하는 TypedDocumentNode 타입 제공

  • @graphql-codegen/cli: Codegen 커맨드라인 도구

  • @graphql-codegen/typescript-operations: 쿼리, 뮤테이션, 프래그먼트에 대한 타입을 생성하는 플러그인

  • @graphql-codegen/typed-document-node: 타입이 연결된 도큐먼트 객체를 생성하는 플러그인

2단계: 설정 파일 생성

Codegen은 프로젝트 루트의 설정 파일에서 설정을 읽습니다. 우선 빈 설정으로 codegen.ts를 만드세요:

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

const config: CodegenConfig = {};

export default config;

또는 npx graphql-codegen init을 실행해 몇 가지 질문에 답하고 자동 생성된 설정 파일을 받을 수도 있습니다. 여기서는 각 설정의 의미를 보기 위해 수동으로 작성합니다.

3단계: 스키마와 도큐먼트 위치 지정

Codegen에는 두 가지 입력이 필요하므로 둘 다 만들어 보겠습니다.

먼저 프로젝트 루트에 schema.graphql로 스키마를 저장합니다:

enum Level {
  BEGINNER
  INTERMEDIATE
  ADVANCED
}

type Instructor {
  id: ID!
  name: String!
  bio: String
}

type Course {
  id: ID!
  title: String!
  level: Level!
  durationHours: Int!
  instructor: Instructor!
}

type Enrollment {
  id: ID!
  course: Course!
  studentEmail: String!
}

type Query {
  courses(level: Level, limit: Int): [Course!]!
  course(id: ID!): Course
}

type Mutation {
  enrollStudent(courseId: ID!, studentEmail: String!): Enrollment!
}

그다음 강의와 강사를 조회하는 쿼리를 src/operations/courses.graphql에 생성합니다:

query GetCourses($level: Level, $limit: Int) {
  courses(level: $level, limit: $limit) {
    id
    title
    level
    instructor {
      name
    }
  }
}

이제 두 파일을 Codegen에 지정합니다. schema 키에는 스키마 경로를, documents에는 오퍼레이션과 일치하는 글롭 패턴을 넣습니다:

const config: CodegenConfig = {
  schema: "schema.graphql",
  documents: ["src/**/*.graphql"],
};

4단계: 출력 파일 선택

generates 키는 Codegen이 출력을 어디에 쓸지 알려줍니다. 그 안의 각 키는 파일 경로입니다:

const config: CodegenConfig = {
  schema: "schema.graphql",
  documents: ["src/**/*.graphql"],
  generates: {
    "src/generated/graphql.ts": {},
  },
};

src/generated/ 폴더가 없으면 Codegen이 생성합니다.

5단계: 관련 플러그인 추가

플러그인이 없는 출력 파일은 비어 있습니다. 설치한 두 플러그인을 추가하세요:

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

const config: CodegenConfig = {
  schema: "schema.graphql",
  documents: ["src/**/*.graphql"],
  generates: {
    "src/generated/graphql.ts": {
      plugins: ["typescript-operations", "typed-document-node"],
    },
  },
};

export default config;

예전 튜토리얼을 따라 하셨다면 이 목록에 typescript 플러그인도 보셨을 겁니다. 이제는 제거해도 됩니다. typescript-operations만으로 동작하며 실제 사용되는 Input, Enum, Operation 타입만 생성합니다. 최신 버전에서 둘을 같은 출력 파일에 넣으면 TypeScript가 중복 선언 오류를 발생시킵니다.

6단계: Codegen 실행

명령을 기억하지 않도록 npm 스크립트를 추가합니다:

npm pkg set scripts.codegen="graphql-codegen"

그다음 실행하세요:

npm run codegen

Codegen은 스키마를 로드하고, 쿼리를 스키마와 대조해 검증한 뒤 출력을 작성합니다:

Codegen 생성 결과

Codegen 생성 결과

오퍼레이션이 스키마에 없는 필드를 참조하면 이 단계에서 실행이 실패하고 파일은 작성되지 않습니다.

7단계: 생성된 출력 확인

src/generated/graphql.ts를 여세요. 다음과 같은 내용을 볼 수 있습니다:

생성된 출력

Codegen은 네 가지를 생성했습니다:

  • Level: 열거형에 대한 문자열 유니언으로, 이 쿼리에서 사용되었기 때문에 생성됨

  • GetCoursesQueryVariables: 쿼리에 전달하는 변수 타입으로, 스키마에서 필수로 표시하지 않았기 때문에 둘 다 선택적

  • GetCoursesQuery: 쿼리가 요청한 필드만 포함한 응답의 모양

  • GetCoursesDocument: 결과와 변수 타입이 연결된 타입 지정 도큐먼트 노드

GetCoursesQuery를 다시 보세요. 쿼리가 요청하지 않은 durationHours나 강사의 bio는 포함되지 않습니다. 코드에서 course.durationHours를 읽으려고 하면 런타임의 undefined가 아니라 컴파일 오류가 발생합니다.

이것이 전체 설정입니다. GraphQL을 작성하면 Codegen이 TypeScript를 작성합니다.

GraphQL Codegen 구성 방법

대부분의 Codegen 설정은 위 예시보다 길지만, 동일한 네 가지 설정으로 구성됩니다.

codegen.ts, codegen.yml, codegen.json 중 하나를 설정 파일로 사용할 수 있습니다. 이 글에서는 설정 자체에 자동 완성과 타입 체크가 가능한 TypeScript를 사용하지만, 설정 값은 형식과 관계없이 동일합니다.

Schema

schema 키는 스키마의 위치를 지정합니다. 여러 소스 유형을 허용합니다.

스키마가 레포지토리에 있는 경우 로컬 파일이나 글롭 패턴을 사용할 수 있습니다:

schema: "schema.graphql",
// or, for a schema split across files
schema: "schema/**/*.graphql",

스키마가 실행 중인 서버에 있는 경우 URL을 사용할 수 있습니다. Codegen은 엔드포인트에 인트로스펙션 쿼리를 보내며, 인증을 위한 헤더를 전달할 수 있습니다:

schema: [
  {
    "https://api.example.com/graphql": {
      headers: { Authorization: Bearer ${process.env.API_TOKEN} },
    },
  },
],

URL 옵션은 서버에서 인트로스펙션을 활성화한 경우에만 동작합니다. 많은 프로덕션 API는 이를 비활성화하므로, CI에서는 레포지토리에 커밋된 스키마 파일을 사용하는 편이 대체로 더 안전합니다.

Documents

documents 키는 쿼리, 뮤테이션, 서브스크립션, 프래그먼트가 어디에 있는지 지정합니다.

오퍼레이션은 .graphql 파일에 두거나 TypeScript 코드에 인라인으로 넣을 수 있습니다. 인라인 오퍼레이션은 gql 태그로 감싸거나 /* GraphQL */ 주석을 달면 Codegen이 찾습니다:

const EnrollStudent = /* GraphQL */ 
  mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
    enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
      id
      course {
        title
      }
    }
  }
;

두 파일 형식을 모두 스캔하려면 글롭을 넓히세요. 또한 Codegen이 자체 출력을 스캔하지 않도록 부정 패턴을 추가합니다:

documents: ["src/**/*.{graphql,ts}", "!src/generated/**"],

모든 오퍼레이션에 고유한 이름을 부여하세요. Codegen은 오퍼레이션 이름을 기반으로 타입 이름을 만듭니다. 예를 들어 EnrollStudent는 EnrollStudentMutation과 EnrollStudentMutationVariables로 변환됩니다.

Generates

generates 키는 출력 경로와 그 안에 들어갈 내용을 매핑합니다. 필요에 따라 여러 출력 대상을 둘 수 있으며, 각 대상에는 고유한 플러그인을 설정할 수 있습니다.

예를 들어, 오퍼레이션 타입을 한 파일에, 전체 스키마 타입을 다른 파일에 두고 싶다면 다음과 같습니다:

generates: {
  "src/generated/graphql.ts": {
    plugins: ["typescript-operations", "typed-document-node"],
    config: {
      useTypeImports: true,
    },
  },
  "src/generated/schema-types.ts": {
    plugins: ["typescript"],
  },
},

출력 대상 내부의 config 객체는 해당 파일에만 적용됩니다. 여기서 useTypeImports는 일부 빌드 설정에 필요한 import type 사용을 강제합니다. config를 루트에 두면 모든 출력에 적용됩니다.

출력 경로는 "src/gql/"처럼 슬래시로 끝나는 폴더일 수도 있습니다. 여러 파일을 작성하는 프리셋은 이런 형식을 사용합니다.

Plugins

플러그인은 무엇을 생성할지 결정합니다. Codegen은 스키마와 도큐먼트를 로드하고, 각 플러그인이 그것들을 특정 출력물로 변환합니다.

TypeScript 프로젝트에서 자주 보게 될 플러그인은 다음과 같습니다:

  • typescript-operations: 쿼리, 뮤테이션, 서브스크립션, 프래그먼트에 대한 타입과 그들이 사용하는 enum 및 input 타입을 생성

  • typed-document-node: 결과와 변수 타입이 연결된 도큐먼트 객체 생성. 대부분의 GraphQL 클라이언트가 인식함

  • typescript: 오퍼레이션 사용 여부와 관계없이 스키마의 모든 객체, enum, input에 대한 타입 생성

  • typescript-resolvers: 서버 측 리졸버 함수에 대한 타입 시그니처 생성

프리셋은 합리적인 기본 설정을 갖춘 플러그인 묶음입니다. 동작 방식 섹션에서 언급한 client 프리셋은 클라이언트 앱에 대해 The Guild가 권장하는 프리셋입니다. 내부적으로 개선된 typescript-operations를 기반으로 하므로 추가 설정 없이도 그 이점을 누릴 수 있습니다.

각 플러그인에는 고유한 설정 옵션이 있으며, 그 수가 많습니다. 처음부터 모두 배울 필요는 없습니다. 기본값으로 시작하고, 생성 결과가 코드의 기대와 다를 때 필요한 옵션을 찾아보면 됩니다.

GraphQL Codegen이 생성할 수 있는 것들

Codegen의 출력은 선택한 플러그인에 따라 달라집니다.

동일한 스키마와 오퍼레이션으로 순수 TypeScript 타입을 만들 수도 있고, 준비된 클라이언트 코드를 생성할 수도 있습니다. 가장 자주 쓰는 출력은 다음과 같습니다.

TypeScript 타입

typescript 플러그인은 스키마의 모든 객체, enum, input에 대한 타입을 생성합니다. 강의 카탈로그의 Course 타입은 다음과 같습니다:

export type Course = {
  __typename?: 'Course';
  durationHours: Scalars['Int']['output'];
  id: Scalars['ID']['output'];
  instructor: Instructor;
  level: Level;
  title: Scalars['String']['output'];
};

이 타입들은 전체 스키마를 묘사하므로, 서버 측이나 테스트 픽스처, 완전한 객체를 다루는 유틸리티 함수에서 유용합니다.

하지만 클라이언트 코드에는 적합하지 않습니다. Course 타입은 쿼리가 요청하지 않은 필드까지 모두 존재한다고 가정합니다.

오퍼레이션 타입

typescript-operations 플러그인은 오퍼레이션마다 결과 타입과 변수 타입, 두 가지를 생성합니다. 쿼리와 서브스크립션에도 동일하게 동작합니다.

다음은 EnrollStudent 뮤테이션에 대해 생성되는 예시입니다:

export type EnrollStudentMutationVariables = Exact<{
  courseId: string | number;
  studentEmail: string;
}>;

export type EnrollStudentMutation = { enrollStudent: { id: string, course: { title: string } } };

결과 타입은 선택 집합(selection set)과 필드 단위로 일치합니다. 변수 타입은 Exact이므로, TypeScript는 불필요하거나 잘못된 변수 이름을 거부합니다.

courseId 변수는 string과 number를 모두 허용합니다. GraphQL의 ID 스칼라가 입력으로 둘 다 허용하기 때문입니다. v6부터 기본 타입은 string | number이며, 이는 클라이언트 사용 사례에 적합한 올바른 타입입니다.

타입 지정 도큐먼트 노드

일반 DocumentNode는 파싱된 GraphQL 쿼리입니다. 쿼리 구조는 알지만, 무엇을 반환하고 어떤 변수를 받는지는 모릅니다.

타입 지정 도큐먼트 노드는 두 가지 타입 정보를 모두 포함합니다. typed-document-node 플러그인은 오퍼레이션마다 하나씩 생성합니다:

export const GetCoursesDocument = { /* ... */ } as unknown as DocumentNode<GetCoursesQuery, GetCoursesQueryVariables>;

이 방식으로, TypedDocumentNode를 받는 모든 함수는 도큐먼트만으로 결과와 변수 타입을 추론할 수 있습니다. 제네릭을 수동으로 넘길 필요가 없고, 잘못된 타입과 쿼리를 조합할 수 없습니다.

대부분의 최신 GraphQL 클라이언트는 타입 지정 도큐먼트 노드를 지원합니다. Apollo Client, urql, graphql-request가 그 예이며, 이 글 후반에서 모두 살펴봅니다.

클라이언트별 통합

일부 플러그인은 더 나아가 특정 클라이언트나 프레임워크용 코드를 생성합니다. 예를 들면 다음과 같습니다:

  • typescript-react-apollo: Apollo Client용 타입 지정 React 훅 생성

  • typescript-urql: urql용 타입 지정 훅과 컴포넌트 생성

  • typescript-react-query: TanStack Query용 타입 지정 훅 생성

  • typescript-graphql-request: 오퍼레이션당 하나의 함수를 갖는 타입 지정 SDK 생성

이들은 모두 커뮤니티 플러그인입니다. The Guild는 이를 별도의 커뮤니티 저장소로 이전했기에, 릴리스 속도가 코어 플러그인과 다릅니다.

또한 예전만큼 자주 필요하지 않습니다. 클라이언트가 타입 지정 도큐먼트 노드를 수용하는 경우, 생성된 훅이 타입 안전성 측면에서 추가로 제공하는 이점이 크지 않습니다. 예를 들어 Apollo의 공식 문서는 런타임 코드를 포함하지 않고 타입만 생성하는 typescript-operations 플러그인을 권장합니다.

일반적으로는 오퍼레이션 타입과 타입 지정 도큐먼트 노드부터 시작하세요. 팀에서 생성된 훅이나 SDK 함수가 필요할 때만 클라이언트 전용 플러그인을 추가하세요.

TypeScript와 함께 사용하는 GraphQL Codegen

생성된 타입의 가치는 실수를 잡아낼 때 드러납니다. 그럼 실수를 만들어 보겠습니다.

이 예시는 강의 카탈로그 프로젝트에 두 번째 쿼리, API에 쿼리를 보내는 작은 헬퍼, 그리고 결과를 사용하는 코드를 추가합니다. http://localhost:4000/graphql에서 스키마를 구현하는 GraphQL 서버가 실행 중이라고 가정합니다.

예시에서 사용하는 스키마 일부는 다음과 같습니다:

type Instructor {
  id: ID!
  name: String!
  bio: String
}

type Course {
  id: ID!
  title: String!
  level: Level!
  durationHours: Int!
  instructor: Instructor!
}

type Query {
  course(id: ID!): Course
}

두 가지가 중요합니다. course 필드는 ! 없이 Course를 반환하므로, 해당 ID에 맞는 강의가 없으면 null이 됩니다. 강사의 bio도 null 가능입니다.

src/operations/course.graphql에 단일 강의를 조회하는 쿼리를 만듭니다:

query GetCourse($id: ID!) {
  course(id: $id) {
    title
    durationHours
    instructor {
      name
      bio
    }
  }
}

npm run codegen을 다시 실행하세요. Codegen은 src/generated/graphql.ts에 두 개의 새 타입을 추가합니다:

graphql.ts 파일의 내용

스키마의 null 가능성이 그대로 반영됩니다. course는 null일 수 있고, bio도 null일 수 있으며, id는 쿼리에서 ID!로 선언했기 때문에 필수입니다.

이제 애플리케이션 코드를 작성해 봅시다. API로 타입 지정 도큐먼트를 전송하는 헬퍼를 src/execute.ts에 만듭니다:

import { print } from "graphql";
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";

const API_URL = "http://localhost:4000/graphql";

export async function execute<TResult, TVariables>(
  document: TypedDocumentNode<TResult, TVariables>,
  variables: TVariables,
): Promise<TResult> {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ query: print(document), variables }),
  });
  const { data, errors } = await response.json();
  if (errors?.length) {
    throw new Error(errors[0].message);
  }
  return data as TResult;
}

이 헬퍼는 강의에 대해 아무것도 모릅니다. 전달받은 도큐먼트에서 두 타입을 모두 가져오며, print() 함수가 도큐먼트를 요청용 쿼리 문자열로 되돌립니다.

그다음 이를 src/course-details.ts에서 사용합니다:

import { execute } from "./execute";
import { GetCourseDocument, type GetCourseQuery } from "./generated/graphql";

function formatCourse(course: NonNullable<GetCourseQuery["course"]>): string {
  const bio = course.instructor.bio ?? "No bio yet";
  return course.title({course.durationHours}h) by ${course.instructor.name} - ${bio};
}

const { course } = await execute(GetCourseDocument, { id: "c1" });

if (course) {
  console.log(formatCourse(course));
} else {
  console.log("Course not found");
}

formatCourse()의 매개변수 타입은 생성된 쿼리 타입에서 가져옵니다. NonNullable은 함수가 if 체크 이후에만 실행되므로 null 가능성을 제거합니다.

예시에서는 최상위 await를 사용하므로, package.json에 "type": "module"을 설정하고 npx tsx src/course-details.ts로 실행하세요:

GraphQL 모크 서버 호출 성공

GraphQL 모크 서버 호출 성공

이제 GraphQL 서버가 실행 중이라고 가정했습니다. 제가 사용한 모크 서버는 다음과 같습니다:

import { createServer } from "node:http";
import { readFileSync } from "node:fs";
import { createSchema, createYoga } from "graphql-yoga";

const instructors = [{ id: "i1", name: "Dario Radecic", bio: "Developer" }];

const courses = [
  { id: "c1", title: "Intro to GraphQL", level: "BEGINNER", durationHours: 4, instructor: instructors[0] },
  { id: "c2", title: "GraphQL Schema Design", level: "ADVANCED", durationHours: 6, instructor: instructors[0] },
];

const schema = createSchema({
  typeDefs: readFileSync("schema.graphql", "utf8"),
  resolvers: {
    Query: {
      courses: (_: unknown, args: { level?: string; limit?: number }) =>
        courses
          .filter((course) => !args.level || course.level === args.level)
          .slice(0, args.limit ?? undefined),
      course: (_: unknown, args: { id: string }) =>
        courses.find((course) => course.id === args.id) ?? null,
    },
    Mutation: {
      enrollStudent: (_: unknown, args: { courseId: string; studentEmail: string }) => ({
        id: "e1",
        course: courses.find((course) => course.id === args.courseId),
        studentEmail: args.studentEmail,
      }),
    },
  },
});

createServer(createYoga({ schema })).listen(4000, () => {
  console.log("Mock API running at http://localhost:4000/graphql");
});

이제 일부러 깨뜨려 봅시다. src/course-details.ts를 열고 formatCourse() 아래의 모든 코드를 수동 타입에서 자주 지나치는 세 가지 실수로 교체하세요:

// Missing the required id variable
await execute(GetCourseDocument, {});

const { course } = await execute(GetCourseDocument, { id: "c1" });

// Reading a field the query doesn't request
console.log(course?.level);

// Forgetting that course can be null
console.log(course.title);

그다음 프로젝트를 타입 체크하세요:

npx tsc -p .

TypeScript는 코드가 실행되기 전에 세 가지 모두를 잡아냅니다:

TypeScript가 잡아낸 에러

TypeScript가 잡아낸 에러

이처럼 Codegen은 프런트엔드와 API 간의 불일치를 줄여 줍니다. 타입은 서버가 사용하는 것과 동일한 스키마에서 나오므로, 존재하지 않는 필드를 설명하거나 서버가 반환할 수 있는 null을 놓치지 않습니다.

그리고 보호는 첫 실행 이후에도 계속됩니다. 예를 들어 백엔드 팀이 durationHours를 null 가능으로 바꿨다고 합시다. 재생성하면 타입이 number | null로 바뀌고, 숫자라고 가정했던 모든 줄을 TypeScript가 가리켜 줍니다.

GraphQL 클라이언트와 함께 사용하는 Codegen

Codegen은 요청을 보내지 않습니다. 요청은 GraphQL 클라이언트의 역할입니다.

생성된 타입 지정 도큐먼트 노드가 둘 사이의 인계 지점입니다. 도큐먼트를 클라이언트에 전달하면, 클라이언트가 타입을 추론합니다. 아래 예시는 이전 섹션의 동일한 src/generated/graphql.ts 파일과 모크 API를 사용하므로, 별도의 터미널에서 npx tsx mock/server.ts를 계속 실행해 두세요.

Apollo와 함께 사용하는 GraphQL Codegen

Apollo Client와 그 피어 의존성인 rxjs를 설치합니다:

npm install @apollo/client rxjs

예시에서는 EnrollStudent 뮤테이션도 실행합니다. src/operations/enroll.graphql로 저장하세요:

mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
  enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
    id
    course {
      title
    }
  }
}

Apollo의 문서는 또 다른 Codegen 설정 옵션 두 가지를 권장합니다. Apollo Client는 항상 __typename 필드를 포함하지만 루트 타입에는 추가하지 않기 때문입니다. codegen.ts의 출력 대상에 다음을 추가하세요:

"src/generated/graphql.ts": {
  plugins: ["typescript-operations", "typed-document-node"],
  config: {
    nonOptionalTypename: true,
    skipTypeNameForRoot: true,
  },
},

이 옵션을 사용하면 생성된 결과 타입의 모든 객체에 __typename 필드가 필수로 추가됩니다(예: __typename: 'Course'). 이는 Apollo 캐시가 반환하는 형태와 일치합니다.

npm run codegen을 다시 실행해 새 뮤테이션과 설정을 반영하세요.

Apollo Client는 query()와 mutate()에서 타입 지정 도큐먼트 노드를 받습니다. 다음을 src/apollo.ts로 저장하세요:

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { EnrollStudentDocument, GetCoursesDocument } from "./generated/graphql";

const client = new ApolloClient({
  link: new HttpLink({ uri: "http://localhost:4000/graphql" }),
  cache: new InMemoryCache(),
});

const { data } = await client.query({
  query: GetCoursesDocument,
  variables: { level: "BEGINNER", limit: 5 },
});

data?.courses.forEach((course) => console.log(course.title));

const result = await client.mutate({
  mutation: EnrollStudentDocument,
  variables: { courseId: "c1", studentEmail: "student@example.com" },
});

console.log(result.data?.enrollStudent.course.title);

data 객체는 GetCoursesQuery | undefined로 타입이 지정되고, TypeScript는 variables를 GetCoursesQueryVariables와 대조해 검사합니다. 뮤테이션도 EnrollStudentMutation과 해당 변수 타입으로 동일하게 동작합니다.

npx tsx src/apollo.ts로 실행하세요:

Apollo 출력

Apollo 출력

첫 줄은 초급(BEGINNER) 강의가 하나뿐이기 때문에 쿼리 결과에서 나온 것입니다. 두 번째 줄은 뮤테이션 결과로, 학생이 등록한 강의의 제목을 반환합니다.

React 훅(useQuery(), useMutation() 등)도 타입 지정 도큐먼트 노드로 동일하게 타입을 추론합니다.

urql과 함께 사용하는 GraphQL Codegen

urql 코어 패키지를 설치합니다:

npm install @urql/core

urql은 기본적으로 타입 지정 도큐먼트 노드를 지원합니다. src/urql.ts에서 query()의 첫 번째 인자로 도큐먼트를 전달하세요:

import { Client, cacheExchange, fetchExchange } from "@urql/core";
import { GetCoursesDocument } from "./generated/graphql";

const client = new Client({
  url: "http://localhost:4000/graphql",
  exchanges: [cacheExchange, fetchExchange],
});

const result = await client
  .query(GetCoursesDocument, { level: "ADVANCED" })
  .toPromise();

if (result.error) {
  throw result.error;
}

result.data?.courses.forEach((course) => console.log(course.title));

두 번째 인자는 쿼리 변수 타입으로 지정되며, result.data는 쿼리 결과 타입으로 지정됩니다.

npx tsx src/urql.ts로 실행하세요:

Urql 출력

Urql 출력

urql 패키지의 프레임워크 바인딩(예: React의 useQuery())도 타입 지정 도큐먼트 노드를 지원합니다.

다른 클라이언트와 함께 사용

타입 지정 결과를 얻기 위해 풀 스택 클라이언트가 꼭 필요한 것은 아닙니다.

graphql-request는 단일 요청을 보내고 데이터를 반환하는 미니멀 클라이언트입니다. 다음 명령으로 설치하세요:

npm install graphql-request

이 클라이언트도 Apollo와 urql과 마찬가지로 타입 지정 도큐먼트 노드에서 타입을 추론합니다. 다음을 src/graphql-request.ts로 저장하세요:

import { request } from "graphql-request";
import { GetCourseDocument } from "./generated/graphql";

const { course } = await request(
  "http://localhost:4000/graphql",
  GetCourseDocument,
  { id: "c2" },
);

console.log(course?.title);

npx tsx src/graphql-request.ts로 실행하세요:

GraphQL request 출력

GraphQL request 출력

같은 아이디어는 TanStack Query에도 적용됩니다. 자체적으로 GraphQL 요청을 보내지 않으므로, 쿼리 함수 내부에서 graphql-request나 이전 섹션의 execute() 헬퍼를 호출하면 결과 타입이 코드 전반으로 전파됩니다.

클라이언트가 타입 지정 도큐먼트 노드를 전혀 지원하지 않더라도, 순수 오퍼레이션 타입은 여전히 사용할 수 있습니다. GetCourseQuery와 GetCourseQueryVariables를 임포트하여 제네릭으로 전달하세요. 수동 작업이 조금 늘어나지만, 타입은 여전히 스키마에서 비롯됩니다.

GraphQL Codegen을 자동으로 실행하는 방법

생성된 타입은 현재 스키마와 오퍼레이션과 일치할 때만 도움이 됩니다.

변경 후 Codegen 실행을 잊으면, 타입은 더 이상 존재하지 않는 코드를 설명하게 됩니다. 수동 타입과 동일한 드리프트 문제가, 다만 절차가 하나 더 늘어난 상태로 발생합니다. 해결책은 워크플로우에서 수동 단계를 제거하는 것입니다.

npm 스크립트

팀 모두가 동일한 방식으로 Codegen을 실행하도록 package.json에 스크립트를 몇 개 추가하는 것부터 시작하세요:

{
  "scripts": {
    "codegen": "graphql-codegen",
    "codegen:watch": "graphql-codegen --watch",
    "codegen:check": "graphql-codegen --check",
    "prebuild": "npm run codegen",
    "build": "tsc -p ."
  }
}

이후 섹션에서 각 스크립트의 용도를 설명합니다.

감시 모드

감시 모드는 감시 중인 파일이 변경될 때마다 Codegen을 재실행합니다. Codegen이 자동으로 설치하지 않는 @parcel/watcher 패키지가 필요합니다:

npm install -D @parcel/watcher
npm run codegen:watch

설치를 건너뛰면 Codegen은 한 번만 실행되고 감시 대신 Failed to import @parcel/watcher를 출력합니다.

감시 모드는 오퍼레이션과 로컬 스키마 파일 모두를 추적합니다. course.graphql에 필드를 추가하면, 1초 후 GetCourseQuery에 새 필드가 나타납니다. schema.graphql에서 durationHours를 null 가능으로 바꾸면 타입이 number | null로 바뀌고, 에디터가 숫자라고 가정한 모든 줄을 표시합니다.

개발 중에는 dev 서버와 나란히 별도의 터미널에서 계속 실행해 두세요.

빌드 전에 생성

npm은 build 전에 prebuild 스크립트를 실행합니다. 위 스크립트 설정에서는 npm run build가 먼저 타입을 재생성한 뒤 TypeScript 컴파일러를 실행합니다.

이렇게 하면 누군가 변경 사항을 pull한 뒤 Codegen 실행을 깜빡해도, 빌드는 오래된 타입에 의존하지 않습니다.

CI/CD

--check 플래그는 드라이런 모드에서 Codegen을 실행합니다. 파일을 쓰지 않고, 생성될 결과와 디스크의 파일을 비교해 차이가 있으면 코드 1로 종료합니다.

다음은 이를 사용하는 최소한의 GitHub Actions 워크플로입니다:

name: CI

on: [push, pull_request]

jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
      - run: npm ci
      - run: npm run codegen:check
      - run: npm run build

codegen:check 단계는 생성 파일을 레포지토리에 커밋하는 경우에만 의미가 있습니다. 커밋하지 않는다면, prebuild 훅을 통해 어차피 빌드 전에 재생성되므로 해당 단계를 제거하세요.

스키마 변경 시 재생성

오퍼레이션 변경은 레포지토리 내에서 일어나므로 감시 모드와 prebuild로 처리됩니다. 스키마 변경은 보통 다른 팀에서 오기 때문에 더 까다롭습니다.

스키마 위치에 따라 접근 방식이 달라집니다:

  • 같은 레포지토리의 스키마 파일: 감시 모드와 CI가 변경을 감지합니다. 가장 작업하기 쉬운 설정입니다
  • URL의 스키마: Codegen은 실행 때마다 최신 버전을 가져옵니다. 따라서 CI의 codegen:check는 서버 스키마가 커밋된 출력과 달라지는 즉시 실패합니다
  • 다른 레포지토리에서 게시한 스키마: 빌드 과정에서 복사하거나 다운로드한 뒤 로컬 사본에 대해 Codegen을 실행합니다

URL 옵션에는 한 가지 주의점이 있습니다. 감시 모드는 파일 변경에 반응하기 때문에, 원격 스키마 변경은 감지하지 못합니다. 이러한 변경은 다음 수동 실행 또는 CI 작업에서만 확인됩니다.

GraphQL Codegen vs 수동 타입 작성

두 접근 방식 모두 GraphQL 데이터에 대한 TypeScript 타입을 제공하지만, 알아두어야 할 차이점이 있습니다.

수동 타입 작성

수동 타입 작성에서는 스키마를 읽고 일치하는 인터페이스를 직접 작성합니다.

이 방식은 완전한 통제력을 제공합니다. 원하는 대로 타입에 이름을 붙이고, 여러 오퍼레이션을 하나의 공통 타입으로 합치거나, 스키마에 없는 헬퍼 타입을 추가할 수 있습니다.

하지만 수작업입니다. 새로운 필드나 변경된 인자가 생길 때마다 일치하는 타입을 수동으로 찾아 업데이트해야 합니다. 그리고 놓쳤을 때 경고해 주는 것도 없습니다.

이게 바로 드리프트의 원인입니다. 서두의 Course 인터페이스는 백엔드가 title을 바꿔도 여전히 컴파일되었습니다. TypeScript는 API가 아닌, 코드와 타입 간의 일치만 검사하기 때문입니다.

생성된 타입

생성된 타입은 서버가 사용하는 것과 동일한 스키마에서 나오므로, 설계상 GraphQL 정의와 동기화됩니다. 스키마 변경 후 Codegen을 실행하면 영향을 받는 모든 타입이 한 번에 업데이트됩니다.

또한 작성해야 할 코드가 크게 줄어듭니다. 강의 카탈로그 프로젝트는 세 개의 오퍼레이션을 가지고 있지만, 그 중 어떤 것도 수동 타입이 필요하지 않았습니다.

게다가 스키마 주도 개발을 거의 공짜로 얻습니다. 스키마나 오퍼레이션을 변경하고 재생성하면, 컴파일러가 변경이 필요한 모든 위치를 알려줍니다.

하지만 기억해야 할 점은 Codegen은 제공한 것만 반영한다는 것입니다.

스키마가 모든 필드를 null 가능으로 표시하면, 생성된 타입은 | null 체크로 가득합니다. 페이지에 다섯 개만 필요할 때 오퍼레이션이 40개 필드를 가져오면, 생성된 타입에도 40개 필드가 들어갑니다.

차이점을 간단히 요약하면 다음과 같습니다:

  수동 타입 생성된 타입
유지보수 스키마 변경마다 수동 변경 후 한 번의 명령
스키마 드리프트 가능하며 조용히 발생 생성 또는 컴파일 단계에서 포착
보일러플레이트 오퍼레이션이 늘수록 증가 수동 작성 불필요
커스텀 타입 통제 완전 플러그인 설정 옵션으로 제한
스키마 주도 개발 수동 대조 컴파일러가 영향받은 줄을 모두 표시

수동 타입과 생성 타입 비교

GraphQL Codegen vs. GraphQL 인트로스펙션

두 개념은 Codegen이 종종 인트로스펙션을 사용하기 때문에 혼동되곤 하지만, 해결하는 문제는 다릅니다.

인트로스펙션은 GraphQL 명세의 기능입니다. 모든 GraphQL 서버는 __schema, __type 같은 특수 필드를 통해 자신의 스키마에 대한 쿼리에 응답할 수 있습니다. 결과는 API가 노출하는 모든 타입, 필드, 인자에 대한 JSON 설명입니다.

쉽게 말해, 인트로스펙션은 "이 API는 어떻게 생겼나?"라는 질문에 답합니다.

Codegen은 "내 애플리케이션에 필요한 코드는 무엇인가?"라는 질문에 답합니다. 스키마 정보와 오퍼레이션을 받아 TypeScript 타입과 타입 지정 도큐먼트 노드를 작성합니다.

codegen.ts에서 schema를 URL로 설정하면, Codegen은 해당 서버에 인트로스펙션 쿼리를 보내 스키마를 가져옵니다. 로컬 .graphql 파일을 쓰는 경우에는 인트로스펙션이 전혀 일어나지 않습니다.

Codegen은 인트로스펙션 결과를 출력으로 생성할 수도 있습니다. @graphql-codegen/introspection 플러그인은 스키마를 인트로스펙션 JSON으로 작성하며, urql의 Graphcache 같은 일부 도구가 스키마 인지 캐싱에 사용합니다.

  인트로스펙션 GraphQL Codegen
정의 GraphQL 내장 질의 시스템 커맨드라인 도구와 플러그인
입력 실행 중인 서버로의 쿼리 스키마와 오퍼레이션
출력 스키마의 JSON 설명 타입, 도큐먼트 노드, 클라이언트 코드
실행 시점 클라이언트가 서버에 요청할 때마다 개발, 빌드, CI 동안

인트로스펙션과 Codegen 비교

자주 발생하는 GraphQL Codegen 문제

대부분의 Codegen 오류는 다음 네 가지 문제 중 하나로 귀결됩니다.

생성된 타입이 누락됨

Codegen은 오류 없이 실행되었지만 기대한 타입이 출력 파일에 없습니다.

다음 세 가지를 순서대로 확인하세요:

  • 스키마: 로드된 스키마에 해당 타입이 없으면 생성할 수 없습니다. 스키마 파일을 열거나 직접 인트로스펙션 쿼리를 실행해 존재 여부를 확인하세요

  • 도큐먼트: 오퍼레이션 타입은 Codegen이 찾은 오퍼레이션에 대해서만 존재합니다. 쿼리가 있는 파일이 documents 글롭과 일치하지 않으면 해당 타입이 빠집니다

  • 플러그인: v6부터 typescript-operations는 오퍼레이션에서 사용하는 enum과 input만 생성합니다. Course 같은 전체 스키마 타입이 필요하다면, 별도의 출력 파일에 typescript 플러그인을 추가하세요

설치하지 않은 플러그인을 사용하면, Codegen은 명확한 메시지와 함께 실패합니다:

Unable to find template plugin matching 'typescript-react-apollo'
Install one of the following packages:
- @graphql-codegen/typescript-react-apollo

Codegen이 오퍼레이션을 찾지 못함

documents 글롭이 어떤 오퍼레이션 파일과도 일치하지 않을 때 발생하는 오류입니다:

Unable to find any GraphQL type definitions for the following pointers:
  - src/**/*.gql

글롭의 확장자가 파일과 일치하는지 확인하세요. .gql 패턴은 .graphql 파일을 찾지 못하고, src/**/*.graphql 패턴은 .ts 파일 내부의 인라인 오퍼레이션을 찾지 못합니다.

인라인 오퍼레이션의 경우 태그도 확인하세요. Codegen은 gql로 감싼 템플릿 리터럴이나 /* GraphQL */ 주석이 있는 경우에만 찾습니다. 쿼리가 들어 있는 일반 템플릿 문자열은 인식하지 않습니다.

ignoreNoDocuments: true를 설정하면 Codegen은 이 오류를 표시하지 않고, 오퍼레이션 타입 없이 출력을 생성합니다. 초기 프로젝트에서는 유용하지만, 나중에는 깨진 글롭을 숨길 수 있습니다.

생성된 타입이 최신 상태가 아님

타입은 컴파일되지만, 오퍼레이션이나 스키마의 최신 변경을 반영하지 않습니다.

Codegen은 자동으로 실행되지 않습니다. .graphql 파일, 인라인 오퍼레이션, 스키마, codegen.ts를 변경할 때마다 다시 실행하세요. 개발 중에는 npm run codegen:watch가 이를 대신해 줍니다.

CI에서 타입이 오래되었다면, 생성 파일을 커밋하거나 prebuild 훅으로 빌드 전에 재생성하도록 하세요.

생성된 타입이 API와 일치하지 않음

타입은 컴파일되지만, 런타임의 서버 데이터 형태가 다릅니다.

대부분은 Codegen과 앱이 서로 다른 스키마를 참조하기 때문입니다. 예를 들어 Codegen은 일주일 전의 로컬 schema.graphql을 읽고, 앱은 이후 변경된 프로덕션 API를 호출하는 상황입니다.

schema 키가 가리키는 위치를 확인하세요. 파일이라면 앱이 호출하는 API와 비교해 최신으로 업데이트하세요. URL이라면 앱이 사용하는 환경과 동일한지(스테이징 등과 혼동되지 않는지) 확인하세요.

그다음 재생성하고 변경 사항을 확인하세요. 오퍼레이션 파일이 바뀌었는데 생성 파일이 바뀌지 않았다면, 해당 오퍼레이션이 documents 글롭 범위 밖일 가능성이 큽니다.

GraphQL Codegen 모범 사례

Codegen은 많은 설정이 없어도 잘 동작합니다. 프로젝트가 커져도 이를 유지하려면 다음 관행을 따르세요:

  • 스키마와 생성 파일을 동기화 상태로 유지: 개발 중에는 감시 모드를 실행하고, 빌드 전에는 prebuild를 사용해, 누구도 오래된 스키마에서 나온 타입으로 작업하지 않도록 하세요

  • 오퍼레이션 전용 타입 생성: 클라이언트 코드는 각 쿼리가 가져오는 내용과 정확히 일치하는 typescript-operations를 사용하세요

  • 워크플로우에 필요할 때만 생성 파일 커밋: 커밋하면 PR에서 타입 변경을 리뷰하고 CI에서 --check를 실행해 오래된 출력을 잡을 수 있습니다. 팀에 필요 없다면 출력 폴더를 .gitignore에 추가하고 빌드마다 생성하세요

  • 설정은 간결하게 유지: 대부분의 클라이언트 프로젝트는 두 개의 플러그인을 사용하는 하나의 출력 파일로 충분합니다. 불필요한 옵션은 이후 변경 시 이해해야 할 부담만 늘립니다

  • 모든 푸시에 CI에서 Codegen 실행: --check 단계나 prebuild 훅이 로컬 재생성을 깜빡한 변경을 잡아냅니다

  • 생성 파일을 수동으로 수정하지 않기: 다음 Codegen 실행이 변경 사항을 덮어씁니다. 대신 스키마, 오퍼레이션, 설정을 수정하세요

결론

GraphQL Codegen은 GraphQL 개발에서 가장 반복적인 작업—스키마와 오퍼레이션을 수동으로 TypeScript 타입으로 바꾸는 일—을 대신 수행합니다.

프로젝트 규모와 관계없이 워크플로우는 동일합니다. 스키마와 오퍼레이션을 정의하고, Codegen을 구성한 다음, 타입과 타입 지정 도큐먼트 노드를 생성해 애플리케이션 코드에 임포트합니다.

가장 큰 가치는 타입이 실제로 호출하는 API를 설명한다는 점입니다. 작성하지 않아도 되는 코드를 설명하는 것이 아니라 말이죠. 스키마가 변경되면 컴파일러가 문제가 생긴 부분을 알려주므로, 초기에 오류를 잡을 수 있습니다.

현대 NoSQL 데이터베이스에서 GraphQL이 어떻게 쓰이는지 알아보고 싶다면 MongoDB and GraphQL 블로그 글을 읽어보세요.

GraphQL Codegen 자주 묻는 질문

GraphQL Codegen은 무엇에 사용되나요?

GraphQL Codegen은 GraphQL 스키마와 오퍼레이션에서 코드를 생성합니다. 대부분의 팀은 쿼리와 뮤테이션을 위한 TypeScript 타입과 타입 지정 도큐먼트 노드를 얻기 위해 사용하여, 해당 타입을 수동으로 작성하고 업데이트할 필요를 없앱니다. 서버용 리졸버 타입이나 특정 클라이언트를 위한 준비된 훅도 생성할 수 있습니다.

Codegen을 사용하려면 실행 중인 GraphQL 서버가 필요한가요?

아니요. Codegen은 로컬 .graphql 파일에서 스키마를 읽을 수 있으므로, 서버 없이도 동작합니다. schema 키를 URL로 지정한 경우에만 실행 중인 서버가 필요합니다. 이때 Codegen은 인트로스펙션을 통해 스키마를 가져옵니다.

생성된 파일을 Git에 커밋해야 하나요?

팀의 워크플로우에 따라 다릅니다. 생성 파일을 커밋하면 PR에서 타입 변경을 리뷰할 수 있고, CI에서 graphql-codegen --check로 오래된 출력을 잡을 수 있습니다. 그런 필요가 없다면 출력 폴더를 .gitignore에 추가하고 빌드 전에 파일을 재생성하세요.

client 프리셋을 써야 하나요, 아니면 개별 플러그인을 써야 하나요?

팀의 워크플로우에 따라 다릅니다. 대부분의 클라이언트 앱에는 오퍼레이션 타입, 타입 지정 도큐먼트 노드, 프래그먼트 마스킹을 한 번에 제공하는 client 프리셋을 The Guild가 권장합니다. 다만 Apollo는 프리셋이 런타임 코드와 Apollo Client와 충돌하는 기능을 추가한다고 보고, 대신 typescript-operations 플러그인을 권장합니다. Apollo를 사용한다면 개별 플러그인을, 그 외에는 프리셋을 기본값으로 사용하세요.

Codegen 업데이트 후 중복 선언 오류가 나는 이유는 무엇인가요?

v6부터 typescript-operations 플러그인은 자체적으로 enum과 input 타입을 생성합니다. 설정에서 동일한 출력 파일에 typescript와 typescript-operations를 함께 나열하면, 두 플러그인이 Level 같은 타입을 모두 선언해 TypeScript가 중복 선언 오류를 발생시킵니다. 해당 출력에서 typescript 플러그인을 제거하거나, 전체 스키마 타입이 여전히 필요하다면 별도 파일로 옮기세요.

주제

DataCamp와 함께 배우기

강의

Software Development with Claude Code

4시간
8.7K
Claude Code brings AI assistance to your terminal. Learn the workflows that turn it into a reliable tool for real software development.
자세히 보기Right Arrow
강의 시작
더 보기Right Arrow