핏펫 커머스 플랫폼이 GraphQL 스키마에서 프론트엔드 TypeScript interface 를 자동 생성하고, Apollo Client 의 codegen 으로는 부족한 부분을 schema.json 을 직접 파싱해 원하는 형태로 확장한 기록이다. 백엔드가 Django(Graphene) 이지만 schema.graphql 의 json 형태가 같아 다른 언어에도 옮길 수 있다고 밝힌다.
핵심 포인트- 개발 초기에는 API 변경이 잦고 그때마다 이미 만든 interface 를 손으로 고쳐야 하는데, 이를 스키마에서 자동 생성으로 대체한다.
- Apollo Client 의 codegen 은 GraphQL 문서의 유효성 검사(중복 query·존재하지 않는 field 검출)와 TypeScript 파일 생성 두 가지를 해 준다.
- 다만 생성 결과에는 camelCase 와 snake_case 가 섞인 이름이 나오고, 날짜·object 가 `any` 로 떨어지는 등 손댈 수 없는 한계가 있다.
- 그래서 schema.graphql 을 json 으로 뽑아 메모리에 올리고 types 정보를 파싱해, 원하는 형태의 interface·model class·mutation 파라미터·query/mutation 구문을 직접 생성했다.
- 스크립트 한 번 실행으로 백엔드 변경분이 프론트엔드 파일에 반영되도록 묶어 두었다.
상세 정리- 배경 정리: Graphene-Django 로 Query(R)와 Mutation(CUD)을 등록하며, Query 로도 수정이 가능하지만 명시적으로 Mutation 을 쓰는 규칙이 낫다고 짚는다.
- Type 정의: Django 모델을 참조하면 대부분의 field 가 자동 선언되므로 중복 선언이 필요 없고, 추가할 field 는 `resolve_()` 로 선언한다. 1:N 관계도 related_name 으로 자동 연결된다.
- 준비: 백엔드에서 schema.graphql 을 생성하고, 필요하면 json 형태로도 추출한다. graphql 파일은 Apollo 의 정합성 체크·interface 생성에, json 은 자유로운 커스텀 생성에 쓴다.
- Apollo 설정: 프로젝트 루트에 `apollo.config.js` 를 두고 gql 문법이 있는 경로(includes), 로컬 GraphQL 서버 endpoint, 또는 `localSchemaFile` 경로를 지정한다.
- 한계 확인: createReview mutation 의 생성 결과에서 `createReviewVariables`, `createReview_createReview_review` 처럼 이름이 길고 표기법이 섞여 나오는데 tooling 안에서는 더 손댈 수 없다.
- json 파싱: schema.json 의 directives 등을 빼면 실제 필요한 데이터는 전부 `types` 안에 있으므로 그 값만 메모리에 담고 파일은 바로 지운다. 한글 설명이 깨지지 않게 `ensure_ascii=False` 를 준다.
- 구조 파악: PageInfo(페이지네이션), Type(싱글 쿼리), TypeConnection(페이지네이션 리스트), Mutation 계층으로 표현되며 TypeConnection 이 하위에 PageInfo 나 Type 을 갖는 식으로 재사용된다.
- 파싱 규칙 ①: 각 type 의 첫 번째 fields depth 에 담긴 정보만이 그 type 의 field 를 설명하고, 한 type 이 다른 type 을 반복 참조할 수 있다.
- 파싱 규칙 ②: `fields > type > kind` 가 OBJECT 면 다른 type 참조, NON_NULL 이면 required 이며 이 경우 `ofType` 으로 한 번 더 감싸 속성을 표현한다. OBJECT 이면서 NON_NULL 이면 OBJECT 를 먼저 명시한다.
- 파싱 규칙 ③: list 여부는 type_kind 가 LIST 이거나 이름에 Connection suffix 가 있는지로 판별한다.
- Query 와 Mutation 정보는 schema_types 중 name 이 각각 Query, Mutation 인 object 에 모두 들어 있고, 여기에는 추가로 받을 수 있는 매개변수도 정의된다.
- 자동 생성물 ①: interface 를 재생성해 날짜를 Moment 로, object 를 정확한 타입명으로 바꾸고, 공통 id 는 상속으로 재활용하며 연결된 커스텀 type 도 미리 선언했다.
- 자동 생성물 ②: type 을 프론트엔드 class 로 만들어 모델 내 반복 로직을 담았다. API 응답이 자동으로 모델화되고, 필요하면 한 번 더 상속해 메서드를 추가한다.
- 자동 생성물 ③: mutation 의 variable 로 넣을 파라미터 object 를 만들어 유효성 검증을 쉽게 했다. Input interface 는 Apollo codegen 결과를 그대로 활용했다.
- 자동 생성물 ④: Apollo Client 문법에 맞는 query·mutation 구문과, 재사용 조각인 Fragment 를 생성했다.
- 주의점: query·mutation 자동 생성 시 type depth 가 두 단계를 넘지 않게 한다. product > category > product 처럼 루프가 생길 수 있기 때문이다.
- 운영: 프론트엔드 경로를 인자로 받는 파이썬 스크립트로 묶어, 백엔드가 바뀔 때마다 한 번 실행하면 반영된다.
- 실사용 결론: query·mutation 은 자동 생성 코드를 참조하되 별도로 작성하는 쪽을 택했다. 종류마다 필요한 response data 가 type 전체가 아니기 때문이다. 그럼에도 백엔드 변경을 파일 하나에서 확인할 수 있어 효과적이었다.
왜 읽나GraphQL 스키마와 프론트엔드 타입을 손으로 맞추고 있는 팀에게, 기성 codegen 의 한계와 그 너머를 직접 만드는 방법을 함께 보여준다.