GraphQL 모범 사례
GraphQL은 충분히 성숙해졌으며 오랫동안 사용되어 왔기 때문에, 커뮤니티에서 모범 사례를 공유하는 많은 글이 발표되었습니다. 이 가이드들은 스키마 설계, 명명 규칙, 보안 처리, 의미 있는 오류 제공 등 GraphQL의 거의 모든 측면을 다루고 있습니다.
다음은 GraphQL 모범 사례에 관한 가장 설득력 있는 가이드들입니다.
graphql.org의 모범 사례
GraphQL 공식 사이트에서는 GraphQL 모범 사례에 대한 일반적인 소개를 제공합니다.
이 항목들은 주로 다음과 같은 상위 수준의 관심사를 다룹니다:
- 결과를 페이지네이션하는 최선의 방법
- 아키텍처 내에서 GraphQL 레이어의 위치
- 전역 객체 식별을 위한 Node 인터페이스 사용 방법
- 결과를 캐시하는 방법
- 그 외 다수

Lee Byron의 권고 사항
GraphQL을 세상에 공개한 직후, GraphQL 창시자인 Lee Byron은 Lessons From 4 Years of GraphQL 이라는 글을 발표하여 GraphQL을 어떻게 개념적으로 활용해야 하는지 설명했습니다:
- 명명은 중요합니다
- 엔드포인트가 아닌 그래프로 생각하세요
- 뷰가 아닌 데이터를 기술하세요
- GraphQL은 얇은 인터페이스입니다
- 구현 세부 사항을 숨기세요
또한 Facebook에서 GraphQL을 사용하면서 배운 귀중한 원칙과 교훈들도 자세히 설명하고 있습니다.
GraphQL Rules
GraphQL Rules는 GraphQL을 사용하는 일상적인 모범 사례, 특히 GraphQL 스키마 설계에 특화된 사이트입니다.
이 리소스는 매우 상세합니다. Designing GraphQL Mutations 글이나 Shopify의 튜토리얼 Designing a GraphQL API 등 몇 가지 뛰어난 리소스의 정보를 모아 간결하게 제시합니다.
기술된 규칙에는 다음이 포함됩니다:
- 명명 규칙
- GraphQL 필드와 인수에는
camelCase를 사용합니다. - GraphQL 타입에는
UpperCamelCase를 사용합니다. - ENUM 타입 이름에는
CAPITALIZED_WITH_UNDERSCORES를 사용합니다.
- GraphQL 필드와 인수에는
- 타입 규칙
- 특정 의미 값을 가진 필드나 인수를 선언할 때는 커스텀 스칼라 타입을 사용합니다.
- 특정 값 집합을 포함하는 필드에는 Enum을 사용합니다.
- 필드 규칙 (Output)
- 필드에는 의미 있는 이름을 사용하고 필드 이름에 구현 세부 사항이 노출되지 않도록 합니다.
- 필드가 항상 특정 값을 가지는 경우
Non-Null필드를 사용합니다. - 관련 필드는 최대한 커스텀 Object 타입으로 묶습니다.
- 인수 규칙 (Input)
- 연관된 인수는 새로운 input-type으로 묶습니다.
- 인수에는 엄격한 스칼라 타입을 사용합니다. 예:
String대신DateTime. - 쿼리 실행에 필요한 인수는
required로 표시합니다.
- 목록 규칙
- 목록 필터링에는 사용 가능한 모든 필터를 포함하는
filter인수를 사용합니다. - 목록 정렬에는
Enum또는[Enum!]타입의sort인수를 사용합니다. - 반환되는 항목 수를 제한하기 위해 기본값이 있는
limit와skip을 사용합니다. - 페이지네이션에는
page·perPage인수를 사용하고items(요소 배열)와pageInfo(메타데이터)를 포함하는 output 타입을 반환합니다. - 무한 스크롤 목록에는 Relay Cursor Connections Specification을 사용합니다.
- 목록 필터링에는 사용 가능한 모든 필터를 포함하는
- Mutation 규칙
- 단일 리소스 내의 mutation을 그룹화하기 위해 Namespace-type을 사용합니다.
- CRUD를 넘어서 생각하세요 — 리소스에 대한 다양한 비즈니스 작업을 위한 작은 mutation을 만드세요.
- 여러 항목에 대해 mutation을 수행할 수 있는 기능(동일 타입 일괄 변경)을 고려합니다.
- Mutation은 모든 필수 인수를 명확히 기술해야 하며, 양자택일 옵션이 있어서는 안 됩니다.
- Mutation에서는 모든 변수를 하나의 고유한 input 인수에 넣습니다.
- 모든 mutation은 고유한 payload 타입을 가져야 합니다.
리졸버 모범 사례
GraphQL Resolvers: Best Practices 글은 필드 리졸버를 최적으로 작성하는 방법을 설명합니다. Node.js 서버(PayPal의 인프라는 Express 기반)를 대상으로 하고 있지만, PHP를 포함한 다른 기술에도 적용할 수 있는 교훈이 많이 포함되어 있습니다.
주요 요점은 다음과 같습니다:
- 부모에서 자식으로의 데이터 가져오기 및 전달은 신중하게 사용합니다.
- dataloader 같은 라이브러리를 사용하여 하위 요청의 중복을 제거합니다.
- 데이터 소스에 가하고 있는 부하를 파악합니다.
- "context"를 변경하지 않습니다. 일관성 있고 버그가 적은 코드를 보장합니다.
- 읽기 쉽고 유지 관리하기 쉬우며 테스트 가능한 리졸버를 작성합니다. 지나치게 복잡하게 만들지 않습니다.
- 리졸버를 최대한 얇게 유지합니다. 데이터 가져오기 로직을 재사용 가능한 비동기 함수로 추출합니다.
OWASP - GraphQL Cheat Sheet
OWASP(Open Web Application Security Project)는 소프트웨어 보안 향상을 위해 활동하는 비영리 재단입니다. 다양한 기술이 어떤 취약점에 노출되어 있는지 연구하고, 보안 문제에 대한 해결책을 상세히 설명하여 개발자가 공격을 예방하기 쉽도록 합니다.
OWASP는 GraphQL Cheat Sheet를 공개하여 GraphQL에서 가장 일반적인 공격과 주요 보안 문제, 그리고 이를 해결하는 방법을 설명합니다.
GraphQL에 대한 일반적인 공격에는 다음이 포함됩니다:
- 인젝션 — 일반적으로 다음을 포함하지만 이에 한정되지 않습니다:
- SQL 및 NoSQL 인젝션
- OS 커맨드 인젝션
- SSRF 및 CRLF 인젝션/요청 스머글링
- DoS(서비스 거부)
- 손상된 인가 남용: IDOR를 포함한 부적절하거나 과도한 접근
- 배치 공격(Batching Attacks), GraphQL 고유의 무차별 대입 공격 기법
- 안전하지 않은 기본 설정 남용
OWASP는 이러한 각각을 방지하기 위한 권고 사항을 제공합니다.
GraphQL 쿼리 모범 사례
Apollo 팀은 GraphQL query best practices를 공개하여 GraphQL 쿼리를 구성하는 실용적인 방법에 대한 실질적인 통찰을 제공합니다.
예를 들어, 다음 두 쿼리는 동일한 목표를 달성하지만, 첫 번째 쿼리에는 오퍼레이션 이름이 있어 디버깅 시 더 이해하기 쉽고 유용합니다:
# Recommended ✅
query GetBooks {
books {
title
}
}
# Not recommended ❌
query {
books {
title
}
}권고 사항에는 다음이 포함됩니다:
- 모든 오퍼레이션에 이름을 붙입니다
- GraphQL 인수를 제공하기 위해 변수를 사용합니다
- 필요한 곳에서 필요한 데이터만 쿼리합니다
- 관련 필드 집합을 캡슐화하기 위해 fragment를 사용합니다
- 전역 데이터와 사용자별 데이터를 분리하여 쿼리합니다
one graph 활용
마찬가지로 Apollo 팀의 사이트 Principled GraphQL은 GraphQL이 단순한 명세가 아니라, 어쩌면 더 중요하게는 회사의 데이터 "그래프"와 상호작용하기 위한 인터페이스임을 설명합니다.
10가지 원칙 목록을 통해 이 사이트는 단일 그래프를 최대한 활용하는 방법을 설명합니다:
- One Graph: 회사는 각 팀이 만든 여러 그래프 대신 하나의 통합된 그래프를 가져야 합니다.
- Federated Implementation: 그래프는 하나뿐이지만, 그 그래프의 구현은 여러 팀에 걸쳐 연합되어야 합니다.
- Track the Schema in a Registry: 그래프를 등록하고 추적하기 위한 단일 진실의 원천이 있어야 합니다.
- Abstract, Demand-Oriented Schema: 스키마는 서비스 구현 세부 사항을 숨기면서 소비자에게 유연성을 제공하는 추상화 레이어 역할을 해야 합니다.
- Use an Agile Approach to Schema Development: 스키마는 실제 요구 사항에 기반하여 점진적으로 구축되고 시간이 지남에 따라 원활하게 발전해야 합니다.
- Iteratively Improve Performance: 성능 관리는 변화하는 쿼리 부하와 서비스 구현에 원활하게 적응하는 지속적인 데이터 기반 프로세스여야 합니다.
- Use Graph Metadata to Empower Developers: 개발자는 전체 개발 프로세스 전반에 걸쳐 그래프에 대한 풍부한 인식을 갖추어야 합니다.
- Access and Demand Control: 클라이언트별로 그래프에 대한 접근을 허용하고, 클라이언트가 무엇에 어떻게 접근할 수 있는지 관리합니다.
- Structured Logging: 모든 그래프 오퍼레이션의 구조화된 로그를 수집하고, 그래프 사용 현황을 이해하기 위한 주요 도구로 활용합니다.
- Separate the GraphQL Layer from the Service Layer: 그래프 기능을 각 서비스에 내장하는 대신 별도의 계층으로 분리한 레이어드 아키텍처를 채택합니다.