GraphQL

현행 작업 초안

소개

이 문서는 GraphQL의 명세서로, 클라이언트-서버 애플리케이션의 데이터 모델의 기능과 요구사항을 설명하고 수행하는 질의 언어 및 실행 엔진입니다.

GraphQL에 적합한 구현체는 이 명세서에 명시된 모든 규범적인 요구사항을 반드시 준수해야 합니다(적합성 참조). GraphQL 명세서는 OWFa 1.0 라이선스 하에 제공됩니다(저작권 및 라이선스 참조).

GraphQL은 2012년에 처음 만들어졌으며, 이 오픈 표준의 개발은 2015년에 시작되었습니다. 이는 2019년에 GraphQL 명세 프로젝트Joint Development Foundation이 설립되어 제공하는 산출물입니다.

GraphQL Foundation은 2019년에 설립되었으며 GraphQL 생태계 개발을 지원하는 여러 조직들의 중립적인 중심점 역할을 합니다. 귀하의 조직이 GraphQL로 혜택을 보고 있다면, 회원 가입을 고려해보세요.

이 명세서는 graphql/graphql-spec GitHub에서 개발됩니다. 기여는 GraphQL Working Group에 의해 관리되며, GraphQL Technical Steering Committee가 주관합니다. 더 자세한 정보는 기여 가이드를 참고하세요.

GraphQL은 발전해왔으며, 이 명세서의 향후 판에서도 계속 진화할 수 있습니다. 이전 GraphQL 명세서는 해당 릴리즈 태그와 일치하는 퍼머링크에서 확인할 수 있습니다. 최신 작업 초안 릴리즈는 https://spec.graphql.org/draft에서 확인할 수 있습니다.

1개요

GraphQL은 직관적이고 유연한 문법과 시스템을 제공하여 클라이언트 애플리케이션을 구축하기 위해 설계된 질의 언어입니다. 이를 통해 데이터 요구사항과 상호작용을 쉽게 설명할 수 있습니다.

예를 들어, 다음 GraphQL 요청은 Facebook의 GraphQL 구현에서 id가 4인 사용자의 이름을 반환합니다.

예시 № 1{
  user(id: 4) {
    name
  }
}

이 요청의 결과 데이터(JSON)는 다음과 같습니다:

예시 № 2{
  "user": {
    "name": "Mark Zuckerberg"
  }
}

GraphQL은 임의 계산을 수행할 수 있는 프로그래밍 언어가 아니라, 이 명세서에 정의된 기능을 가진 애플리케이션 서비스에 요청을 하는 데 사용되는 언어입니다. GraphQL은 구현 서비스의 프로그래밍 언어나 저장 시스템을 지정하지 않습니다. 대신, 애플리케이션 서비스는 자신의 기능을 통일된 언어, 타입 시스템, 철학에 매핑합니다. GraphQL은 제품 개발에 친화적인 통합 인터페이스와 도구 제작에 강력한 플랫폼을 제공합니다.

GraphQL의 주요 설계 원칙은 다음과 같습니다:

이러한 원칙 덕분에 GraphQL은 클라이언트 애플리케이션을 구축하는 데 강력하고 생산적인 환경을 제공합니다. 품질 좋은 도구가 지원되는 GraphQL 서비스와 함께 애플리케이션을 개발하는 사람들은 방대한 문서를 읽지 않거나 별도의 공식 교육 없이도 빠르게 작업할 수 있습니다. 이를 위해서는 서비스를 구축하고 도구를 만드는 사람들이 필요합니다.

다음의 공식 명세서는 그런 구축자들을 위한 참고 자료입니다. 명세서에서는 언어와 문법, 타입 시스템과 내성 시스템, 그리고 실행 및 검증 엔진과 알고리즘을 설명합니다. 이 명세서는 조직과 플랫폼을 넘나드는 GraphQL 도구, 클라이언트 라이브러리, 서비스 구현의 생태계를 위한 기초와 프레임워크를 제공합니다. 우리는 이를 위해 커뮤니티와 함께 작업하는 것을 기대합니다.

2언어

클라이언트는 GraphQL 질의 언어를 사용하여 GraphQL 서비스에 요청을 보냅니다. 이러한 요청 소스를 문서(document)라고 부릅니다. 문서에는 연산(쿼리, 변이, 구독)과 데이터 요구의 재사용을 가능하게 하는 프래그먼트라는 일반적인 구성 단위가 포함될 수 있습니다.

GraphQL 문서는 종단 기호가 토큰(나눌 수 없는 어휘 단위)인 통사적 문법으로 정의됩니다. 이러한 토큰은 소스 문자 패턴에 맞는 어휘 문법에서 정의됩니다. 이 문서에서 통사적 문법 생성은 콜론 :으로, 어휘 문법 생성은 더블 콜론 ::으로 구분됩니다.

GraphQL 문서의 소스 텍스트는 SourceCharacter의 연속이어야 합니다. 문자 시퀀스는 TokenIgnored 어휘 문법으로 기술되어야 합니다. Ignored을 생략한 어휘 토큰 시퀀스는 반드시 하나의 Document 통사적 문법으로 기술되어야 합니다.

비고 이 문서 곳곳에서 사용되는 어휘 및 통사 문법과 기타 표기 관례에 대한 추가 정보는 부록 A를 참조하세요.
어휘 분석 & 통사적 파싱

GraphQL 문서의 소스 텍스트는 먼저 어휘 토큰(Token) 및 무시된 토큰(Ignored) 시퀀스로 변환됩니다. 소스 텍스트는 왼쪽에서 오른쪽으로 스캔하며, 어휘 문법 생성에 허용된 다음 가능한 코드포인트 시퀀스를 반복적으로 다음 토큰으로 취합니다. 그 다음, 어휘 토큰 시퀀스는 왼쪽에서 오른쪽으로 다시 스캔되어 Document 통사적 문법에 따라 추상 구문 트리(AST)로 생성됩니다.

이 문서의 어휘 문법 생성은 lookahead 제한을 사용하여 모호성을 제거하고 하나의 유효한 어휘 분석만을 보장합니다. 어휘 토큰은 lookahead 제한에 해당하는 문자가 뒤따르지 않을 때만 유효합니다.

예를 들어, IntValueDigit 제한을 가지므로 뒤에 Digit이 올 수 없습니다. 그래서 123 시퀀스는 (12, 3) 토큰을 의미할 수 없으며, 12Digit 3이 뒤따르기 때문에 하나의 토큰만을 나타냅니다. 여러 토큰을 나타내려면 각 문자 사이에 Whitespace 또는 다른 Ignored를 사용하세요.

비고 이는 일반적으로 “최대로 먹기(maximal munch)”의 가장 긴 일치와 동일한 동작을 하지만, 일부 lookahead 제한은 추가 제약이 포함될 수 있습니다.

2.1소스 텍스트

SourceCharacter
Any Unicode scalar value

GraphQL 문서는 SourceCharacter의 연속인 소스 텍스트에서 해석되며, 각각의 SourceCharacterUnicode 스칼라 값입니다. 이는 U+0000에서 U+D7FF 또는 U+E000에서 U+10FFFF까지의 모든 Unicode 코드 포인트가 될 수 있으며, 이 명세서에서는 대부분 비공식적으로 “문자(character)”라고 부릅니다.

GraphQL 문서는 ASCII 범위 내에서만 표현될 수 있으며, 이는 다양한 도구, 언어, 직렬화 포맷과의 호환성을 극대화하고 텍스트 에디터 및 소스 관리에서 표시 문제를 방지하기 위함입니다. 비 ASCII Unicode 스칼라 값은 StringValueComment 내에 나타날 수 있습니다.

비고 예를 들어 JavaScript나 Java 같이 GraphQL 문서를 메모리 내에서 UTF-16으로 표현하는 구현체는 대리쌍(surrogate pair)을 만날 수 있습니다. 이들은 하나의 보조 코드 포인트(supplementary code point)를 인코딩하며 하나의 유효한 소스 문자이지만, 쌍이 맞지 않는 대리 코드 포인트(surrogate code point)는 유효한 소스 문자가 아닙니다.

2.1.1공백 문자

Whitespace
Horizontal Tab (U+0009)
Space (U+0020)

공백은 소스 텍스트의 가독성을 높이고 다른 토큰을 분리하기 위해 사용됩니다. 어떠한 토큰 앞뒤에도 얼마든지 공백 문자가 올 수 있습니다. 토큰 사이의 공백은 GraphQL 문서의 의미에 영향을 주지 않으나, 공백 문자는 String 또는 Comment 토큰 안에는 포함될 수 있습니다.

비고 GraphQL은 Unicode “Zs” 범주 문자를 공백으로 간주하지 않으며, 텍스트 에디터나 소스 관리 도구에 의해 잘못 해석되는 것을 방지합니다.

2.1.2줄 끝

LineTerminator
New Line (U+000A)
Carriage Return (U+000D)New Line (U+000A)
Carriage Return (U+000D)New Line (U+000A)

공백과 마찬가지로, 줄 끝 문자는 소스 텍스트의 가독성을 높이고 어휘 토큰을 구분합니다. 얼마든지 다른 토큰 앞뒤에 나올 수 있으며 GraphQL 문서의 의미에는 영향을 주지 않습니다.

비고 구문 오류와 관련된 소스의 줄 번호를 제공하는 오류 보고는 선행 LineTerminator 개수를 참고하여 줄 번호를 산출해야 합니다.

2.1.3주석

GraphQL 소스 문서에는 # 마커로 시작하는 단일 행 주석이 포함될 수 있습니다.

주석에는 SourceCharacterLineTerminator을 제외한 모든 문자가 올 수 있으며, 따라서 주석은 # 문자로 시작하여 LineTerminator (또는 소스 끝) 이전까지 모든 SourceCharacter로 구성됩니다.

주석은 공백과 마찬가지로 Ignored로 간주되며, 모든 토큰 뒤나 LineTerminator 앞에 올 수 있고 GraphQL 문서의 의미에는 영향을 주지 않습니다.

2.1.4의미 없는 쉼표

Comma
,

공백이나 줄 끝 문자와 유사하게, 쉼표(,)는 소스 텍스트의 가독성을 높이고 어휘 토큰을 구분하는 데 사용되지만, GraphQL 문서 내에서는 문법적, 의미적으로 중요하지 않습니다.

비중요 쉼표 문자는 쉼표의 부재 또는 존재가 문서의 해석되는 구문에 의미적으로 영향을 주지 않음을 보장합니다. 이는 다른 언어에서 흔히 발생하는 사용자 오류를 예방합니다. 또한 가독성 및 유지보수를 위해 리스트 구분에 후행 쉼표 또는 줄 끝 문자를 사용할 수 있게 합니다.

2.1.5어휘 토큰

GraphQL 문서는 여기서 정의된 여러 종류의 나눌 수 없는 어휘 토큰으로 구성되며, 소스 Unicode 문자 패턴에 따라 어휘 문법으로 정의됩니다. 어휘 토큰은 Ignored 토큰으로 구분될 수 있습니다.

토큰은 이후 GraphQL 통사적 문법 규칙에서 종단 기호로 사용됩니다.

2.1.6무시된 토큰

Ignored 토큰은 가독성을 높이고 어휘 토큰 사이의 구분을 위해 사용되지만, 그 외의 경우에는 중요하지 않으며 통사적 문법 생성에서는 참조되지 않습니다.

모든 어휘 토큰 앞뒤에는 얼마든지 Ignored가 올 수 있습니다. 소스 문서의 무시된 영역은 의미가 없지만, SourceCharacterIgnored에 나타날 경우 어휘 Token 내에도 의미 있게 포함될 수 있습니다. 예를 들어 StringValue는 공백 문자를 포함할 수 있습니다. Ignored토큰 내부에 나타날 수 없으며, 예를 들어 FloatValue를 정의하는 문자 사이에는 공백 문자가 허용되지 않습니다.

바이트 순서 표시(Byte Order Mark)
UnicodeBOM
Byte Order Mark (U+FEFF)

바이트 순서 표시(Byte Order Mark)는 파일의 시작 부분에 나타날 수 있는 특별한 Unicode 코드 포인트로, 프로그램이 해당 텍스트가 Unicode임을 파악하고 어떤 인코딩이 사용되었는지 알 수 있도록 합니다. 파일이 종종 연결되기 때문에, 바이트 순서 표시는 어떤 어휘 토큰 앞이나 뒤에도 올 수 있으며 Ignored로 간주됩니다.

2.1.7구두점

Punctuator
! $ & ( ) ... : = @ [ ] { | }

GraphQL 문서에는 구조를 설명하기 위해 구두점이 포함됩니다. GraphQL은 데이터 설명 언어이며 프로그래밍 언어가 아니므로, GraphQL에는 수식을 표현할 때 자주 사용되는 구두점이 없습니다.

2.1.8이름(Name)

Letter
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m
n o p q r s t u v w x y z
Digit
0 1 2 3 4 5 6 7 8 9

GraphQL 문서에는 여러가지 이름이 많이 등장합니다: 연산, 필드, 인자, 타입, 지시문, 프래그먼트, 변수 등. 모든 이름은 동일한 문법 형식을 따라야 합니다.

GraphQL에서 이름은 대소문자를 구분합니다. 즉, name, Name, NAME은 각각 다른 이름을 의미합니다. 언더스코어도 구분에 영향을 주는데, other_nameothername은 다른 이름입니다.

Name 뒤에는 NameContinue이 올 수 없습니다. 즉, Name 토큰은 항상 가능한 가장 긴 유효 시퀀스여야 합니다. 예를 들어 a1a 뒤에 NameContinue 1이 오기 때문에 두 토큰으로 나눠 해석할 수 없습니다.

비고 GraphQL의 이름은 가능한 많은 시스템과의 상호운용성을 위해 라틴 ASCII 하위 집합 SourceCharacter로 제한됩니다.
예약된 이름(Reserved Names)

GraphQL 타입 시스템 내의 모든 Name은 두 개의 언더스코어 "__"로 시작하면 안 됩니다. 단, 이 명세서 정의에 따라 내성(introspection) 시스템의 일부인 경우는 예외입니다.

2.2설명(Descriptions)

GraphQL에서는 실행 문서(Document) 및 GraphQL 타입 시스템의 모든 명명 정의에 설명을 추가하여 문서화를 중요한 기능으로 삼습니다. 이 설명은 내성(introspection)을 통해서도 제공되어 GraphQL 서비스의 문서가 기능과 항상 일치하도록 보장합니다(타입 시스템 설명 참고).

GraphQL 설명은 CommonMark로 규정된 Markdown으로 제공됩니다. 설명 문자열(대개 BlockString 형태)은 설명 대상 정의 바로 앞에 위치합니다.

GraphQL 실행 문서 내에 있는 설명은 오직 문서화 목적만을 위한 것입니다. 설명은 문서의 실행, 검증, 응답에 아무 영향을 주지 않아야 하며, 실행 문서에서 설명과 주석을 모두 제거해도 동작이나 결과는 달라지지 않습니다.

아래는 잘 설명된 연산의 예시입니다:

예시 № 3"""
Request the current status of a time machine and its operator.
You can also check the status for a particular year.
**Warning:** certain years may trigger an anomaly in the space-time continuum.
"""
query GetTimeMachineStatus(
  "The unique serial number of the time machine to inspect."
  $machineId: ID!
  "The year to check the status for."
  $year: Int
) {
  timeMachine(id: $machineId) {
    ...TimeMachineDetails
    status(year: $year)
  }
}

"Details about a time machine and its operator."
fragment TimeMachineDetails on TimeMachine {
  id
  model
  lastMaintenance
  operator {
    name
    licenseLevel
  }
}

2.3문서(Document)

GraphQL 문서는 GraphQL 서비스 또는 클라이언트가 처리하는 전체 파일 또는 요청 문자열을 설명합니다. 하나의 문서에는 여러 개의 정의가 포함될 수 있는데, 각각 실행 가능하거나 GraphQL 타입 시스템을 나타냅니다.

문서가 ExecutableDocument이며 최소 하나의 OperationDefinition을 포함하는 경우에만 GraphQL 서비스에 의해 실행될 수 있습니다. TypeSystemDefinitionOrExtension을 포함하는 문서는 실행되지 않아야 하며, 이러한 문서를 받은 GraphQL 실행 서비스는 설명이 포함된 오류를 반환해야 합니다.

단순히 GraphQL 요청만 실행하고 새로운 GraphQL 스키마를 생성하지 않는 서비스를 구현할 때는 ExecutableDocument만 허용하도록 선택할 수 있습니다.

OperationDefinition이 없거나 TypeSystemDefinitionOrExtension이 포함된 문서도 파싱 및 검증이 가능하여, 클라이언트 도구가 여러 개의 파일에서 등장할 수 있는 다양한 GraphQL 사용 사례를 표현할 수 있도록 해줍니다.

만약 하나의 문서에 연산이 하나만 있다면 그 연산은 이름이 없어도 됩니다. 변수와 지시문이 없는 쿼리라면 query 키워드 및 연산 이름을 생략한 약식(shorthand) 형태로 표시할 수도 있습니다. 반대로, 문서 내에 여러 연산이 있을 경우 각 연산에는 이름을 지정해야 하며, 여러 연산이 포함된 문서를 GraphQL 서비스에 제출할 때는 실행할 연산의 이름도 반드시 함께 제공해야 합니다.

2.4연산

OperationType
query mutation subscription

GraphQL이 모델링하는 연산의 종류는 세 가지입니다:

  • query – 읽기 전용 조회.
  • mutation – 쓰기 작업 후 조회.
  • subscription – 시간 경과에 따른 이벤트 시퀀스에 응답하여 데이터를 가져오는 장기(오래 지속되는) 요청.

각 연산은 선택적으로 연산 이름과 selection set으로 표현됩니다.

예를 들어, 다음의 mutation 연산은 어떤 스토리에 “좋아요”를 표시하고 그 후 업데이트된 좋아요 수를 가져올 수 있습니다:

Example № 4"""
Mark story 12345 as "liked"
and return the updated number of likes on the story
"""
mutation {
  likeStory(storyID: 12345) {
    story {
      likeCount
    }
  }
}
쿼리 약식 문법

문서에 연산이 하나뿐이고 그 연산이 변수도 없고 지시문도 적용되어 있지 않은 쿼리인 경우, 해당 연산은 query 키워드와 연산 이름을 생략한 약식 표기(shorthand)로 표현할 수 있습니다.

예를 들어, 이름이 없는 이 쿼리 연산은 쿼리 약식 문법으로 작성된 것입니다.

Example № 5{
  field
}

약식 쿼리 문법에는 설명(Descriptions)을 사용할 수 없습니다.

비고 아래 많은 예제에서 쿼리 약식 문법을 사용할 것입니다.

2.5선택 집합

연산은 필요한 정보의 집합을 선택하며, 과다/과소 페칭을 피하여 정확히 그 정보만을 받습니다.

selection set은 객체, 유니온 또는 인터페이스 타입에 대해 선택(필드, 프래그먼트 스프래드 및 인라인 프래그먼트)의 정렬된 집합을 정의합니다.

Example № 6{
  id
  firstName
  lastName
}

이 쿼리 연산에서 id, firstName, lastName 필드가 selection set을 구성합니다. 선택 집합은 프래그먼트 참조도 포함할 수 있습니다.

2.6필드

selection set은 주로 필드로 구성됩니다. 필드는 선택 집합 내에서 요청할 수 있는 하나의 개별적인 정보를 설명합니다.

일부 필드는 복잡한 데이터나 다른 데이터와의 관계를 설명합니다. 이러한 데이터를 더 탐색하기 위해 필드 자체가 선택 집합을 포함할 수 있어 깊이 있는 중첩 요청이 가능합니다. 모든 GraphQL 연산은 응답이 스칼라 값을 반환하는 필드까지 선택을 명시하여 응답의 형태가 명확하도록 해야 합니다.

예를 들어, 이 연산은 복합 데이터와 관계의 필드를 스칼라 값까지 선택합니다.

Example № 7{
  me {
    id
    firstName
    lastName
    birthday {
      month
      day
    }
    friends {
      name
    }
  }
}

연산의 최상위 selection set에 있는 필드는 종종 애플리케이션과 현재 뷰어가 전역적으로 접근 가능한 정보를 나타냅니다. 일반적인 예로는 현재 로그인한 뷰어에 대한 참조나 고유 식별자에 의해 참조되는 특정 타입의 데이터에 접근하는 필드 등이 있습니다.

Example № 8# `me` could represent the currently logged in viewer.
{
  me {
    name
  }
}

# `user` represents one of many users in a graph of data, referred to by a
# unique identifier.
{
  user(id: 4) {
    name
  }
}

2.7인자

ArgumentsConst
(ArgumentConstlist)
ArgumentConst
Name:ValueConst

필드는 개념적으로 값을 반환하는 함수이며, 때로는 동작을 변경하는 인자를 받을 수 있습니다. 이러한 인자들은 GraphQL 서비스 구현 내의 함수 인자와 직접 매핑되는 경우가 많습니다.

예를 들어, 특정 사용자를 질의하고(인자 id로 요청) 특정 size의 프로필 사진을 가져오려는 경우:

Example № 9{
  user(id: 4) {
    id
    name
    profilePic(size: 100)
  }
}

하나의 필드에 여러 인자가 있을 수 있습니다:

Example № 10{
  user(id: 4) {
    id
    name
    profilePic(width: 100, height: 50)
  }
}
인자는 순서가 없다

인자는 어떤 구문적 순서로 제공되어도 동일한 의미를 유지합니다.

다음 두 연산은 의미적으로 동일합니다:

Example № 11{
  picture(width: 200, height: 100)
}
Example № 12{
  picture(height: 100, width: 200)
}

2.8필드 별칭

response name은 응답 객체에서 필드의 결과와 대응되는 키입니다. 기본적으로 response name은 필드 이름을 사용하지만, 별칭을 지정하면 다른 response name을 정의할 수 있습니다.

예를 들어 서로 다른 크기의 프로필 사진 두 개를 가져오고 결과 응답 객체에 중복 키가 생기지 않도록 할 수 있습니다:

Example № 13{
  user(id: 4) {
    id
    name
    smallPic: profilePic(size: 64)
    bigPic: profilePic(size: 1024)
  }
}

이는 다음과 같은 결과를 반환합니다:

Example № 14{
  "user": {
    "id": 4,
    "name": "Mark Zuckerberg",
    "smallPic": "https://cdn.site.io/pic-4-64.jpg",
    "bigPic": "https://cdn.site.io/pic-4-1024.jpg"
  }
}

연산의 최상위 필드에도 별칭을 지정할 수 있습니다:

Example № 15{
  zuck: user(id: 4) {
    id
    name
  }
}

이는 다음과 같은 결과를 반환합니다:

Example № 16{
  "zuck": {
    "id": 4,
    "name": "Mark Zuckerberg"
  }
}

2.9프래그먼트

프래그먼트는 GraphQL에서 주요한 구성 단위입니다.

프래그먼트는 문서 내에서 반복되는 공통 필드 선택을 재사용할 수 있게 하여 중복된 텍스트를 줄입니다. 인터페이스나 유니온을 질의할 때 타입 조건에 따라 조건부로 필드를 포함하려면 인라인 프래그먼트를 선택 내에서 직접 사용할 수 있습니다.

예를 들어, 어떤 사용자의 친구 및 상호 친구에 대한 공통 정보를 가져오려면:

Example № 17query noFragments {
  user(id: 4) {
    friends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
    mutualFriends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
  }
}

반복되는 필드를 프래그먼트로 추출하여 상위 프래그먼트나 연산에서 조합할 수 있습니다.

Example № 18query withFragments {
  user(id: 4) {
    friends(first: 10) {
      ...friendFields
    }
    mutualFriends(first: 10) {
      ...friendFields
    }
  }
}

"Common fields for a user's friends."
fragment friendFields on User {
  id
  name
  profilePic(size: 50)
}

프래그먼트는 전개 연산자(...)를 사용하여 소비됩니다. 프래그먼트에서 선택된 모든 필드는 프래그먼트 호출과 같은 수준의 필드 선택에 추가됩니다. 이는 여러 수준의 프래그먼트 전개를 통해 발생합니다.

예를 들면:

Example № 19query withNestedFragments {
  user(id: 4) {
    friends(first: 10) {
      ...friendFields
    }
    mutualFriends(first: 10) {
      ...friendFields
    }
  }
}

fragment friendFields on User {
  id
  name
  ...standardProfilePic
}

fragment standardProfilePic on User {
  profilePic(size: 50)
}

연산 noFragments, withFragments, 그리고 withNestedFragments는 모두 동일한 응답 객체를 생성합니다.

2.9.1타입 조건

프래그먼트는 적용될 타입을 명시해야 합니다. 예에서 friendFieldsUser를 질의하는 컨텍스트에서 사용할 수 있습니다.

프래그먼트는 입력 값(스칼라, 열거형, 입력 객체)에는 지정할 수 없습니다.

프래그먼트는 객체 타입, 인터페이스, 유니온에 지정할 수 있습니다.

프래그먼트 내부의 선택은 해당 프래그먼트가 작동하는 객체의 구체 타입이 프래그먼트의 타입과 일치할 때만 값을 반환합니다.

예를 들어 Facebook 데이터 모델을 사용한 이 연산에서:

Example № 20query FragmentTyping {
  profiles(handles: ["zuck", "coca-cola"]) {
    handle
    ...userFragment
    ...pageFragment
  }
}

fragment userFragment on User {
  friends {
    count
  }
}

fragment pageFragment on Page {
  likers {
    count
  }
}

profiles 루트 필드는 각 요소가 Page 또는 User일 수 있는 목록을 반환합니다. 결과의 객체가 User일 때는 friends가 있고 likers는 없습니다. 반대로 결과가 Page일 때는 likers가 있고 friends는 없습니다.

Example № 21{
  "profiles": [
    {
      "handle": "zuck",
      "friends": { "count": 1234 }
    },
    {
      "handle": "coca-cola",
      "likers": { "count": 90234512 }
    }
  ]
}

2.9.2인라인 프래그먼트

프래그먼트는 selection set 내에서 인라인으로 정의될 수도 있습니다. 이는 타입 조건에 따라 필드를 조건부로 포함하거나 선택 집합에 지시문을 적용하는 데 유용합니다.

이 표준 프래그먼트 포함 기능은 위의 query FragmentTyping 예제에서 보여주었습니다. 동일한 동작을 인라인 프래그먼트를 사용해도 달성할 수 있습니다.

Example № 22query inlineFragmentTyping {
  profiles(handles: ["zuck", "coca-cola"]) {
    handle
    ... on User {
      friends {
        count
      }
    }
    ... on Page {
      likers {
        count
      }
    }
  }
}

인라인 프래그먼트는 필드 그룹에 지시문을 적용하는 데에도 사용할 수 있습니다. TypeCondition이 생략되면 해당 인라인 프래그먼트는 둘러싼 컨텍스트와 동일한 타입으로 간주됩니다.

Example № 23query inlineFragmentNoType($expandedInfo: Boolean) {
  user(handle: "zuck") {
    id
    name
    ... @include(if: $expandedInfo) {
      firstName
      lastName
      birthday
    }
  }
}

2.10입력 값

필드 및 지시문 인수는 다양한 리터럴 원시값을 입력으로 받습니다. 입력 값은 스칼라, 열거값, 리스트 또는 입력 객체일 수 있습니다.

만약 상수로 정의되지 않았다면(예: DefaultValue에서처럼) 입력 값은 변수로 지정될 수 있습니다. 리스트와 입력 객체도(상수로 정의되지 않은 경우) 변수를 포함할 수 있습니다.

2.10.1정수 값

IntValue는 소수점이나 지수가 없이 지정되며 음수일 수 있습니다(예: -123). 선행 0을 가져서는 안 됩니다.

IntValue는 뒤에 Digit이 올 수 없습니다. 즉, IntValue 토큰은 항상 가능한 가장 긴 유효 시퀀스여야 합니다. 예를 들어 소스 문자 121 다음에 Digit 2가 오므로 두 토큰으로 해석될 수 없습니다. 또한 00도 유효하지 않은데, 이는 하나의 토큰으로도 두 개의 0 토큰으로도 해석될 수 없기 때문입니다.

IntValue는 뒤에 . 또는 NameStart이 올 수 없습니다. 만약 . 또는 ExponentIndicator가 뒤따른다면 해당 시퀀스는 FloatValue로만 해석되어야 합니다. 예를 들어 0x123이나 123L 같은 시퀀스는 유효한 어휘 표현이 없습니다.

2.10.2실수 값

Sign
+ -

FloatValue는 소수점(예: 1.0) 또는 지수(예: 1e50) 또는 둘 다(예: 6.0221413e23)를 포함하며 음수일 수 있습니다. IntValue와 마찬가지로 선행 0을 가져서는 안 됩니다.

FloatValue는 뒤에 Digit이 올 수 없습니다. 즉, FloatValue 토큰은 항상 가능한 가장 긴 유효 시퀀스여야 합니다. 예를 들어 소스 문자 1.231.2 다음에 Digit 3이 오므로 두 토큰으로 해석될 수 없습니다.

FloatValue는 뒤에 .이 올 수 없습니다. 예를 들어 1.23.4는 두 토큰(1.2, 3.4)으로 해석될 수 없습니다.

FloatValue는 뒤에 NameStart 문자가 올 수 없습니다. 예를 들어 0x1.2p3 시퀀스는 유효한 어휘 표현이 없습니다.

Note 숫자 리터럴인 IntValueFloatValue는 문자(또는 다른 NameStart)가 즉시 따라오는 것을 제한하여 혼동이나 예기치 않은 동작을 줄입니다. GraphQL은 10진수만 지원합니다.

2.10.3불리언 값

BooleanValue
true false

두 키워드 truefalse는 불리언 값 두 가지를 각각 나타냅니다.

2.10.4문자열 값

HexDigit
0 1 2 3 4 5 6 7 8 9
A B C D E F
a b c d e f
EscapedCharacter
" \ / b f n r t

StringValue는 모든 이스케이프 시퀀스를 아래에 정의된 정적 의미론에 따라 해석하여 Unicode text 값(Unicode 스칼라 값의 시퀀스)으로 평가됩니다. 어휘 토큰 사이에 무시된 공백 등은 문자열 내부에서는 의미를 가집니다.

빈 문자열 "" 다음에 또 다른 "가 오면 블록 문자열의 시작으로 해석되므로 허용되지 않습니다. 예를 들어 소스 """"""는 세 개의 빈 문자열이 아니라 하나의 빈 블록 문자열로만 해석될 수 있습니다.

Escape Sequences

단일 인용된 StringValue에서 모든 Unicode 스칼라 값은 이스케이프 시퀀스로 표현될 수 있습니다. GraphQL 문자열은 C 스타일 이스케이프(예: \n)와 두 가지 형태의 유니코드 이스케이프를 허용합니다: 고정 폭 4자리 16진수(예: \u000A)와 가변 폭(예: \u{1F4A9})으로 보조 문자(예: 이모지)를 표현하는 데 유용합니다.

유니코드 이스케이프가 인코딩하는 16진수 값은 반드시 유효한 Unicode 스칼라 값 범위를 나타내야 하며, 그렇지 않으면 파싱 오류가 발생해야 합니다. 예를 들어 "\uDEAD""\u{110000}"는 유효한 StringValue로 간주되어서는 안 됩니다.

이스케이프 시퀀스는 단일 인용 문자열 내부에서만 의미가 있습니다. 블록 문자열 내부에서는 문자 시퀀스 그대로입니다(예: """\n"""는 문자들 [U+005C, U+006E]를 나타냅니다). 주석 내부에서는 이스케이프 시퀀스가 중요한 문자가 아닙니다. GraphQL 문서의 다른 위치에서는 이스케이프 시퀀스가 나타날 수 없습니다.

일부 코드 포인트(예: 줄 끝 문자)는 StringCharacter에 직접 포함될 수 없으므로 이스케이프 시퀀스를 사용하여 표현해야 합니다. 그 외의 이스케이프 시퀀스는 선택적이며 비 ASCII 유니코드 문자는 문자열 내에서 그대로 허용됩니다. 만약 ASCII만 지원하는 시스템에서 GraphQL을 사용하는 경우 ASCII 범위를 벗어나는 유니코드 문자를 이스케이프 시퀀스로 표현할 수 있습니다.

레거시 이유로 보조 문자는 두 개의 고정 폭 유니코드 이스케이프 시퀀스로(서로게이트 페어로) 이스케이프될 수 있습니다. 예를 들어 입력 "\uD83D\uDCA9""\u{1F4A9}"와 동일한 Unicode 텍스트를 나타내는 유효한 StringValue입니다. 이 레거시 형태는 허용되지만 가변 폭 유니코드 이스케이프가 더 명확하므로 권장됩니다.

구현체는 StringValue를 생성할 때 비출력 제어문자(U+0000~U+001F 및 U+007F~U+009F)를 이스케이프 시퀀스로 표현해야 합니다. 다른 이스케이프는 필수는 아니지만(예: ASCII 전용 출력을 생성할 경우) 사용할 수 있습니다. 보조 문자를 이스케이프할 경우에는 가변 폭 유니코드 이스케이프만 사용해야 합니다.

Block Strings

블록 문자열은 삼중 따옴표(""")로 둘러싸인 문자 시퀀스입니다. 공백, 줄 끝, 따옴표 및 백슬래시 문자를 이스케이프하지 않고 그대로 사용할 수 있어 원문 그대로의 텍스트를 가능하게 합니다. 문자는 모두 유효한 SourceCharacter여야 합니다.

블록 문자열은 들여쓰기된 위치에서 자유 형식 텍스트로 자주 사용되므로, 블록 문자열의 문자열 값 의미론은 균일한 들여쓰기와 시작/끝의 빈 줄을 제외합니다(BlockStringValue() 참조).

예를 들어, 블록 문자열을 포함한 다음 연산은:

Example № 24mutation {
  sendEmail(message: """
    Hello,
      World!

    Yours,
      GraphQL.
  """)
}

다음의 표준 인용 문자열과 동일합니다:

Example № 25mutation {
  sendEmail(message: "Hello,\n  World!\n\nYours,\n  GraphQL.")
}

블록 문자열 값은 선행 및 후행의 빈 줄을 제거하므로 주어진 값에 대해 인쇄할 수 있는 단일한 표준 블록 문자열 표현이 존재하지 않습니다. 블록 문자열은 보통 자유 형식 텍스트를 나타내므로 가독성을 위해 시작과 끝에 빈 줄이 있는 것이 좋습니다.

Example № 26"""
This starts with and ends with an empty line,
which makes it easier to read.
"""
Counter Example № 27"""This does not start with or end with any empty lines,
which makes it a little harder to read."""
Note 문자열 값에 비출력 ASCII 문자가 필요하다면 블록 문자열 대신 적절한 이스케이프 시퀀스를 사용한 표준 인용 문자열을 사용해야 합니다.
Static Semantics

StringValueUnicode text 값을 기술합니다. 이는 Unicode scalar value의 시퀀스입니다.

이 의미론은 소스 텍스트에 대해 StringValue 문법을 어떻게 적용하여 Unicode text를 평가하는지 설명합니다. 이 평가 중 발생한 오류는 해당 소스에 대한 문법 적용 실패로 간주되어 파싱 오류를 발생시켜야 합니다.

StringValue
""
  1. 빈 시퀀스를 반환합니다.
StringValue
  1. 모든 StringCharacter의 평가를 연결하여 Unicode text를 반환합니다.
StringCharacter
  1. valueEscapedUnicode 내의 HexDigit 시퀀스로 표현된 16진수 값으로 둔다.
  2. value유니코드 스칼라 값 범위(0x0000 이상 0xD7FF 이하 또는 0xE000 이상 0x10FFFF 이하)에 있음을 단언한다.
  3. 유니코드 스칼라 값 value를 반환한다.
StringCharacter
  1. leadingValue를 첫 번째 HexDigit 시퀀스로 표현된 16진수 값으로 둔다.
  2. trailingValue를 두 번째 HexDigit 시퀀스로 표현된 16진수 값으로 둔다.
  3. leadingValue가 0xD800 이상 0xDBFF 이하( 선행 서퍼게이트(Leading Surrogate) )라면:
    1. trailingValue가 0xDC00 이상 0xDFFF 이하( 후행 서퍼게이트(Trailing Surrogate) )임을 단언한다.
    2. (leadingValue - 0xD800) × 0x400 + (trailingValue - 0xDC00) + 0x10000을 반환한다.
  4. 그렇지 않으면:
    1. leadingValue유니코드 스칼라 값(Unicode scalar value) 범위 내임을 단언한다.
    2. trailingValue유니코드 스칼라 값(Unicode scalar value) 범위 내임을 단언한다.
    3. 유니코드 스칼라 값 leadingValue의 시퀀스와, 그 다음에 유니코드 스칼라 값 trailingValue의 시퀀스를 반환한다.
Note 만약 두 이스케이프 시퀀스가 모두 Unicode 스칼라 값을 인코딩한다면, 이 의미론은 각 고정 폭 이스케이프에 이전 의미론을 적용한 것과 동일합니다. 가변 폭 이스케이프는 반드시 하나의 Unicode 스칼라 값만을 인코딩해야 합니다.
StringCharacter
  1. 아래 표에 따라 Unicode scalar value를 반환합니다.
Escaped Character Scalar Value Character Name
" U+0022 double quote
\ U+005C reverse solidus (back slash)
/ U+002F solidus (forward slash)
b U+0008 backspace
f U+000C form feed
n U+000A line feed (new line)
r U+000D carriage record
t U+0009 horizontal tab
StringValue
  1. BlockString를 평가하여 얻은 Unicode text를 반환합니다.
BlockString
  1. 모든 BlockStringCharacter의 평가를 연결하여 rawValue라는 Unicode text를 둡니다(이 시퀀스는 비어있을 수 있습니다).
  2. BlockStringValue(rawValue)의 결과를 반환합니다.
BlockStringCharacter
\"""
  1. 문자열 시퀀스 """를 반환합니다.
BlockStringValue(rawValue)
  1. linesrawValueLineTerminator로 분할한 결과로 둡니다.
  2. commonIndentnull로 둡니다.
  3. line에 대해 다음을 수행합니다:
    1. 만약 linelines의 첫 항목이면 다음 line으로 넘어갑니다.
    2. lengthline의 문자 수로 둡니다.
    3. indentline의 선행 연속된 Whitespace 문자 수로 둡니다.
    4. 만약 indentlength보다 작다면:
      1. 만약 commonIndentnull이거나 indentcommonIndent보다 작다면:
        1. commonIndentindent로 둡니다.
  4. 만약 commonIndentnull이 아니면:
    1. line에 대해:
      1. 만약 linelines의 첫 항목이면 다음 줄로 계속합니다.
      2. line의 시작에서 commonIndent 문자를 제거합니다.
  5. 만약 lines의 첫 항목 line이 오직 Whitespace만 포함하면:
    1. 그 첫 항목을 lines에서 제거합니다.
  6. 만약 lines의 마지막 항목 line이 오직 Whitespace만 포함하면:
    1. 그 마지막 항목을 lines에서 제거합니다.
  7. formatted를 빈 문자 시퀀스로 둡니다.
  8. line에 대해:
    1. 만약 linelines의 첫 항목이면:
      1. formattedline을 추가합니다.
    2. 그렇지 않다면:
      1. formatted에 줄 바꿈 문자(U+000A)를 추가합니다.
      2. formattedline을 추가합니다.
  9. formatted를 반환합니다.

2.10.5Null 값

NullValue
null

널 값은 키워드 null로 표현됩니다.

GraphQL에는 값의 부재를 표현하는 의미론상 서로 다른 두 가지 방식이 있습니다:

  • 명시적으로 리터럴 값 null을 제공하는 방법.
  • 값을 전혀 제공하지 않는(암묵적) 방법.

예를 들어, 다음 두 필드 호출은 유사하지만 동일하지 않습니다:

Example № 28{
  field(arg: null)
  field
}

첫 번째는 인수 "arg"에 대해 명시적으로 null을 제공한 것이고, 두 번째는 인수 "arg"에 대해 값을 암묵적으로 제공하지 않은 것입니다. 이 두 형태는 다르게 해석될 수 있습니다. 예를 들어 각각 필드를 삭제하는 변이와 필드를 변경하지 않는 변이를 나타낼 수 있습니다. Non-Null 타입을 기대하는 입력에 대해서는 어느 형태도 사용할 수 없습니다.

Note 값의 부재를 나타내는 동일한 두 방법은 변수에서도 가능합니다. 즉, 변수를 null로 제공하거나 변수를 전혀 제공하지 않는 방식입니다.

2.10.6열거값

EnumValue
Nametruefalsenull

열거 값은 따옴표 없는 이름으로 표현됩니다(예: MOBILE_WEB). 관례상 열거 값은 모두 대문자로 작성하는 것을 권장합니다. 열거 값은 해당 열거 타입이 정확히 알려진 문맥에서만 사용되므로, 리터럴에서 열거 타입 이름을 별도로 제공할 필요는 없습니다.

2.10.7리스트 값

ListValueConst
[]
[ValueConstlist]

리스트는 대괄호 [ ]로 감싼 순서 있는 값들의 시퀀스입니다. 리스트 리터럴의 값은 어떤 값 리터럴이나 변수도 될 수 있습니다(예: [1, 2, 3]).

GraphQL에서는 쉼표가 선택적이므로 후행 쉼표가 허용되며 반복된 쉼표는 누락된 값을 나타내지 않습니다.

Semantics
ListValue
[]
  1. 새로운 빈 리스트 값을 반환합니다.
ListValue
[Valuelist]
  1. inputList를 새로운 빈 리스트 값으로 둡니다.
  2. Valuelist에 대해:
    1. 해당 Value를 평가한 결과를 value로 둡니다.
    2. valueinputList에 추가합니다.
  3. inputList를 반환합니다.

2.10.8입력 객체 값

ObjectValueConst
{}
{ObjectFieldConstlist}
ObjectFieldConst
Name:ValueConst

입력 객체 리터럴 값은 중괄호 { }로 감싼 키가 있는 입력 값들의 순서 없는 목록입니다. 객체 리터럴의 값은 어떤 입력 값 리터럴이나 변수도 될 수 있습니다(예: { name: "Hello world", score: 1.0 }). 우리는 입력 객체의 리터럴 표현을 "object literals"라고 부릅니다.

Input Object Fields Are Unordered

입력 객체 필드는 어떤 구문적 순서로 제공되어도 동일한 의미를 유지합니다.

다음 두 연산은 의미적으로 동일합니다:

Example № 29{
  nearestThing(location: { lon: 12.43, lat: -53.211 })
}
Example № 30{
  nearestThing(location: { lat: -53.211, lon: 12.43 })
}
Semantics
ObjectValue
{}
  1. 필드가 없는 새로운 입력 객체 값을 반환합니다.
ObjectValue
  1. inputObject를 필드가 없는 새로운 입력 객체 값으로 둡니다.
  2. field에 대해:
    1. name을 그 fieldName로 둡니다.
    2. value를 그 fieldValue를 평가한 결과로 둡니다.
    3. inputObject에 이름이 name이고 값이 value인 필드를 추가합니다.
  3. inputObject를 반환합니다.

2.11변수

GraphQL 연산은 변수를 사용해 매개변수를 받을 수 있으며, 이를 통해 재사용을 극대화하고 클라이언트에서 런타임에 값들을 문자열로 조합하는 비용을 피할 수 있습니다.

만약 상수로 정의되지 않았다면(예: DefaultValue같은 경우), Variable을 입력 값으로 제공할 수 있습니다.

변수는 연산의 최상단에서 정의되어야 하며 해당 연산의 실행 범위 전체에 걸쳐 유효합니다. 이러한 변수들의 값은 요청의 일부로 GraphQL 서비스에 제공되어 실행 중에 대체됩니다.

예를 들어, 특정 기기의 크기에 따라 프로필 사진 크기를 가져오려는 경우:

Example № 31query getZuckProfile(
  "The size of the profile picture to fetch."
  $devicePicSize: Int
) {
  user(id: 4) {
    id
    name
    profilePic(size: $devicePicSize)
  }
}

변수 값들을 JSON으로 제공한다면, profilePic의 크기를 60으로 요청할 수 있습니다:

Example № 32{
  "devicePicSize": 60
}
프래그먼트 내에서의 변수 사용

변수는 프래그먼트 내부에서 사용할 수 있습니다. 변수는 주어진 연산에 대해 전역 범위를 가지므로, 프래그먼트 내에서 사용되는 변수는 그 프래그먼트를 (직접 또는 간접적으로) 포함하는 모든 최상위 연산에서 선언되어야 합니다. 만약 프래그먼트에서 변수가 참조되는데 해당 변수를 정의하지 않는 연산에서 그 프래그먼트를 포함하면, 그 연산은 유효하지 않습니다(자세한 내용은 All Variable Uses Defined 참조).

2.12타입 참조

GraphQL은 인수와 변수에 기대되는 데이터 타입을 서술합니다. 입력 타입은 다른 입력 타입의 리스트일 수 있으며, 또는 어떤 입력 타입의 Non-Null 변형일 수 있습니다.

의미론
Type
  1. nameName의 문자열 값으로 둡니다.
  2. type을 스키마에서 이름이 name인 타입으로 둡니다.
  3. type는 존재해야 합니다.
  4. type을 반환합니다.
Type
  1. itemTypeType 평가의 결과로 둡니다.
  2. typeitemType을 포함 타입으로 하는 List 타입으로 둡니다.
  3. type을 반환합니다.
Type
  1. nullableTypeType 평가의 결과로 둡니다.
  2. typenullableType을 포함 타입으로 갖는 Non-Null 타입으로 둡니다.
  3. type을 반환합니다.

2.13지시문

DirectivesConst
DirectiveConstlist
DirectiveConst
@NameArgumentsConstopt

지시문은 GraphQL 문서에서 런타임 실행 또는 타입 검증 동작을 대체하거나 확장하는 방법을 설명하는 수단을 제공합니다.

어떤 경우에는 필드 인수로는 표현할 수 없는 방식으로 GraphQL의 실행 동작을 변경해야 할 수 있습니다(예: 필드를 조건부로 포함하거나 건너뛰기). 지시문은 실행자에게 추가 정보를 제공하여 이러한 요구를 충족합니다.

지시문은 이름과, 어떤 입력 타입의 값을 받을 수 있는 인수 목록을 가집니다.

지시문은 타입, 필드, 프래그먼트 및 연산에 대해 추가 정보를 서술하는 데 사용될 수 있습니다.

앞으로 GraphQL의 향후 버전이 새로운 구성 가능한 실행 기능을 도입하면, 이는 지시문을 통해 노출될 수 있습니다. GraphQL 서비스와 도구는 여기서 설명된 것 이외의 추가적인 사용자 정의 지시문을 제공할 수도 있습니다.

지시문 순서의 중요성

지시문은 특정 구문적 순서로 제공될 수 있으며, 그 순서는 의미론적 해석을 가질 수 있습니다.

다음 두 타입 정의는 서로 다른 의미를 가질 수 있습니다:

Example № 33type Person
  @addExternalFields(source: "profiles")
  @excludeField(name: "photo") {
  name: String
}
Example № 34type Person
  @excludeField(name: "photo")
  @addExternalFields(source: "profiles") {
  name: String
}

2.14스키마 좌표

schema coordinate는 GraphQL 스키마 내의 schema element를 고유하게 식별하는 사람이 읽을 수 있는 문자열입니다. 도구들이 타입, 필드 및 기타 schema element를 참조할 때 사용하도록 의도되어 있습니다. 예로는 문서에서 타입과 필드를 참조하기 위한 링크, 로깅 도구에서 특정 필드의 본 프로덕션 조회 빈도를 추적하기 위한 조회 키 등이 있습니다.

schema element는 스키마 내에 정의된 명명된 타입, 필드, 입력 필드, 열거값, 필드 인수, 지시문 또는 지시문 인수(내장 타입과 지시문 포함)가 될 수 있습니다.

참고 메타 필드는 스키마 내에서 정의되지 않으므로 schema element가 아닙니다. 따라서 내성(introspection) 타입 또한 schema element가 아닙니다.

containing element는 한 토큰(이 경우 Name)이 적게 포함된 스키마 요소로서, 특정 schema element를 구문적으로 포함하는 요소를 말합니다. 구체적으로는 다음과 같습니다:

schema coordinate는 항상 유일합니다. 각 schema element는 정확히 한 개의 가능한 schema coordinate로만 참조될 수 있습니다.

schema coordinate는 정의된 스키마 요소나 내장된 스키마 요소를 모두 가리킬 수 있습니다. 예를 들어 String@deprecated(reason:)는 모두 내장된 스키마 요소를 가리키는 유효한 schema coordinate입니다.

참고 유니언 멤버는 스키마 내의 타입을 참조합니다. 스키마의 타입은 TypeCoordinate로 식별됩니다. 유니언 멤버를 나타내는 별도의 schema coordinate는 없으며, 이는 앞에서 설명한 schema coordinate의 유일성 속성을 보존합니다.
Parsing a Schema Coordinate

SchemaCoordinate는 자체 어휘 토큰 집합을 가진 독립적인 문법입니다. 이는 Document 내부에 포함되지 않습니다. SchemaCoordinate의 소스 텍스트는 SourceCharacter의 연속이어야 합니다.

다른 GraphQL 문서와 달리, SchemaCoordinate는 문자 시퀀스 내에 Whitespace나 다른 Ignored 문법을 포함해서는 안 됩니다. 이는 각 schema coordinate가 단일하고 모호하지 않은 어휘 형태를 갖도록 보장합니다.

Resolving a Schema Coordinate

schema element를 참조하려면, schema coordinate를 GraphQL schema의 문맥에서 해석해야 합니다.

만약 해당 schema element를 찾을 수 없다면 resolve 함수는 값을 반환하지 않습니다(오류를 발생시키지는 않습니다). 그러나 schema coordinate 내의 비-리프(non-leaf) 노드 중 어느 하나라도 schema에서 찾을 수 없을 경우에는 오류가 발생합니다.

참고 구문상으로는 메타 필드나 내성 스키마의 요소를 schema coordinate로 기술할 수 있지만(예: Business.__typename 또는 __Type.fields(includeDeprecated:)), 이들은 schema element가 아니므로 그러한 좌표를 해석하는 동작은 정의되어 있지 않습니다.
TypeCoordinate
  1. typeNameName의 값으로 둡니다.
  2. 만약 존재한다면 schema 내에서 이름이 typeName인 타입을 반환합니다.
MemberCoordinate
  1. typeName를 첫 번째 Name의 값으로 둡니다.
  2. typeschema에서 이름이 typeName인 타입으로 둡니다.
  3. 단언: type는 존재해야 하며, Enum, Input Object, Object 또는 Interface 타입이어야 합니다.
  4. 만약 type이 Enum 타입이라면:
    1. enumValueName을 두 번째 Name의 값으로 둡니다.
    2. 만약 존재한다면 type 내 이름이 enumValueName인 열거값을 반환합니다.
  5. 그렇지 않고 type이 Input Object 타입이라면:
    1. inputFieldName을 두 번째 Name의 값으로 둡니다.
    2. 만약 존재한다면 type의 입력 필드 중 이름이 inputFieldName인 필드를 반환합니다.
  6. 그 외의 경우:
    1. fieldName을 두 번째 Name의 값으로 둡니다.
    2. 만약 존재한다면 type의 필드 중 이름이 fieldName인 필드를 반환합니다.
ArgumentCoordinate
  1. typeName를 첫 번째 Name의 값으로 둡니다.
  2. typeschema에서 이름이 typeName인 타입으로 둡니다.
  3. 단언: type는 존재해야 하며 Object 또는 Interface 타입이어야 합니다.
  4. fieldName을 두 번째 Name의 값으로 둡니다.
  5. fieldtype의 필드 중 이름이 fieldName인 필드로 둡니다.
  6. 단언: field는 존재해야 합니다.
  7. fieldArgumentName을 세 번째 Name의 값으로 둡니다.
  8. 만약 존재한다면 field의 인수 중 이름이 fieldArgumentName인 인수를 반환합니다.
DirectiveCoordinate
  1. directiveNameName의 값으로 둡니다.
  2. 만약 존재한다면 schema 내에서 이름이 directiveName인 지시문을 반환합니다.
DirectiveArgumentCoordinate
  1. directiveName을 첫 번째 Name의 값으로 둡니다.
  2. directiveschema에서 이름이 directiveName인 지시문으로 둡니다.
  3. 단언: directive는 존재해야 합니다.
  4. directiveArgumentName을 두 번째 Name의 값으로 둡니다.
  5. 만약 존재한다면 directive의 인수 중 이름이 directiveArgumentName인 인수를 반환합니다.
Examples
Element Kind Schema Coordinate Schema Element
Named Type Business Business type
Field Business.name name field on the Business type
Input Field SearchCriteria.filter filter input field on the SearchCriteria input object type
Enum Value SearchFilter.OPEN_NOW OPEN_NOW value of the SearchFilter enum
Field Argument Query.searchBusiness(criteria:) criteria argument on the searchBusiness field on the Query type
Directive @private @private directive
Directive Argument @private(scope:) scope argument on the @private directive

위 표는 아래 스키마를 기준으로 각 종류의 schema coordinate 예시를 보여줍니다.

type Query {
  searchBusiness(criteria: SearchCriteria!): [Business]
}

input SearchCriteria {
  name: String
  filter: SearchFilter
}

enum SearchFilter {
  OPEN_NOW
  DELIVERS_TAKEOUT
  VEGETARIAN_MENU
}

type Business {
  id: ID
  name: String
  email: String @private(scope: "loggedIn")
}

directive @private(scope: String!) on FIELD_DEFINITION

3타입 시스템

GraphQL 타입 시스템은 GraphQL 서비스의 기능을 설명하며, 요청된 연산이 유효한지 판단하고 응답 결과의 타입을 보장하며, 변수의 입력 타입을 설명하여 요청 시 제공된 값이 유효한지 판단하는 데 사용됩니다.

GraphQL 언어에는 GraphQL 서비스의 타입 시스템을 기술하는 데 사용되는 IDL(인터페이스 설명 언어)이 포함되어 있습니다. 도구들은 이 정의 언어를 사용하여 클라이언트 코드 생성이나 서비스 부트스트래핑과 같은 유틸리티를 제공할 수 있습니다.

GraphQL 요청을 단순히 실행하기만 하고 새로운 GraphQL 스키마를 구성하지 않으려는 도구나 서비스는 TypeSystemDefinition을 허용하지 않도록 선택할 수 있습니다. 반대로 스키마만 생성하고 요청을 실행하지 않으려는 도구는 TypeSystemDocument만 허용하고 ExecutableDefinition 또는 TypeSystemExtension을 허용하지 않도록 선택할 수 있지만, 해당 요소들이 존재할 경우에는 설명적인 오류를 반환해야 합니다.

참고 이 명세서의 나머지 부분에서는 예시 타입 시스템을 설명할 때 타입 시스템 정의 언어가 사용됩니다.

3.1타입 시스템 확장

타입 시스템 확장은 이전 타입 시스템으로부터 확장된 GraphQL 타입 시스템을 나타내는 데 사용됩니다. 예를 들어 로컬 서비스가 GraphQL 클라이언트가 로컬에서만 접근하는 데이터를 나타내기 위해 사용하거나, 한 GraphQL 서비스가 다른 GraphQL 서비스를 확장하는 경우에 사용될 수 있습니다.

스키마를 생성하고 확장하는 것만 목적이며 요청을 실행하지 않는 도구는 TypeSystemExtensionDocument만 허용하고 ExecutableDefinition을 허용하지 않도록 선택할 수 있지만, 해당 요소가 존재할 경우에는 설명적인 오류를 제공해야 합니다.

3.2타입 시스템 설명

문서화는 GraphQL 타입 시스템의 중요한 기능으로, TypeSystemDocument의 정의와 나란히 작성되고 내성(introspection)을 통해 제공됩니다.

설명(Descriptions)은 GraphQL 서비스 설계자가 서비스의 기능과 일치하는 문서를 쉽게 제공할 수 있게 합니다. 설명은 모든 타입 시스템 정의에 대해 Markdown(CommonMark에 규정된 대로)으로 제공되어야 합니다.

GraphQL 스키마와 기타 모든 정의(예: 타입, 필드, 인수 등)는 자체 설명적이라고 간주되지 않는 한 Description을 제공해야 합니다.

예를 들어, 다음의 간단한 GraphQL 스키마는 잘 설명되어 있습니다:

Example № 35"""
A simple GraphQL schema which is well described.
"""
schema {
  query: Query
}

"""
Root type for all your query operations
"""
type Query {
  """
  Translates a string from a given language into a different language.
  """
  translate(
    "The original language that `text` is provided in."
    fromLanguage: Language

    "The translated language to be returned."
    toLanguage: Language

    "The text to be translated."
    text: String
  ): String
}

"""
The set of languages supported by `translate`.
"""
enum Language {
  "English"
  EN

  "French"
  FR

  "Chinese"
  CH
}

3.3스키마

GraphQL 서비스의 전체 타입 시스템 능력은 해당 서비스의 "스키마"라고 합니다. 스키마는 지원하는 타입과 지시문 및 각 연산 종류(query, mutation, subscription)에 대한 루트 연산 타입으로 정의되며, 이는 해당 연산들이 타입 시스템에서 어디에서 시작하는지를 결정합니다.

GraphQL 스키마 자체는 내부적으로 유효해야 합니다. 이 절에서는 관련된 경우 스키마 검증 절차의 규칙을 설명합니다.

GraphQL 스키마 내의 모든 타입은 고유한 이름을 가져야 합니다. 두 제공된 타입이 동일한 이름을 가질 수 없으며, 제공된 타입은 내장 타입(스칼라 및 내성 타입 포함)과 이름이 충돌해서는 안 됩니다.

GraphQL 스키마 내의 모든 지시문은 고유한 이름을 가져야 합니다.

스키마 내에 정의된 모든 타입과 지시문은 이름이 "__"(언더스코어 두 개)로 시작해서는 안 됩니다. 이는 GraphQL 내성 시스템에서 전용으로 사용됩니다.

3.3.1루트 연산 타입

스키마는 지원하는 각 연산 종류(query, mutation, subscription)에 대해 초기 루트 연산 타입을 정의합니다. 이는 해당 연산들이 타입 시스템에서 어디서 시작되는지를 결정합니다.

query 루트 연산 타입은 반드시 제공되어야 하며 Object 타입이어야 합니다.

mutation 루트 연산 타입은 선택 사항입니다; 제공되지 않으면 서비스는 변이를 지원하지 않습니다. 제공되는 경우에는 Object 타입이어야 합니다.

마찬가지로 subscription 루트 연산 타입도 선택 사항이며, 제공되지 않으면 서비스는 구독을 지원하지 않습니다. 제공되는 경우에는 Object 타입이어야 합니다.

제공되는 경우 query, mutation, subscription 루트 타입들은 모두 서로 다른 타입이어야 합니다.

query 루트 연산 타입의 필드들은 GraphQL 쿼리 연산의 최상위에서 사용 가능한 필드를 나타냅니다.

예를 들어, 다음 연산은:

Example № 36query {
  myName
}

query 루트 연산 타입에 "myName"이라는 필드가 존재할 때에만 유효합니다:

Example № 37type Query {
  myName: String
}

유사하게, 다음의 변이도 유효하려면 mutation 루트 연산 타입에 "setName"이라는 필드가 있어야 합니다.

Example № 38mutation {
  setName(name: "Zuck") {
    newName
  }
}

타입 시스템 정의 언어를 사용할 때 문서는 최대 하나의 schema 정의만 포함해야 합니다.

다음 예시는 query와 mutation 루트 연산 타입을 모두 가진 GraphQL 스키마를 정의합니다:

Example № 39schema {
  query: MyQueryRootType
  mutation: MyMutationRootType
}

type MyQueryRootType {
  someField: String
}

type MyMutationRootType {
  setSomeField(to: String): String
}
기본 루트 연산 타입 이름

기본 루트 타입 이름은 각각의 query, mutation, subscription 루트 연산 타입에 대해 각각 "Query", "Mutation", "Subscription"입니다.

타입 시스템 정의 언어는 각 루트 연산 타입이 해당하는 기본 루트 타입 이름을 사용하는 경우, 다른 어떤 타입도 기본 루트 타입 이름을 사용하지 않으며 스키마에 설명이 없는 경우 스키마 정의를 생략할 수 있습니다.

동일하게, 타입 시스템 정의 언어로 GraphQL 스키마를 표현할 때 각 루트 연산 타입이 기본 루트 타입 이름을 사용하는 경우, 다른 타입이 기본 이름을 사용하지 않으며 스키마에 설명이 없다면 스키마 정의는 생략되어야 합니다.

다음 예시는 "Query" 타입이 스키마의 query 루트 연산 타입으로 가정되기 때문에 명시적인 schema 정의가 없어도 유효한 완전한 GraphQL 스키마를 설명합니다.

Example № 40type Query {
  someField: String
}

다음 예시는 "Mutation"이라는 이름의 타입이 있더라도 mutation 루트 연산 타입이 없기 때문에 유효한 GraphQL 스키마를 설명합니다. 스키마 정의가 포함되어야 합니다. 그렇지 않으면 "Mutation" 타입이 스키마의 mutation 루트 타입으로 잘못 가정될 수 있습니다.

Example № 41schema {
  query: Query
}

type Query {
  latestVirus: Virus
}

type Virus {
  name: String
  mutations: [Mutation]
}

type Mutation {
  name: String
}

다음 예시는 설명(description)과 함께 query와 mutation 루트 연산 타입을 모두 갖는 유효한 GraphQL 스키마를 설명합니다:

Example № 42"""
Example schema
"""
schema {
  query: Query
  mutation: Mutation
}

type Query {
  someField: String
}

type Mutation {
  someMutation: String
}

3.3.2스키마 확장

SchemaExtension
extendschemaDirectivesConstopt{RootOperationTypeDefinitionlist}
extendschemaDirectivesConst{

스키마 확장은 이전 스키마에서 확장된 스키마를 나타내는 데 사용됩니다. 예를 들어 GraphQL 서비스가 기존 스키마에 추가적인 연산 타입이나 지시문을 더하는 경우에 사용될 수 있습니다.

참고 추가 연산 타입 정의가 없는 스키마 확장은 구문적 모호성을 피하기 위해 { (예: 쿼리 약식) 뒤에 올 수 없습니다. 아래의 타입 정의 및 확장에도 동일한 제한이 적용됩니다.
스키마 검증

스키마 확장은 잘못 정의될 경우 유효하지 않을 수 있습니다.

  1. 스키마는 이미 정의되어 있어야 합니다.
  2. 제공된 비-반복 지시문(non-repeatable directives)은 이전 스키마에 이미 적용되어 있어서는 안 됩니다.

3.4타입

GraphQL 스키마의 기본 단위는 타입입니다. GraphQL에는 이름이 있는 6가지 종류의 타입 정의와 2가지 래핑 타입이 있습니다.

가장 기본 타입은 Scalar입니다. 스칼라는 문자열이나 정수 같은 원시 값을 나타냅니다. 스칼라 필드의 가능한 응답이 열거 가능한 경우가 종종 있으며, 그런 경우에는 가능한 응답 공간을 지정하는 Enum 타입을 제공합니다.

스칼라와 열거형은 응답 트리의 리프를 구성하며, 중간 레벨은 필드 집합을 정의하는 Object 타입으로서 각 필드는 시스템의 다른 타입이 되어 임의의 타입 계층을 정의할 수 있습니다.

GraphQL은 두 가지 추상 타입을 지원합니다: 인터페이스와 유니언입니다.

Interface는 필드 목록을 정의합니다; 이 인터페이스를 구현하는 Object 타입과 다른 인터페이스 타입들은 이 필드들을 보장하여 구현합니다. 필드가 인터페이스 타입을 반환한다고 주장할 때, 실행 시에는 해당 인터페이스를 구현한 유효한 Object 타입이 반환됩니다.

Union은 가능한 타입 목록을 정의합니다; 인터페이스와 유사하게, 유니언이 반환된다고 하면 그 목록 중 하나의 타입이 반환됩니다.

마지막으로, GraphQL 필드 인수나 변수에 복잡한 구조를 입력으로 제공하는 것이 유용한 경우가 많습니다. Input Object 타입은 스키마가 정확히 어떤 데이터가 기대되는지 정의할 수 있게 합니다.

3.4.1래핑 타입

지금까지의 모든 타입은 nullable(널 허용)하고 단일 값이라고 가정됩니다. 예: 스칼라 문자열은 null 또는 단일 문자열을 반환합니다.

GraphQL 스키마는 필드가 다른 타입의 리스트를 나타낼 수 있음을 기술할 수 있습니다; 이를 위해 다른 타입을 래핑하는 List 타입이 제공됩니다.

유사하게, Non-Null 타입은 다른 타입을 래핑하며, 결과 값이 절대 null이 아님을 나타냅니다(그리고 실행 오류가 null 값을 발생시킬 수 없음을 의미합니다).

이 두 타입을 "래핑 타입"이라고 부르며, 비-래핑 타입은 "이름 있는 타입(named types)"이라고 합니다. 래핑 타입은 내부에 이름 있는 타입을 갖고 있으며, 타입을 계속 언랩핑하여 이름 있는 타입을 찾을 수 있습니다.

3.4.2입력 및 출력 타입

타입은 GraphQL 전반에서 인수와 변수에 입력으로 허용되는 값뿐만 아니라 필드가 출력하는 값도 설명하는 데 사용됩니다. 이 두 용도는 타입을 입력 타입(input types)출력 타입(output types)으로 분류합니다. 스칼라와 열거형 타입과 같은 일부 타입은 입력 타입과 출력 타입 모두로 사용될 수 있습니다. 다른 종류의 타입은 한쪽 용도로만 사용될 수 있습니다. 입력 객체 타입은 입력 타입으로만 사용될 수 있고, Object, Interface, Union 타입은 출력 타입으로만 사용될 수 있습니다. List와 Non-Null 타입은 래핑된 타입이 어떻게 사용될 수 있는지에 따라 입력 또는 출력 타입으로 사용될 수 있습니다.

IsInputType(type)
  1. 만약 type이 List 타입 또는 Non-Null 타입이라면:
    1. unwrappedTypetype의 언랩핑된 타입으로 둡니다.
    2. IsInputType(unwrappedType)을 반환합니다.
  2. 만약 type이 Scalar, Enum 또는 Input Object 타입이면:
    1. true를 반환합니다.
  3. false를 반환합니다.
IsOutputType(type)
  1. 만약 type이 List 타입 또는 Non-Null 타입이라면:
    1. unwrappedTypetype의 언랩핑된 타입으로 둡니다.
    2. IsOutputType(unwrappedType)을 반환합니다.
  2. 만약 type이 Scalar, Object, Interface, Union, 또는 Enum 타입이면:
    1. true를 반환합니다.
  3. false를 반환합니다.

3.4.3타입 확장

타입 확장은 이전 타입에서 확장된 GraphQL 타입을 나타내는 데 사용됩니다. 예를 들어 로컬 서비스가 GraphQL 클라이언트가 로컬에서만 접근하는 추가 필드를 나타내기 위해 사용할 수 있습니다.

3.5스칼라

스칼라 타입은 GraphQL 타입 시스템에서 원시 리프 값을 나타냅니다. GraphQL 응답은 계층적 트리 형태를 취하며, 이 트리의 리프는 일반적으로 GraphQL 스칼라 타입(또는 Enum 타입이나 null 값)이 됩니다.

GraphQL은 아래 섹션에서 완전히 정의된 여러 내장 스칼라를 제공하지만, 타입 시스템은 추가적인 의미를 도입하기 위해 사용자 정의 스칼라를 더할 수도 있습니다.

Built-in Scalars

GraphQL은 잘 정의된 기본 스칼라 집합을 명시합니다: Int, Float, String, Boolean, 및 ID. GraphQL 프레임워크는 이들 타입을 모두 지원해야 하며, 이러한 이름으로 타입을 제공하는 서비스는 이 문서에서 설명하는 동작을 따라야 합니다. 예를 들어, 서비스가 Int라는 타입을 포함시키고 그것을 64비트 정수나 국제화 정보 등 문서에서 정의한 것과 다른 용도로 사용해서는 안 됩니다.

__Schema 내성 타입에서 타입 집합을 반환할 때, 참조된 모든 내장 스칼라는 포함되어야 합니다. 만약 어떤 내장 스칼라 타입이 스키마의 어디에서도 참조되지 않으면(그 타입의 필드, 인수, 또는 입력 필드가 없음) 포함되어서는 안 됩니다.

타입 시스템 정의 언어로 GraphQL 스키마를 표현할 때는 간결함을 위해 모든 내장 스칼라를 생략해야 합니다.

Custom Scalars

GraphQL 서비스는 내장 스칼라 외에 사용자 정의 스칼라 타입을 사용할 수 있습니다. 예를 들어, 서비스는 직렬화 시 문자열로 표현되지만 RFC 4122를 준수하는 UUID라는 스칼라를 정의할 수 있습니다. UUID 타입의 필드를 질의할 때는 RFC 4122 호환 파서로 결과를 파싱할 수 있다는 것을 신뢰할 수 있습니다. 또 다른 유용한 사용자 정의 스칼라 예시는 URL로, 문자열로 직렬화되지만 서비스가 유효한 URL임을 보장합니다.

사용자 정의 스칼라를 정의할 때, GraphQL 서비스는 scalar specification URL@specifiedBy 지시문이나 specifiedByURL 내성 필드를 통해 제공해야 합니다. 이 URL은 스칼라의 데이터 형식, 직렬화 및 강제 규칙에 대한 사람이 읽을 수 있는 명세에 링크해야 합니다.

예를 들어, UUID 스칼라를 제공하는 서비스는 RFC 4122 또는 그 RFC의 합리적인 부분집합을 정의한 문서로 연결할 수 있습니다. 만약 scalar specification URL이 존재하면, 이를 인식하는 시스템과 도구는 해당 명세의 규칙을 준수해야 합니다.

Example № 43scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
scalar DateTime
  @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time")

사용자 정의 scalar specification URL은 모호성을 피하기 위해 단일하고 안정적인 형식을 제공해야 합니다. 만약 링크된 명세가 변경 중이라면, 서비스는 변경될 가능성이 있는 리소스 대신 고정된 버전으로 링크해야 합니다.

Note 일부 커뮤니티 유지 사용자 정의 스칼라 명세는 scalars.graphql.org에 호스팅되어 있습니다.

사용자 정의 scalar specification URL는 정의된 이후 변경해서는 안 됩니다. 이를 변경하면 도구가 중단되거나 링크된 명세의 내용에 대한 깨지는 변경을 초래할 수 있습니다.

내장 스칼라 타입은 이 문서에서 규정되므로 scalar specification URL을 제공해서는 안 됩니다.

Note 사용자 정의 스칼라는 그 형식을 요약하고 예시를 설명에 제공해야 합니다; 자세한 지침은 GraphQL 스칼라의 implementation guide를 참조하세요.
Result Coercion and Serialization

GraphQL 서비스는 특정 스칼라 타입의 필드를 준비할 때 그 스칼라 타입이 설명하는 계약을 준수해야 하며, 값이 강제(coerce)될 수 없거나 강제 과정에서 데이터 손실이 발생할 경우 execution error를 발생시켜야 합니다.

서비스는 내부의 다른 타입들을 기대되는 반환 타입으로 합리적으로 강제할 수 있도록 선택할 수 있습니다. 예를 들어 Int 타입의 필드를 강제할 때 불리언 true1로, 문자열 "123"을 10진수 123으로 파싱할 수 있습니다. 그러나 정보 손실이 발생할 가능성이 있는 경우에는 execution error를 발생시켜야 합니다.

이러한 강제 동작은 클라이언트에 관찰되지 않기 때문에, 강제의 정확한 규칙은 구현에 맡겨집니다. 서비스의 유일한 요구사항은 결과가 기대되는 스칼라 타입을 준수해야 한다는 점입니다.

GraphQL 스칼라는 사용 중인 직렬화 형식에 따라 직렬화됩니다. 각 스칼라 타입에 가장 적합한 직렬화 원시형이 있을 수 있으며, 서비스는 적절한 경우 해당 원시형을 생성해야 합니다.

Serialization Format을 참고하면 JSON 및 기타 형식에서 스칼라의 직렬화에 대한 자세한 정보를 볼 수 있습니다.

Input Coercion

GraphQL 서비스가 인수의 입력으로 스칼라 타입을 기대할 경우, 강제는 관찰 가능하므로 규칙이 잘 정의되어야 합니다. 입력 값이 강제 규칙에 일치하지 않으면 실행 전에 입력 값이 검증되므로 request error를 발생시켜야 합니다.

GraphQL은 정수 및 부동소수점 입력 값을 표현하는 서로 다른 상수 리터럴을 가지며, 강제 규칙은 만나는 입력 값의 종류에 따라 다르게 적용될 수 있습니다. 또한 GraphQL은 변수를 통해 매개변수화될 수 있고, 변수 값은 종종 HTTP 같은 전송에 직렬화되어 전송됩니다. 일부 일반 직렬화(예: JSON)는 정수와 부동소수점을 구분하지 않기 때문에, 분수 부분이 비어 있으면 정수 입력 값(예: 1.0)으로 해석되고, 그렇지 않으면 부동소수점 입력 값으로 해석됩니다.

아래 모든 타입에 대해, Non-Null을 제외하고는 명시적 값 null이 제공되면 입력 강제의 결과는 null입니다.

3.5.1Int

Int 스칼라 타입은 부동이 없는 부호 있는 32비트 정수 값을 나타냅니다. 응답 형식이 32비트 정수를 지원하면 해당 형식을 사용해야 합니다.

Result Coercion

Int 타입을 반환하는 필드는 32비트 정수 내부 값을 기대합니다.

GraphQL 서비스는 정보 손실 없이 합리적으로 정수가 아닌 내부 값을 정수로 강제할 수 있지만, 그렇지 못하면 execution error를 발생시켜야 합니다. 예로는 부동소수 1.0에 대해 1을 반환하거나 문자열 "123"에 대해 123을 반환하는 경우가 있습니다. 반면, 1.2 같은 부동소수는 잘라내어 1로 만드는 대신 execution error를 발생시키는 것이 더 적절합니다.

내부 정수 값이 -231보다 작거나 231 이상인 경우에는 execution error를 발생시켜야 합니다.

Input Coercion

입력 타입으로 기대될 때는 오직 정수 입력 값만 허용됩니다. 숫자 내용을 가진 문자열을 포함한 다른 모든 입력 값은 잘못된 타입을 나타내는 요청 오류를 발생시켜야 합니다. 또한 입력 정수 값이 -231보다 작거나 231 이상이면 request error를 발생시켜야 합니다.

Note 32비트를 초과하는 정수 값은 플랫폼과 전송이 모두 큰 정수를 인코딩하지 못할 수 있으므로 String 또는 사용자 정의 Scalar 타입을 사용하는 것이 바람직합니다.

3.5.2Float

Float 스칼라 타입은 IEEE 754에서 규정한 대로 배정밀도 유한 값(signed double-precision finite values)을 나타냅니다. 적절한 배정밀도 숫자 타입을 지원하는 응답 형식은 그 타입을 사용해야 합니다.

Result Coercion

Float 타입을 반환하는 필드는 배정밀도 부동소수 내부 값을 기대합니다.

GraphQL 서비스는 정보 손실 없이 합리적으로 비부동값을 Float로 강제할 수 있지만, 그렇지 못하면 execution error를 발생시켜야 합니다. 예로는 정수 1에 대해 1.0을 반환하거나 문자열 "123"에 대해 123.0을 반환하는 경우가 있습니다.

비유한 부동소수 내부 값(NaNInfinity)은 Float로 강제될 수 없으며 execution error를 발생시켜야 합니다.

Input Coercion

입력 타입으로 기대될 때는 정수 및 부동소수 입력 값 둘 다 허용됩니다. 정수 입력 값은 빈 분수 부분을 추가하여 Float로 강제됩니다(예: 정수 입력 값 11.0으로). 문자열을 포함한 다른 모든 입력 값은 잘못된 타입을 나타내는 request error를 발생시켜야 합니다. 만약 입력 값이 유한 IEEE 754로 표현할 수 없는 값을 나타내면(예: NaN, Infinity, 또는 사용 가능한 정밀도 밖의 값) request error를 발생시켜야 합니다.

3.5.3String

String 스칼라 타입은 유니코드 코드 포인트의 시퀀스로 표현되는 텍스트 데이터를 나타냅니다. String 타입은 주로 자유 형식의 사람 읽기 가능한 텍스트를 표현하는 데 사용됩니다. 내부 인코딩 방식(예: UTF-8)은 서비스 구현에 맡겨집니다. 모든 응답 직렬화 형식은 문자열 표현을 지원해야 하며(예: JSON 유니코드 문자열), 해당 표현을 사용해 이 타입을 직렬화해야 합니다.

Result Coercion

String 타입을 반환하는 필드는 유니코드 문자열 값을 기대합니다.

GraphQL 서비스는 정보 손실 없이 합리적으로 비문자 원시 값을 String으로 강제할 수 있지만, 그렇지 못하면 execution error를 발생시켜야 합니다. 예로는 불리언 true에 대해 문자열 "true"를 반환하거나 정수 1에 대해 문자열 "1"을 반환하는 경우가 있습니다.

Input Coercion

입력 타입으로 기대될 때는 유효한 유니코드 문자열 입력 값만 허용됩니다. 다른 모든 입력 값은 잘못된 타입을 나타내는 request error를 발생시켜야 합니다.

3.5.4Boolean

Boolean 스칼라 타입은 true 또는 false를 나타냅니다. 응답 형식은 내장 불리언 타입을 사용해야 하며, 그렇지 않은 경우 정수 10 표현을 사용해야 합니다.

Result Coercion

Boolean 타입을 반환하는 필드는 불리언 내부 값을 기대합니다.

GraphQL 서비스는 정보 손실 없이 합리적으로 비불리언 원시 값을 Boolean으로 강제할 수 있지만, 그렇지 못하면 execution error를 발생시켜야 합니다. 예로는 0이 아닌 숫자에 대해 true를 반환하는 경우가 있습니다.

Input Coercion

입력 타입으로 기대될 때는 오직 불리언 입력 값만 허용됩니다. 다른 모든 입력 값은 잘못된 타입을 나타내는 request error를 발생시켜야 합니다.

3.5.5ID

ID 스칼라 타입은 고유 식별자를 나타내며, 객체를 재조회하거나 캐시의 키로 자주 사용됩니다. ID 타입은 String과 동일한 방식으로 직렬화되지만 사람이 읽기 위한 용도는 아닙니다. 종종 숫자 형태일 수 있지만 항상 String으로 직렬화되어야 합니다.

Result Coercion

GraphQL은 ID 형식에 관해 중립적이며, 다양한 형식(작은 자동 증가 숫자, 128비트 랜덤 수, base64 인코딩 값, 또는 GUID 같은 문자열 형식) 간의 일관성을 위해 문자열로 직렬화합니다.

GraphQL 서비스는 기대하는 ID 형식에 따라 적절히 강제해야 합니다. 강제가 불가능할 경우 execution error를 발생시켜야 합니다.

Input Coercion

입력 타입으로 기대될 때는 문자열(예: "4") 또는 정수(예: 4 또는 -4) 입력 값을 서비스가 기대하는 ID 형식에 맞게 ID로 강제해야 합니다. 다른 모든 입력 값, 예를 들어 부동소수 입력 값(예: 4.0)은 잘못된 타입을 나타내는 request error를 발생시켜야 합니다.

3.5.6Scalar 확장

스칼라 타입 확장은 이전에 정의된 스칼라 타입으로부터 확장된 스칼라 타입을 나타내는 데 사용됩니다. 예를 들어, 기존 스칼라에 지시문을 추가하는 GraphQL 도구나 서비스에서 사용할 수 있습니다.

Type Validation

스칼라 타입 확장은 잘못 정의될 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 스칼라 타입이어야 합니다.
  2. 제공된 비-반복 지시문(non-repeatable directives)은 이전 스칼라 타입에 이미 적용되어 있어서는 안 됩니다.

3.6객체

GraphQL 연산은 계층적이고 구성적이며 정보의 트리를 기술합니다. 스칼라 타입은 이러한 계층적 연산의 리프 값을 설명하는 반면, 객체는 중간 수준을 설명합니다.

GraphQL 객체는 이름이 지정된 필드들의 목록을 나타내며, 각 필드는 특정 타입의 값을 반환합니다. 객체 값은 선택된 필드 이름(또는 별칭)을 키로 하고 필드 평가 결과를 값으로 하는 순서 있는 맵으로 직렬화되어야 하며, 이는 선택 집합에 나타난 순서에 따라 정렬되어야 합니다.

객체 타입 내에 정의된 모든 필드는 GraphQL 내성 시스템에서 전용으로 사용하는 "__" (언더스코어 두 개)로 시작하는 이름을 가져서는 안 됩니다.

예를 들어, 타입 Person은 다음과 같이 설명될 수 있습니다:

Example № 44type Person {
  name: String
  age: Int
  picture: Url
}

name 필드는 String 값을 반환하고, age 필드는 Int 값을 반환하며, picture 필드는 Url 값을 반환합니다.

객체 값을 질의할 때는 적어도 하나의 필드를 선택해야 합니다. 이렇게 선택된 필드들은 질의된 객체의 정확한 부분 집합을 포함하는 순서 있는 맵을 생성하며, 이 맵은 질의에서 요청된 순서대로 표현되어야 합니다. 해당 객체 타입에 선언된 필드만 질의할 수 있습니다.

예를 들어, Person의 모든 필드를 선택하면:

Example № 45{
  name
  age
  picture
}

다음과 같은 객체를 반환합니다:

Example № 46{
  "name": "Mark Zuckerberg",
  "age": 30,
  "picture": "http://some.cdn/picture.jpg"
}

필드의 부분 집합을 선택하면:

Example № 47{
  age
  name
}

정확히 해당 부분 집합만을 반환해야 합니다:

Example № 48{
  "age": 30,
  "name": "Mark Zuckerberg"
}

객체 타입의 필드는 스칼라, 열거형, 다른 객체 타입, 인터페이스 또는 유니언이 될 수 있습니다. 또한 이러한 다섯 가지 중 하나를 기본 타입으로 갖는 래핑 타입일 수도 있습니다.

예를 들어, Person 타입은 relationship을 포함할 수 있습니다:

Example № 49type Person {
  name: String
  age: Int
  picture: Url
  relationship: Person
}

반환 타입이 객체 타입인 모든 필드에 대해 선택 집합이 제공되어야 하므로, 다음 연산은 유효하지 않습니다:

Counter Example № 50{
  name
  relationship
}

다음 예제는 유효합니다:

Example № 51{
  name
  relationship {
    name
  }
}

그리고 각 객체 타입에서 질의된 부분 집합을 반환합니다:

Example № 52{
  "name": "Mark Zuckerberg",
  "relationship": {
    "name": "Priscilla Chan"
  }
}
필드 정렬

객체를 질의할 때, 결과 필드 매핑은 실행 중에 만난 순서대로 개념적으로 정렬됩니다. 단, 해당 타입에 적용되지 않는 프래그먼트와 @skip 또는 @include 지시문으로 건너뛴 필드나 프래그먼트는 제외됩니다. 이 정렬은 CollectFields() 알고리즘을 사용할 때 올바르게 생성됩니다.

순서를 표현할 수 있는 응답 직렬화 형식은 이 정렬을 유지해야 합니다. unordered map만 표현할 수 있는 형식(예: JSON)이라도 텍스트상으로는 이 순서를 보존해야 합니다. 예를 들어 두 필드 {foo, bar}가 그 순서대로 질의되었다면, 결과 JSON 직렬화는 같은 순서로 {"foo": "...", "bar": "..."}를 포함해야 합니다.

요청에 나타난 필드 순서와 동일한 순서로 응답을 생성하면 디버깅 시 사람이 읽기 쉽고, 응답의 속성 순서를 예측할 수 있을 때 더 효율적인 파싱이 가능합니다.

프래그먼트가 다른 필드보다 먼저 펼쳐지면, 그 프래그먼트가 지정하는 필드들은 뒤따르는 필드들보다 먼저 응답에 나타납니다.

Example № 53{
  foo
  ...Frag
  qux
}

fragment Frag on Query {
  bar
  baz
}

다음과 같은 순서화된 결과를 생성합니다:

Example № 54{
  "foo": 1,
  "bar": 2,
  "baz": 3,
  "qux": 4
}

선택에서 동일한 필드가 여러 번 질의되면, 첫 번째로 만난 시점을 기준으로 정렬됩니다. 단, 해당 타입에 적용되지 않는 프래그먼트는 정렬에 영향을 주지 않습니다.

Example № 55{
  foo
  ...Ignored
  ...Matching
  bar
}

fragment Ignored on UnknownType {
  qux
  baz
}

fragment Matching on Query {
  bar
  qux
  foo
}

다음과 같은 순서화된 결과를 생성합니다:

Example № 56{
  "foo": 1,
  "bar": 2,
  "qux": 3
}

또한, 지시문으로 인해 필드가 제외되면 해당 필드는 정렬에 고려되지 않습니다.

Example № 57{
  foo @skip(if: true)
  bar
  foo
}

다음과 같은 순서화된 결과를 생성합니다:

Example № 58{
  "bar": 1,
  "foo": 2
}
결과 강제화

객체를 강제하는 결과를 결정하는 것은 GraphQL 실행자의 핵심이며, 자세한 내용은 값 완성(Value Completion)을 참조하십시오.

입력 강제화

객체는 입력으로는 절대 유효하지 않습니다.

타입 검증

객체 타입은 잘못 정의된 경우 유효하지 않을 수 있습니다. 다음 규칙 집합은 GraphQL 스키마의 모든 객체 타입이 따라야 합니다.

  1. 객체 타입은 하나 이상의 필드를 정의해야 합니다.
  2. 객체 타입의 각 필드에 대해:
    1. 해당 객체 타입 내에서 필드 이름은 고유해야 하며, 두 필드가 같은 이름을 가질 수 없습니다.
    2. 필드 이름은 "__" (언더스코어 두 개)로 시작해서는 안 됩니다.
    3. 필드가 반환하는 타입은 IsOutputType(fieldType)true를 반환하는 타입이어야 합니다.
    4. 해당 필드의 각 인수에 대해:
      1. 인수 이름은 "__"로 시작해서는 안 됩니다.
      2. 해당 필드 내에서 인수 이름은 고유해야 하며, 두 인수가 같은 이름을 가질 수 없습니다.
      3. 인수의 타입은 IsInputType(argumentType)true를 반환하는 입력 타입이어야 합니다.
      4. 인수 타입이 Non-Null이고 기본 값이 정의되어 있지 않다면:
        1. @deprecated 지시문은 이 인수에 적용되어서는 안 됩니다.
      5. 인수가 기본 값을 가지면, 그 기본 값은 해당 타입에 대한 강제 규칙에 따라 argumentType과 호환되어야 합니다.
  3. 객체 타입은 하나 이상의 고유한 인터페이스를 구현한다고 선언할 수 있습니다.
  4. 객체 타입은 구현하는 모든 인터페이스의 상위 집합이어야 합니다:
    1. 이 객체 타입을 objectType이라 하겠습니다.
    2. 각각의 선언된 인터페이스 interfaceType에 대해, IsValidImplementation(objectType, interfaceType)true여야 합니다.
IsValidImplementation(type, implementedType)
  1. 만약 implementedType가 어떤 인터페이스들을 구현한다고 선언하면, type도 그 인터페이스들을 구현한다고 선언해야 합니다.
  2. typeimplementedType에 정의된 모든 필드 이름을 포함해야 합니다.
    1. 그 이름을 가진 필드를 field라 합시다.
    2. 해당 이름을 가진 구현 필드를 implementedField라 합시다.
    3. fieldimplementedField에 정의된 모든 인수와 같은 이름을 가진 인수를 포함해야 합니다.
      1. 그 이름을 가진 인수는 implementedField의 동일한 이름 인수와 동일한 타입(불변)이어야 합니다.
    4. fieldimplementedField에 정의되지 않은 추가 인수를 포함할 수 있으나, 그 추가 인수는 필수여서는 안 됩니다(예: Non-Null 타입이면 안 됩니다).
    5. fieldimplementedField의 반환 타입과 같거나 그 서브타입(공변)이어야 합니다:
      1. fieldTypefield의 반환 타입이라 합시다.
      2. implementedFieldTypeimplementedField의 반환 타입이라 합시다.
      3. IsValidImplementationFieldType(fieldType, implementedFieldType)true여야 합니다.
    6. 만약 field가 deprecated 되어 있다면 implementedField도 deprecated 되어야 합니다.
IsValidImplementationFieldType(fieldType, implementedFieldType)
  1. 만약 fieldType이 Non-Null 타입이면:
    1. nullableTypefieldType의 언랩핑된 nullable 타입으로 둡니다.
    2. implementedNullableTypeimplementedFieldType의 언랩핑된 nullable 타입으로 둡니다(만약 그것이 Non-Null 타입이면), 그렇지 않으면 implementedFieldType 자체로 둡니다.
    3. 그리고 IsValidImplementationFieldType(nullableType, implementedNullableType)을 반환합니다.
  2. 만약 fieldType이 List 타입이고 implementedFieldType도 List 타입이면:
    1. itemTypefieldType의 언랩핑된 아이템 타입으로 둡니다.
    2. implementedItemTypeimplementedFieldType의 언랩핑된 아이템 타입으로 둡니다.
    3. 그리고 IsValidImplementationFieldType(itemType, implementedItemType)을 반환합니다.
  3. 그렇지 않으면 IsSubType(fieldType, implementedFieldType)을 반환합니다.
IsSubType(possibleSubType, superType)
  1. 만약 possibleSubTypesuperType와 동일한 타입이면 true를 반환합니다.
  2. 만약 possibleSubType이 객체 타입이고 superType이 유니언 타입이며 possibleSubTypesuperType의 가능한 타입이라면 true를 반환합니다.
  3. 만약 possibleSubType이 객체 또는 인터페이스 타입이고 superType이 인터페이스 타입이며 possibleSubTypesuperType를 구현한다고 선언하면 true를 반환합니다.
  4. 그렇지 않으면 false를 반환합니다.

3.6.1필드 인수

객체 필드는 값을 생성하는 개념상의 함수입니다. 때때로 객체 필드는 반환 값을 더 구체화하기 위해 인수를 받을 수 있습니다. 객체 필드 인수는 가능한 모든 인수 이름과 기대되는 입력 타입의 목록으로 정의됩니다.

필드 내에 정의된 모든 인수의 이름은 GraphQL 내성 시스템에서 전용으로 사용하는 "__" (언더스코어 두 개)로 시작해서는 안 됩니다.

예를 들어, Person 타입의 picture 필드는 반환할 이미지의 크기를 결정하는 인수를 받을 수 있습니다.

Example № 59type Person {
  name: String
  picture(size: Int): Url
}

연산은 선택적으로 필드에 인수를 지정하여 이러한 인수를 제공할 수 있습니다.

다음 연산 예제:

Example № 60{
  name
  picture(size: 600)
}

다음과 같은 결과를 반환할 수 있습니다:

Example № 61{
  "name": "Mark Zuckerberg",
  "picture": "http://some.cdn/picture_600.jpg"
}

객체 필드 인수의 타입은 입력 타입이어야 합니다(객체, 인터페이스 또는 유니언 타입을 제외한 모든 타입).

3.6.2필드 폐기(Deprecation)

응용 프로그램 필요에 따라 객체의 필드를 폐기(deprecated)로 표시할 수 있습니다. 변경으로 인해 기존 클라이언트가 깨지지 않도록 이러한 필드를 선택 집합에 포함시키는 것은 여전히 합법적이지만, 문서 및 도구에서는 적절히 다루어져야 합니다.

타입 시스템 정의 언어를 사용할 때는 @deprecated 지시문으로 필드가 폐기되었음을 나타냅니다:

Example № 62type ExampleType {
  oldField: String @deprecated
}

3.6.3객체 확장

객체 타입 확장은 이전에 정의된 타입에서 확장된 타입을 나타내는 데 사용됩니다. 예를 들어 로컬 데이터를 표현하거나 다른 GraphQL 서비스를 확장한 서비스에서 사용할 수 있습니다.

다음 예제는 Story 타입에 로컬 데이터 필드를 추가하는 예입니다:

Example № 63extend type Story {
  isHiddenLocally: Boolean
}

객체 타입 확장은 추가 필드를 더하지 않고 인터페이스나 지시문만 추가할 수도 있습니다.

다음 예제는 필드를 추가하지 않고 User 타입에 지시문을 추가한 예입니다:

Example № 64extend type User @addedDirective
타입 검증

객체 타입 확장은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 객체 타입이어야 합니다.
  2. 객체 타입 확장의 필드 이름은 고유해야 하며, 두 필드가 같은 이름을 가져서는 안 됩니다.
  3. 객체 타입 확장의 필드는 이전 객체 타입에 이미 정의되어 있어서는 안 됩니다.
  4. 제공된 비-반복 지시문(non-repeatable directives)은 이전 객체 타입에 이미 적용되어 있어서는 안 됩니다.
  5. 제공된 인터페이스는 이전 객체 타입이 이미 구현하고 있어서는 안 됩니다.
  6. 결과적으로 확장된 객체 타입은 구현하는 모든 인터페이스의 상위 집합이어야 합니다.

3.7인터페이스

GraphQL 인터페이스는 이름이 지정된 필드들과 그 필드들의 인수 목록을 나타냅니다. GraphQL 객체와 인터페이스는 이러한 인터페이스들을 구현할 수 있으며, 구현하는 타입은 해당 인터페이스들에 정의된 모든 필드를 정의해야 합니다.

GraphQL 인터페이스의 필드는 GraphQL 객체의 필드와 동일한 규칙을 따릅니다. 그들의 타입은 Scalar, Object, Enum, Interface, 또는 Union이 될 수 있으며, 또는 해당 다섯 가지 중 하나를 기본 타입으로 가지는 어떤 래핑 타입일 수 있습니다.

예를 들어, NamedEntity 인터페이스는 필요한 필드를 설명할 수 있으며, Person 또는 Business와 같은 타입은 이 인터페이스를 구현하여 해당 필드가 항상 존재함을 보장할 수 있습니다.

타입은 또한 여러 인터페이스를 구현할 수 있습니다. 예를 들어 아래 예제에서 BusinessNamedEntityValuedEntity 인터페이스를 모두 구현합니다.

Example № 65interface NamedEntity {
  name: String
}

interface ValuedEntity {
  value: Int
}

type Person implements NamedEntity {
  name: String
  age: Int
}

type Business implements NamedEntity & ValuedEntity {
  name: String
  value: Int
  employeeCount: Int
}

인터페이스를 반환하는 필드는 여러 객체 타입 중 하나가 예상될 때 유용하며, 그 중 일부 필드는 보장되어야 할 때 사용됩니다.

예를 계속하면, ContactNamedEntity를 참조할 수 있습니다.

Example № 66type Contact {
  entity: NamedEntity
  phoneNumber: String
  address: String
}

이로써 선택 집합을 작성하여 Contact의 공통 필드를 선택할 수 있습니다.

Example № 67{
  entity {
    name
  }
  phoneNumber
}

인터페이스 타입에서 필드를 선택할 때에는 해당 인터페이스에 선언된 필드만 질의할 수 있습니다. 위 예제에서 entityNamedEntity를 반환하고 nameNamedEntity에 정의되어 있으므로 유효합니다. 그러나 다음 예제는 Contact에 대해 유효한 선택 집합이 아닙니다:

Counter Example № 68{
  entity {
    name
    age
  }
  phoneNumber
}

이는 entityNamedEntity를 참조하고 있고 age는 해당 인터페이스에 정의되어 있지 않기 때문입니다. age를 질의하려면 entity의 결과가 Person일 때만 유효하며, 이는 프래그먼트나 인라인 프래그먼트를 사용하여 표현할 수 있습니다:

Example № 69{
  entity {
    name
    ... on Person {
      age
    }
  }
  phoneNumber
}
인터페이스가 인터페이스를 구현함

한 인터페이스가 다른 인터페이스를 구현한다고 정의할 때, 구현하는 인터페이스는 구현되는 인터페이스에 의해 지정된 각 필드를 정의해야 합니다. 예를 들어, Resource 인터페이스가 Node 인터페이스를 구현하려면 id 필드를 정의해야 합니다:

Example № 70interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

전이적으로 구현된 인터페이스(구현되는 인터페이스가 다시 구현하는 인터페이스)들도 구현 타입 또는 인터페이스에 정의되어 있어야 합니다. 예를 들어, ImageResource를 구현하려면 또한 Node도 구현해야 합니다:

Example № 71interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

interface Image implements Resource & Node {
  id: ID!
  url: String
  thumbnail: String
}

인터페이스 정의는 순환 참조를 포함하거나 자기 자신을 구현해서는 안 됩니다. 다음 예제는 NodeNamed가 서로 자신을 구현하고 있어 잘못된 예입니다:

Counter Example № 72interface Node implements Named & Node {
  id: ID!
  name: String
}

interface Named implements Node & Named {
  id: ID!
  name: String
}
결과 강제화

인터페이스 타입은 특정 결과가 어떤 객체에 해당하는지 판별할 방법을 가져야 합니다. 일단 객체를 판별하면, 인터페이스에 대한 결과 강제화는 해당 객체에 대한 결과 강제화와 동일합니다.

입력 강제화

인터페이스는 입력으로는 절대 유효하지 않습니다.

타입 검증

인터페이스 타입은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 인터페이스 타입은 하나 이상의 필드를 정의해야 합니다.
  2. 인터페이스 타입의 각 필드에 대해:
    1. 그 필드는 해당 인터페이스 타입 내에서 고유한 이름을 가져야 하며, 두 필드가 같은 이름을 가질 수 없습니다.
    2. 그 필드의 이름은 "__"(언더스코어 두 개)로 시작해서는 안 됩니다.
    3. 그 필드가 반환하는 타입은 IsOutputType(fieldType)true를 반환하는 타입이어야 합니다.
    4. 그 필드의 각 인수에 대해:
      1. 인수의 이름은 "__"로 시작해서는 안 됩니다.
      2. 그 인수는 해당 필드 내에서 고유한 이름을 가져야 하며, 두 인수가 같은 이름을 가질 수 없습니다.
      3. 그 인수는 IsInputType(argumentType)true를 반환하는 입력 타입을 받아야 합니다.
  3. 인터페이스 타입은 하나 이상의 고유한 인터페이스를 선언적으로 구현할 수 있으나, 자기 자신을 구현해서는 안 됩니다.
  4. 인터페이스 타입은 구현하는 모든 인터페이스의 상위 집합이어야 합니다:
    1. 이 인터페이스 타입을 implementingType라 합시다.
    2. 각각 선언된 구현 인터페이스 implementedType에 대해, IsValidImplementation(implementingType, implementedType)true여야 합니다.

3.7.1인터페이스 확장

인터페이스 타입 확장은 이전에 정의된 인터페이스에서 확장된 인터페이스를 나타내는 데 사용됩니다. 예를 들어, 많은 타입에 공통되는 로컬 데이터를 나타내거나 다른 GraphQL 서비스를 확장한 서비스에서 사용될 수 있습니다.

다음 예제에서는 확장된 데이터 필드가 NamedEntity 타입에 추가되고 이를 구현하는 타입들에도 추가되는 모습을 보여줍니다:

Example № 73extend interface NamedEntity {
  nickname: String
}

extend type Person {
  nickname: String
}

extend type Business {
  nickname: String
}

인터페이스 타입 확장은 필드를 추가하지 않고 지시문만 추가할 수도 있습니다.

예를 들어, 다음은 필드를 추가하지 않고 NamedEntity 타입에 지시문을 추가하는 예입니다:

Example № 74extend interface NamedEntity @addedDirective
타입 검증

인터페이스 타입 확장은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 인터페이스 타입이어야 합니다.
  2. 인터페이스 타입 확장의 필드 이름은 고유해야 하며, 두 필드가 같은 이름을 가져서는 안 됩니다.
  3. 인터페이스 타입 확장의 필드는 이전 인터페이스 타입에 이미 정의되어 있어서는 안 됩니다.
  4. 이전 인터페이스 타입을 구현했던 모든 객체나 인터페이스 타입은 인터페이스 타입 확장의 필드들을 포함하는 상위 집합이어야 합니다(이는 객체 타입 확장으로 인해 발생할 수 있습니다).
  5. 제공된 비-반복 지시문(non-repeatable directives)은 이전 인터페이스 타입에 이미 적용되어 있어서는 안 됩니다.
  6. 결과적으로 확장된 인터페이스 타입은 구현하는 모든 인터페이스의 상위 집합이어야 합니다.

3.8유니언

GraphQL 유니언은 여러 GraphQL 객체 타입 중 하나일 수 있는 객체를 나타내지만, 그 타입들 사이에 보장된 필드는 제공하지 않습니다. 또한 인터페이스와는 달리 객체 타입들은 자신이 어떤 인터페이스를 구현하는지 선언하지만, 어떤 유니언에 포함되는지는 알지 못합니다.

인터페이스와 객체의 경우 해당 타입에 정의된 필드만 직접 질의할 수 있으며, 인터페이스에 정의되지 않은 다른 필드를 질의하려면 타입을 명시한 프래그먼트를 사용해야 합니다. 유니언도 마찬가지이지만, 유니언은 어떤 필드도 정의하지 않기 때문에 타입을 좁히는 프래그먼트나 인라인 프래그먼트 없이는 이 타입에서 어떤 필드도 질의할 수 없습니다(메타 필드인 __typename는 예외입니다).

예를 들어 다음과 같은 타입들을 정의할 수 있습니다:

Example № 75union SearchResult = Photo | Person

type Person {
  name: String
  age: Int
}

type Photo {
  height: Int
  width: Int
}

type SearchQuery {
  firstSearchResult: SearchResult
}

이 예제에서 쿼리는 결과가 Person일 때는 name을, Photo일 때는 height를 원합니다. 그러나 유니언 자체는 필드를 정의하지 않으므로, 이를 명시하지 않으면 모호해져 유효하지 않습니다.

Counter Example № 76{
  firstSearchResult {
    name
    height
  }
}

유효한 연산은 타입별 프래그먼트(예: 인라인 프래그먼트)를 포함해야 합니다:

Example № 77{
  firstSearchResult {
    ... on Person {
      name
    }
    ... on Photo {
      height
    }
  }
}

유니언 멤버는 더 긴 타입 목록을 표현할 때 포맷을 돕기 위해 선택적으로 앞에 | 문자를 사용하여 정의할 수 있습니다:

Example № 78union SearchResult =
  | Photo
  | Person
결과 강제화

유니언 타입은 주어진 결과가 어떤 객체에 해당하는지 판별하는 방법을 가져야 합니다. 일단 판별하면, 유니언의 결과 강제화는 해당 객체의 결과 강제화와 동일합니다.

입력 강제화

유니언은 입력으로는 절대 유효하지 않습니다.

타입 검증

유니언 타입은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 유니언 타입은 하나 이상의 고유한 멤버 타입을 포함해야 합니다.
  2. 유니언의 멤버 타입은 모두 객체 기본 타입(Object base types)이어야 합니다; 스칼라, 인터페이스 및 다른 유니언 타입은 유니언의 멤버가 될 수 없습니다. 마찬가지로 래핑 타입도 유니언의 멤버가 될 수 없습니다.

3.8.1유니언 확장

유니언 타입 확장은 이전에 정의된 유니언 타입에서 확장된 유니언을 나타내는 데 사용됩니다. 예를 들어 로컬 데이터를 추가하거나 다른 GraphQL 서비스를 확장한 서비스에서 사용할 수 있습니다.

타입 검증

유니언 타입 확장은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 유니언 타입이어야 합니다.
  2. 유니언 타입 확장의 멤버 타입은 모두 객체 기본 타입이어야 합니다; 스칼라, 인터페이스 및 유니언 타입은 멤버가 될 수 없습니다. 마찬가지로 래핑 타입도 멤버가 될 수 없습니다.
  3. 유니언 타입 확장의 모든 멤버 타입은 고유해야 합니다.
  4. 유니언 타입 확장의 멤버 타입들은 이전 유니언 타입의 멤버가 아니어야 합니다.
  5. 제공된 비-반복 지시문(non-repeatable directives)은 이전 유니언 타입에 이미 적용되어 있어서는 안 됩니다.

3.9열거형

GraphQL 열거형(Enum) 타입은 스칼라와 마찬가지로 GraphQL 타입 시스템에서 리프 값을 나타냅니다. 그러나 열거형은 가능한 값들의 집합을 설명합니다.

열거형은 숫자 값을 참조하는 것이 아니라 고유한 값들이며, 이름으로 직렬화되는 문자열로 표현될 수 있습니다.

예를 들어 Direction이라는 열거형을 정의할 수 있습니다:

Example № 79enum Direction {
  NORTH
  EAST
  SOUTH
  WEST
}
결과 강제화

GraphQL 서비스는 정의된 가능한 값들 중 하나를 반환해야 합니다. 합리적인 강제가 불가능한 경우에는 execution error를 발생시켜야 합니다.

입력 강제화

GraphQL은 열거형 입력 값을 표현하는 상수 리터럴을 제공합니다. GraphQL 문자열 리터럴은 열거형 입력으로 허용되어서는 안 되며, 대신 요청 오류를 발생시켜야 합니다.

EDN과 같이 비문자 상징 값을 별도로 표현하는 직렬화(예: EDN)는 그런 값들만 열거형 입력으로 허용해야 합니다. 그렇지 않은 대부분의 전송 직렬화에서는 문자열을 동일한 이름을 가진 열거형 입력 값으로 해석할 수 있습니다.

타입 검증

열거형 타입은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 열거형은 하나 이상의 고유한 열거값을 정의해야 합니다.

3.9.1열거형 확장

열거형 확장은 이전에 정의된 열거형을 확장하는 데 사용됩니다. 예를 들어 로컬 데이터를 추가하거나 다른 GraphQL 서비스를 확장한 서비스에서 사용할 수 있습니다.

타입 검증

열거형 확장은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 열거형(Enum) 타입이어야 합니다.
  2. 열거형 확장의 모든 값은 고유해야 합니다.
  3. 열거형 확장의 값들은 이전 열거형의 값이어서는 안 됩니다.
  4. 제공된 비-반복 지시문(non-repeatable directives)은 이전 열거형 타입에 이미 적용되어 있어서는 안 됩니다.

3.10입력 객체

필드는 동작을 구성하기 위해 인수를 받을 수 있습니다. 이러한 입력은 보통 스칼라나 열거형이지만 때로는 더 복잡한 값을 나타내어야 할 때가 있습니다.

GraphQL의 Input Object는 입력 필드들의 집합을 정의합니다. 입력 필드는 스칼라, 열거형, 다른 입력 객체 또는 그 기본(named) 타입이 위의 세 가지 중 하나인 래핑 타입일 수 있습니다. 이를 통해 인수가 임의로 복잡한 구조체를 받을 수 있게 됩니다.

예를 들어, Point2D라는 입력 객체는 xy 입력을 설명합니다:

Example № 80input Point2D {
  x: Float
  y: Float
}
참고 위에서 정의된 GraphQL Object 타입(ObjectTypeDefinition)은 여기에서 재사용하기에 부적절합니다. 객체 타입은 인수를 정의하는 필드나 인터페이스 및 유니언에 대한 참조를 포함할 수 있는데, 이는 입력 인수로 사용하기에 적합하지 않기 때문입니다. 이러한 이유로 입력 객체는 시스템에서 별도의 타입으로 존재합니다.
순환 참조

입력 객체는 다른 입력 객체를 필드 타입으로 참조할 수 있습니다. 순환 참조는 입력 객체가 직접적으로 또는 참조된 입력 객체를 통해 자신을 참조할 때 발생합니다.

일반적으로 순환 참조는 허용되지만, Non-Null 단일 필드의 끊기지 않는 연쇄로 정의되어서는 안 됩니다. 그런 입력 객체는 합법적인 값을 제공할 방법이 없기 때문에 유효하지 않습니다.

다음 예제는 필드 self를 생략하거나 값으로 null을 제공할 수 있으므로 유효한 순환 참조 입력 타입입니다.

Example № 81input Example {
  self: Example
  value: String
}

다음 예제도 유효합니다. 이 경우 self 필드는 빈 List가 될 수 있습니다.

Example № 82input Example {
  self: [Example!]!
  value: String
}

다음 예제는 유효하지 않습니다. self 필드에 유한한 값을 제공할 수 없기 때문입니다.

Counter Example № 83input Example {
  value: String
  self: Example!
}

다음 예제도 유효하지 않습니다. First.secondSecond.first 필드에 의해 Non-Null 단일 순환 참조가 발생하기 때문입니다.

Counter Example № 84input First {
  second: Second!
  value: String
}

input Second {
  first: First!
  value: String
}
결과 강제화

입력 객체는 결과로서 유효하지 않습니다. 입력 객체 타입은 객체(Object)나 인터페이스 필드의 반환 타입이 될 수 없습니다.

입력 강제화

입력 객체의 값은 입력 객체 리터럴이거나 변수로 제공된 순서 없는 맵이어야 하며, 그렇지 않으면 request error가 발생해야 합니다. 어느 경우든 입력 객체 리터럴 또는 순서 없는 맵은 이 입력 객체 타입의 필드로 정의되지 않은 이름을 포함해서는 안 되며, 포함되어 있다면 요청 오류가 발생해야 합니다.

강제의 결과는 해당 입력 객체 타입에 의해 정의되고 값이 존재하는 각 필드에 대한 항목을 가진 순서 없는 맵입니다. 결과 맵은 다음 규칙으로 구성됩니다:

  • 만약 정의된 입력 객체 필드에 대해 값이 제공되지 않았고 그 필드 정의가 기본값을 제공한다면, 해당 입력 필드 타입의 강제 규칙에 따라 기본값을 강제한 결과를 사용해야 합니다. 기본값이 제공되지 않았고 입력 객체 필드의 타입이 Non-Null이면 오류를 발생시켜야 합니다. 그렇지 않고 필드가 필수적이지 않다면 강제된 맵에 항목을 추가하지 않습니다.
  • 만약 입력 객체 필드에 대해 값으로 null이 제공되었고 필드의 타입이 Non-Null 타입이 아니라면, 강제된 순서 없는 맵의 항목은 null 값을 가집니다. 즉, 명시적으로 제공된 null 값과 값을 제공하지 않은 경우는 의미상 다릅니다.
  • 만약 입력 객체 필드에 리터럴 값이 제공되면, 강제된 순서 없는 맵의 항목은 그 필드 타입의 입력 강제 규칙에 따라 강제한 결과를 가집니다.
  • 만약 입력 객체 필드에 변수가 제공되면, 해당 변수의 런타임 값이 사용되어야 합니다. 런타임 값이 null이고 필드 타입이 Non-Null이면 execution error가 발생해야 합니다. 런타임 값이 제공되지 않으면 변수 정의의 기본값을 사용해야 합니다. 변수 정의가 기본값을 제공하지 않으면 입력 객체 필드 정의의 기본값을 사용해야 합니다.

다음은 String 필드 a와 필수(Non-Null) Int! 필드 b를 가진 입력 객체 타입의 입력 강제화 예시입니다:

Example № 85input ExampleInputObject {
  a: String
  b: Int!
}
리터럴 값 변수 강제된 값
{ a: "abc", b: 123 } {} { a: "abc", b: 123 }
{ a: null, b: 123 } {} { a: null, b: 123 }
{ b: 123 } {} { b: 123 }
{ a: $var, b: 123 } { var: null } { a: null, b: 123 }
{ a: $var, b: 123 } {} { b: 123 }
{ b: $var } { var: 123 } { b: 123 }
$var { var: { b: 123 } } { b: 123 }
"abc123" {} 오류: 잘못된 값
$var { var: "abc123" } 오류: 잘못된 값
{ a: "abc", b: "123" } {} 오류: 필드 b의 값이 잘못되었습니다
{ a: "abc" } {} 오류: 필수 필드 b가 없습니다
{ b: $var } {} 오류: 필수 필드 b가 없습니다.
$var { var: { a: "abc" } } 오류: 필수 필드 b가 없습니다
{ a: "abc", b: null } {} 오류: b는 null일 수 없습니다.
{ b: $var } { var: null } 오류: b는 null일 수 없습니다.
{ b: 123, c: "xyz" } {} 오류: 예상치 못한 필드 c
타입 검증
  1. 입력 객체 타입은 하나 이상의 입력 필드를 정의해야 합니다.
  2. 입력 객체 타입의 각 입력 필드에 대해:
    1. 입력 필드는 해당 입력 객체 타입 내에서 고유한 이름을 가져야 하며, 두 입력 필드가 같은 이름을 가질 수 없습니다.
    2. 입력 필드의 이름은 "__"(언더스코어 두 개)로 시작해서는 안 됩니다.
    3. 입력 필드는 IsInputType(inputFieldType)true를 반환하는 타입을 받아야 합니다.
    4. 만약 입력 필드 타입이 Non-Null이고 기본값이 정의되어 있지 않다면:
      1. @deprecated 지시문은 이 입력 필드에 적용되어서는 안 됩니다.
    5. 만약 입력 객체가 OneOf Input Object라면:
      1. 입력 필드의 타입은 nullable이어야 합니다.
      2. 입력 필드는 기본값을 가져서는 안 됩니다.
  3. 입력 객체가 직접적으로 또는 참조된 입력 객체를 통해 자신을 참조하는 경우, 참조 연쇄의 최소한 하나의 필드는 nullable 또는 List 타입이어야 합니다.
  4. InputObjectDefaultValueHasCycle(inputObject)false여야 합니다.
InputObjectDefaultValueHasCycle(inputObject, defaultValue, visitedFields)
  1. 만약 defaultValue가 제공되지 않았다면, 이를 빈 순서 없는 맵으로 초기화합니다.
  2. 만약 visitedFields가 제공되지 않았다면, 이를 빈 집합으로 초기화합니다.
  3. 만약 defaultValue가 리스트라면:
    1. defaultValue의 각 itemValue에 대해:
      1. 만약 InputObjectDefaultValueHasCycle(inputObject, itemValue, visitedFields)이면 true를 반환합니다.
  4. 그렇지 않고 defaultValue가 순서 없는 맵이라면:
    1. inputObject의 각 필드 field에 대해:
      1. 만약 InputFieldDefaultValueHasCycle(field, defaultValue, visitedFields)이면 true를 반환합니다.
  5. false를 반환합니다.
InputFieldDefaultValueHasCycle(field, defaultValue, visitedFields)
  1. 단언: defaultValue는 순서 없는 맵입니다.
  2. fieldTypefield의 타입으로 합니다.
  3. namedFieldTypefieldType의 기본(named) 타입으로 합니다.
  4. 만약 namedFieldType가 입력 객체 타입이 아니라면:
    1. false를 반환합니다.
  5. fieldNamefield의 이름으로 둡니다.
  6. fieldDefaultValuedefaultValue에서 fieldName에 대한 값으로 둡니다.
  7. 만약 fieldDefaultValue가 존재하면:
    1. InputObjectDefaultValueHasCycle(namedFieldType, fieldDefaultValue, visitedFields)를 반환합니다.
  8. 그렇지 않으면:
    1. fieldDefaultValuefield의 기본값으로 둡니다.
    2. 만약 fieldDefaultValue가 존재하지 않으면:
      1. false를 반환합니다.
    3. 만약 fieldvisitedFields에 포함되어 있으면:
      1. true를 반환합니다.
    4. nextVisitedFieldsfieldvisitedFields의 모든 항목을 포함하는 새로운 집합으로 둡니다.
    5. InputObjectDefaultValueHasCycle(namedFieldType, fieldDefaultValue, nextVisitedFields)를 반환합니다.

3.10.1OneOf 입력 객체

OneOf Input Object는 입력 객체의 특수 변형으로서 정확히 하나의 필드만 설정되고 non-null이어야 하며, 나머지 필드는 생략되어야 합니다. 이는 입력이 여러 옵션 중 하나일 수 있는 상황을 표현할 때 유용합니다.

타입 시스템 정의 언어를 사용할 때, 입력 객체가 OneOf 입력 객체임을 표시하려면 @oneOf 지시문을 사용합니다(따라서 정확히 하나의 필드만 제공되어야 합니다):

input UserUniqueCondition @oneOf {
  id: ID
  username: String
  organizationAndEmail: OrganizationAndEmailInput
}

스키마 내성(introspection)에서 __Type.isOneOf 필드는 OneOf 입력 객체에 대해 true를 반환하고, 다른 모든 입력 객체에 대해선 false를 반환합니다.

입력 강제화

OneOf 입력 객체의 값은 입력 객체 리터럴이거나 변수로 제공된 순서 없는 맵이어야 하며, 그렇지 않으면 request error가 발생해야 합니다.

  • 입력 객체의 입력 강제 규칙에 따라 강제된 맵을 구성하기 전에: 강제할 값은 정확히 하나의 항목을 포함해야 하고 그 항목은 null이거나 null 리터럴이어서는 안 됩니다. 그렇지 않으면 request error가 발생해야 합니다.
  • 모든 입력 객체의 입력 강제화 규칙은 OneOf 입력 객체에도 적용되어야 합니다.
  • 결과적으로 생성된 강제된 맵은 정확히 하나의 항목을 포함해야 하고, 그 항목의 값은 null이어서는 안 됩니다. 그렇지 않으면 execution error가 발생해야 합니다.

다음은 String 멤버 필드 aInt 멤버 필드 b를 가진 OneOf 입력 객체 타입의 추가적인 입력 강제화 예시입니다:

Example № 86input ExampleOneOfInputObject @oneOf {
  a: String
  b: Int
}
리터럴 값 변수 강제된 값
{ a: "abc" } {} { a: "abc" }
{ b: 123 } {} { b: 123 }
$var { var: { a: "abc" } } { a: "abc" }
{ a: null } {} 오류: 멤버 필드 a의 값은 null일 수 없습니다
$var { var: { a: null } } 오류: 멤버 필드 a의 값은 null일 수 없습니다
{ a: $a } {} 오류: 멤버 필드 a의 값이 지정되어야 합니다
{ a: "abc", b: 123 } {} 오류: 정확히 하나의 키만 지정해야 합니다
{ a: 456, b: "xyz" } {} 오류: 정확히 하나의 키만 지정해야 합니다
$var { var: { a: "abc", b: 123 } } 오류: 정확히 하나의 키만 지정해야 합니다
{ a: "abc", b: null } {} 오류: 정확히 하나의 키만 지정해야 합니다
{ a: "abc", b: $b } {} 오류: 정확히 하나의 키만 지정해야 합니다
{ a: $a, b: $b } { a: "abc" } 오류: 정확히 하나의 키만 지정해야 합니다
{} {} 오류: 정확히 하나의 키만 지정해야 합니다
$var { var: {} } 오류: 정확히 하나의 키만 지정해야 합니다

3.10.2입력 객체 확장

입력 객체 타입 확장은 이전에 정의된 입력 객체 타입으로부터 확장된 입력 객체 타입을 나타내는 데 사용됩니다. 예를 들어, 이는 다른 GraphQL 서비스를 확장한 서비스에서 사용될 수 있습니다.

타입 검증

입력 객체 타입 확장은 잘못 정의된 경우 유효하지 않을 수 있습니다.

  1. 해당 이름의 타입은 이미 정의되어 있어야 하며 입력 객체(Input Object) 타입이어야 합니다.
  2. 입력 객체 타입 확장의 모든 필드는 고유한 이름을 가져야 합니다.
  3. 입력 객체 타입 확장의 모든 필드는 이전 입력 객체의 필드여서는 안 됩니다.
  4. 제공된 비-반복 지시문(non-repeatable directives)은 이전 입력 객체 타입에 이미 적용되어 있어서는 안 됩니다.
  5. @oneOf 지시문은 입력 객체 타입 확장에서 제공되어서는 안 됩니다.
  6. 만약 원래의 입력 객체가 OneOf Input Object였다면:
    1. 입력 객체 타입 확장의 모든 필드는 nullable이어야 합니다.
    2. 입력 객체 타입 확장의 모든 필드는 기본값을 가져서는 안 됩니다.

3.11리스트

GraphQL 리스트는 각 항목의 타입(리스트의 항목 타입이라고 함)을 선언하는 특수한 컬렉션 타입입니다. 리스트 값은 항목 타입에 따라 직렬화되는 순서 있는 목록으로 직렬화됩니다.

필드가 리스트 타입을 사용함을 표시하려면 항목 타입을 대괄호로 감쌉니다: pets: [Pet]. 리스트의 중첩도 허용됩니다: matrix: [[Int]].

결과 강제화

GraphQL 서비스는 리스트 타입의 결과로 반드시 순서 있는 리스트를 반환해야 합니다. 리스트의 각 항목은 항목 타입에 대한 결과 강제화의 결과여야 합니다. 합리적인 강제가 불가능한 경우에는 실행 오류를 발생시켜야 합니다. 특히, 비-리스트 값이 반환되면 강제가 실패해야 하는데, 이는 타입 시스템과 구현 간의 기대 불일치를 나타냅니다.

리스트의 항목 타입이 널 허용(nullable)인 경우, 개별 항목의 준비 또는 강제 중에 발생한 오류는 해당 위치의 값이 null이 되도록 하며 응답에 실행 오류를 추가해야 합니다. 항목 타입이 Non-Null인 경우에는 개별 항목에서 발생한 실행 오류가 리스트 전체에 대한 실행 오류를 초래해야 합니다.

참고 자세한 동작은 실행 오류 처리(Handling Execution Errors)를 참조하세요.
입력 강제화

입력으로 기대될 때, 리스트 값은 리스트의 항목 타입이 각 항목을 수용할 수 있을 때만 허용됩니다.

리스트 타입에 대한 입력으로 전달된 값이 리스트가 아니고 null도 아닐 경우, 입력 강제의 결과는 크기 1의 리스트가 되며, 단일 항목 값은 제공된 값에 대해 리스트의 항목 타입에 대한 입력 강제를 수행한 결과입니다(중첩 리스트의 경우 재귀적으로 적용될 수 있음).

이를 통해 여러 값을 허용하는 입력(종종 "var args"라 불림)은 입력 타입을 리스트로 선언할 수 있으며, 단일 값의 일반적인 경우 클라이언트는 직접 그 값을 전달할 수 있어 리스트를 구성할 필요가 없습니다.

다음은 다양한 리스트 타입과 값에 대한 입력 강제화 예시입니다:

기대 타입 제공 값 강제된 값
[Int] [1, 2, 3] [1, 2, 3]
[Int] [1, "b", true] 오류: 잘못된 항목 값
[Int] 1 [1]
[Int] null null
[[Int]] [[1], [2, 3]] [[1], [2, 3]]
[[Int]] [1, 2, 3] [[1], [2], [3]]
[[Int]] [1, null, 3] [[1], null, [3]]
[[Int]] [[1], ["b"]] 오류: 잘못된 항목 값
[[Int]] 1 [[1]]
[[Int]] null null

3.12Non-Null(널 불가)

기본적으로 GraphQL의 모든 타입은 널 허용입니다; 위의 타입들에 대해 null 값은 유효한 응답입니다. 널을 허용하지 않는 타입을 선언하려면 GraphQL Non-Null 타입을 사용할 수 있습니다. 이 타입은 기본 타입을 래핑하며, 래핑된 타입과 동일하게 동작하되 null이 해당 래핑 타입에 대해 유효한 응답이 아니라는 점만 다릅니다. 필드에 Non-Null 타입을 표시하려면 느낌표를 사용합니다: name: String!.

널 허용 vs 선택적

필드는 선택 집합의 컨텍스트 내에서 항상 선택적입니다. 필드는 생략될 수 있으며 선택 집합은 (선택 집합이 비어있지 않은 한) 여전히 유효합니다. 그러나 Non-Null 타입을 반환하는 필드는 질의되었을 때 결코 null을 반환하지 않습니다.

입력(예: 필드 인수)은 기본적으로 항상 선택적입니다. 그러나 non-null 입력 타입은 필수입니다. Non-Null 입력 타입은 null 값을 허용하지 않을 뿐만 아니라 생략도 허용하지 않습니다. 단순화를 위해 nullable 타입은 항상 선택적이며 non-null 타입은 항상 필수로 간주됩니다.

결과 강제화

위의 모든 결과 강제화에서 null은 유효한 값으로 간주되었습니다. Non-Null 타입의 결과를 강제하려면 래핑된 타입의 강제화를 수행해야 합니다. 그 결과가 null이 아니면, Non-Null 타입의 강제 결과는 그 값입니다. 결과가 null이면 실행 오류를 발생시켜야 합니다.

참고 Non-Null인 응답 위치에서 실행 오류가 발생하면, 그 오류는 부모 응답 위치로 전파됩니다. 이 과정에 대한 자세한 내용은 실행 섹션의 오류와 Non-Null 타입을 참조하세요.
입력 강제화

Non-Null 타입의 인수나 입력 객체 필드가 제공되지 않았거나, 리터럴 값으로 null이 제공되었거나, 런타임에 값이 제공되지 않았거나 null이 제공된 변수가 전달된 경우에는 요청 오류를 발생시켜야 합니다.

Non-Null 타입에 제공된 값이 null이 아닌 리터럴 값이거나 Non-Null 변수 값이라면, 래핑된 타입에 대한 입력 강제를 사용하여 강제됩니다.

Non-Null 인수는 생략될 수 없습니다:

Counter Example № 87{
  fieldWithNonNullArg
}

Non-Null 인수에 null 값을 제공할 수 없습니다:

Counter Example № 88{
  fieldWithNonNullArg(nonNullArg: null)
}

Nullable 타입의 변수를 Non-Null 인수에 제공할 수 없습니다:

Example № 89query withNullableVariable($var: String) {
  fieldWithNonNullArg(nonNullArg: $var)
}
참고 Validation 섹션은 nullable 변수 타입을 non-null 입력 타입에 제공하는 것을 무효한 것으로 정의합니다.
타입 검증
  1. Non-Null 타입은 다른 Non-Null 타입을 래핑해서는 안 됩니다.

3.12.1리스트와 Non-Null 결합

리스트와 Non-Null 래핑 타입은 조합될 수 있으며 보다 복잡한 타입을 표현합니다. 리스트와 Non-Null 타입의 결과 강제화 및 입력 강제화 규칙은 재귀적으로 적용됩니다.

예를 들어 리스트의 내부 항목 타입이 Non-Null인 경우([T!] 등) 그 리스트는 null 항목을 포함할 수 없습니다. 반면 Non-Null의 내부 타입이 리스트인 경우([T]! 등) null은 허용되지 않지만 빈 리스트는 허용됩니다.

다음은 다양한 타입과 값에 대한 결과 강제화 예시입니다:

기대 타입 내부 값 강제된 결과
[Int] [1, 2, 3] [1, 2, 3]
[Int] null null
[Int] [1, 2, null] [1, 2, null]
[Int] [1, 2, Error] [1, 2, null] (오류 로그 포함)
[Int]! [1, 2, 3] [1, 2, 3]
[Int]! null 오류: 값은 null일 수 없습니다
[Int]! [1, 2, null] [1, 2, null]
[Int]! [1, 2, Error] [1, 2, null] (오류 로그 포함)
[Int!] [1, 2, 3] [1, 2, 3]
[Int!] null null
[Int!] [1, 2, null] null (강제화 오류 기록 포함)
[Int!] [1, 2, Error] null (오류 로그 포함)
[Int!]! [1, 2, 3] [1, 2, 3]
[Int!]! null 오류: 값은 null일 수 없습니다
[Int!]! [1, 2, null] 오류: 항목은 null일 수 없습니다
[Int!]! [1, 2, Error] 오류: 항목에서 오류 발생

3.13지시문

ExecutableDirectiveLocation
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
TypeSystemDirectiveLocation
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION

GraphQL 스키마는 문서의 다양한 부분에 주석을 달기 위해 사용되는 지시자들을 설명합니다. 이러한 주석은 유효성 검사기, 실행기 또는 코드 생성기와 같은 클라이언트 도구가 해당 부분을 다르게 평가해야 함을 나타내는 역할을 합니다.

내장 지시자

내장 지시자는 본 명세 내에서 정의된 모든 지시자를 의미합니다.

GraphQL 구현체는 @skip@include 지시자를 제공해야 합니다.

타입 시스템 정의 언어를 지원하는 GraphQL 구현체는 스키마의 사용 중단된 부분을 표현할 때 @deprecated 지시자를 제공해야 합니다.

타입 시스템 정의 언어를 지원하는 GraphQL 구현체는 사용자 정의 스칼라 정의를 표현할 때 @specifiedBy 지시자를 제공하는 것이 좋습니다.

타입 시스템 정의 언어로 GraphQL 스키마를 표현할 때 OneOf 입력 객체를 나타내는 경우 @oneOf 지시자를 제공하는 것이 좋습니다.

타입 시스템 정의 언어를 사용하여 GraphQL 스키마를 표현할 때 간결함을 위해 어떤 내장 지시자든 생략할 수 있습니다.

GraphQL 서비스를 introspect(내부 검사)할 때 제공된 모든 지시자들(어떤 내장 지시자 포함)은 반환된 지시자 집합에 포함되어야 합니다.

사용자 정의 지시자

GraphQL 서비스 및 클라이언트 도구는 본 문서에 정의된 것 외에 추가적인 사용자 정의 지시자를 제공할 수 있습니다. 지시자는 GraphQL을 사용자 정의 또는 실험적 동작으로 확장하는 권장 수단입니다.

참고 사용자 정의 지시자를 정의할 때는 사용 범위를 명확히 하고 향후 문서 버전과의 충돌을 방지하기 위해 지시자의 이름에 접두사를 붙이는 것이 권장됩니다. 예를 들어 Facebook의 GraphQL 서비스에서 사용하는 사용자 정의 지시자@auth 대신 @fb_auth로 명명해야 합니다. 이는 제안된 명세 추가 기능이 RFC 절차 동안 변경될 수 있기 때문에 특히 권장됩니다. 예를 들어 진행 중인 @live의 경우 @rfc_live로 명명하는 것이 좋습니다.

지시자는 선언된 위치에서만 사용되어야 합니다. 다음 예시는 필드에 주석을 달기 위해 사용할 수 있는 지시자를 정의한 것입니다:

Example № 90directive @example on FIELD

fragment SomeFragment on SomeType {
  field @example
}

긴 위치 목록을 표현할 때 형식을 돕기 위해 선택적으로 앞에 | 문자를 붙여 지시자 위치를 정의할 수 있습니다:

Example № 91directive @example on
  | FIELD
  | FRAGMENT_SPREAD
  | INLINE_FRAGMENT

지시자는 타입 시스템 정의 언어에도 주석을 달기 위해 사용될 수 있으며, 이는 GraphQL 실행 서비스를 생성하거나 클라이언트 런타임 코드를 생성하는 등 추가 메타데이터를 제공하는 데 유용합니다.

다음 예에서는 지시자 @example가 필드 및 인수 정의를 주석합니다:

Example № 92directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION

type SomeType {
  field(arg: Int @example): String @example
}

지시자는 repeatable 키워드를 포함하여 반복 가능하도록 정의할 수 있습니다. 동일한 위치에서 서로 다른 인수로 같은 지시자를 여러 번 사용해야 하는 경우(특히 지시자를 통해 타입 또는 스키마 확장에 추가 정보를 제공해야 할 때) 반복 가능한 지시자가 유용합니다:

Example № 93directive @delegateField(name: String!) repeatable on OBJECT | INTERFACE

type Book @delegateField(name: "pageCount") @delegateField(name: "author") {
  id: ID!
}

extend type Book @delegateField(name: "index")

지시자를 정의할 때 해당 지시자가 직접적 또는 간접적으로 자신을 참조해서는 안 됩니다:

Counter Example № 94directive @invalidExample(arg: String @invalidExample) on ARGUMENT_DEFINITION
참고 지시자들이 나타나는 순서(반복 가능한 지시자 포함)가 중요할 수 있습니다.
타입 검증
  1. 지시자 정의는 최소 하나의 DirectiveLocation을 포함해야 합니다.
  2. 지시자 정의는 자신을 직접 참조하는 지시자의 사용을 포함해서는 안 됩니다.
  3. 지시자 정의는 이 지시자를 포함하도록 전이적으로 참조하는 타입 또는 지시자를 참조하여 자신을 간접적으로 참조하는 지시자의 사용을 포함해서는 안 됩니다.
  4. 지시자의 이름은 "__" (밑줄 두 개)로 시작해서는 안 됩니다.
  5. 각 지시자 인수에 대하여:
    1. 해당 인수의 이름은 "__" (밑줄 두 개)로 시작해서는 안 됩니다.
    2. 그 지시자 내에서 인수 이름은 고유해야 하며, 동일한 이름을 가진 두 인수가 존재해서는 안 됩니다.
    3. 인수는 IsInputType(argumentType)true를 반환하는 타입을 허용해야 합니다.

3.13.1@skip

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

@skip 내장 지시자는 필드, 프래그먼트 스프레드 및 인라인 프래그먼트에 제공될 수 있으며, if 인수에 의해 설명된 대로 실행 시 조건부로 제외할 수 있게 합니다.

이 예제에서 experimentalField는 변수 $someTestfalse일 때만 쿼리됩니다.

Example № 95query myQuery($someTest: Boolean!) {
  experimentalField @skip(if: $someTest)
}

3.13.2@include

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

@include 내장 지시자는 필드, 프래그먼트 스프레드 및 인라인 프래그먼트에 제공될 수 있으며, if 인수에 의해 설명된 대로 실행 시 조건부 포함을 허용합니다.

이 예제에서 experimentalField는 변수 $someTesttrue일 때만 쿼리됩니다.

Example № 96query myQuery($someTest: Boolean!) {
  experimentalField @include(if: $someTest)
}
참고 @skip@include 중 어느 것도 다른 것보다 우선하지 않습니다. 동일한 필드나 프래그먼트에 두 지시자가 모두 제공된 경우, 그 필드나 프래그먼트는 @skip 조건이 거짓이고 @include 조건이 참인 경우에만 쿼리되어야 합니다. 다시 말해, @skip 조건이 참이거나 @include 조건이 거짓인 경우 해당 필드나 프래그먼트는 쿼리되어서는 안 됩니다.

3.13.3@deprecated

directive @deprecated(
  reason: String! = "No longer supported"
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE

@deprecated 내장 지시자는 타입 시스템 정의 언어 내에서 필드, 필드의 인수, 입력 타입의 입력 필드 또는 열거형 타입의 값과 같이 GraphQL 서비스 스키마의 사용 중단된 부분을 표시하는 데 사용됩니다.

사용 중단 사유는 Markdown 문법(예: CommonMark)으로 형식화됩니다.

다음 타입 정의 예제에서는 oldFieldnewField를 대신하도록 사용 중단되었고, oldArgnewArg를 대신하도록 사용 중단되었습니다.

Example № 97type ExampleType {
  newField: String
  oldField: String @deprecated(reason: "Use `newField`.")

  anotherField(
    newArg: String
    oldArg: String @deprecated(reason: "Use `newArg`.")
  ): String
}

@deprecated 지시자는 필수(기본값 없음인 non-null) 인수나 입력 객체 필드 정의에 나타나서는 안 됩니다.

Counter Example № 98type ExampleType {
  invalidField(
    newArg: String
    oldArg: String! @deprecated(reason: "Use `newArg`.")
  ): String
}

필수 인수나 입력 필드를 사용 중단하려면 먼저 해당 필드를 nullable로 변경하거나 기본값을 추가하여 선택적으로 만들어야 합니다.

3.13.4@specifiedBy

directive @specifiedBy(url: String!) on SCALAR

@specifiedBy 내장 지시자는 사용자 정의 스칼라 타입의 동작을 지정하기 위한 스칼라 명세 URL을 제공하는 데 사용됩니다. URL은 데이터 형식, 직렬화 및 강제 변환 규칙의 사람 읽기용 명세를 가리켜야 하며, 내장 스칼라 타입에는 나타나서는 안 됩니다.

참고 GraphQL 스칼라 명세 구현에 대한 세부 사항은 scalars.graphql.org 구현 가이드에서 확인할 수 있습니다.

다음 예제에서는 UUID에 대한 사용자 정의 스칼라 타입을 해당 IETF 명세를 가리키는 URL과 함께 정의합니다.

Example № 99scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")

3.13.5@oneOf

directive @oneOf on INPUT_OBJECT

@oneOf 내장 지시자는 타입 시스템 정의 언어 내에서 입력 객체가 OneOf 입력 객체임을 나타내는 데 사용됩니다.

Example № 100input UserUniqueCondition @oneOf {
  id: ID
  username: String
  organizationAndEmail: OrganizationAndEmailInput
}

4내성

GraphQL 서비스는 자신의 스키마에 대해 내성(introspection)을 지원합니다. 이 스키마는 GraphQL 자체를 사용해 질의되며, 도구 구축을 위한 강력한 플랫폼을 만듭니다.

간단한 애플리케이션의 예시 요청을 보겠습니다. 이 경우 User 타입이 있고 세 개의 필드 id, name, birthday를 갖습니다.

예를 들어 다음과 같은 서비스의 타입 정의가 주어졌다면:

Example № 101type User {
  id: String
  name: String
  birthday: Date
}

다음과 같은 연산을 포함하는 요청은:

Example № 102{
  __type(name: "User") {
    name
    fields {
      name
      type {
        name
      }
    }
  }
}

다음과 같은 결과를 생성합니다:

Example № 103{
  "__type": {
    "name": "User",
    "fields": [
      {
        "name": "id",
        "type": { "name": "String" }
      },
      {
        "name": "name",
        "type": { "name": "String" }
      },
      {
        "name": "birthday",
        "type": { "name": "Date" }
      }
    ]
  }
}
예약된 이름

사용자 정의 타입 및 필드와 동일한 문맥에서 사용되는 GraphQL 내성 시스템에 필요한 타입과 필드는 이름 충돌을 피하기 위해 "__" (언더스코어 두 개)로 접두됩니다.

그 이외의 경우, GraphQL 타입 시스템 내의 어떤 Name도 언더스코어 두 개 "__"로 시작해서는 안 됩니다.

4.1타입 이름 내성

GraphQL은 연산의 어떤 selection set에서도 타입 이름 내성(type name introspection)을 지원합니다. 단, subscription 연산의 루트 선택에서는 예외입니다. 타입 이름 내성은 어떤 Object, Interface 또는 Union에서든 메타 필드 __typename: String!을 통해 수행되며, 실행 중 해당 지점의 구체적인 Object 타입 이름을 반환합니다.

이는 주로 Interface나 Union 타입을 대상으로 질의할 때, 반환된 실제 Object 타입이 어떤 것인지 식별하는 데 사용됩니다.

메타 필드로서 __typename은 암묵적이며 어떤 정의된 타입의 필드 목록에도 나타나지 않습니다.

참고 __typename은 subscription 연산의 루트 필드로는 포함될 수 없습니다.

4.2스키마 내성

스키마 내성 시스템은 쿼리 연산의 루트 타입에서 접근 가능한 메타 필드 __schema__type을 통해 접근할 수 있습니다.

__schema: __Schema!
__type(name: String!): __Type

모든 메타 필드와 마찬가지로, 이들은 암묵적이며 쿼리 연산의 루트 타입 필드 목록에는 나타나지 않습니다.

일급 문서화

내성 시스템의 모든 타입은 타입 설계자가 기능 외에 문서를 게시할 수 있도록 description 필드(타입 String)를 제공합니다. GraphQL 서비스는 description 필드를 Markdown 문법(CommonMark 규격)을 사용하여 반환할 수 있으므로, description을 표시하는 도구는 CommonMark 호환 Markdown 렌더러를 사용하는 것이 권장됩니다.

폐기(Deprecation)

하위 호환성 관리를 지원하기 위해, GraphQL 필드, 인수, 입력 필드 및 열거형 값은 폐기되었는지 여부(isDeprecated: Boolean!)와 왜 폐기되었는지에 대한 설명(deprecationReason: String)을 나타낼 수 있습니다.

GraphQL 내성을 사용하여 제작된 도구는 폐기를 존중하여, 정보 숨김이나 개발자용 경고를 통해 폐기된 사용을 권장하지 않도록 해야 합니다.

안정적 정렬

스키마 가독성과 안정성을 향상시키기 위해 모든 데이터 컬렉션의 관찰 가능한 순서는 보존되어야 합니다. 스키마가 TypeSystemDocument에서 생성될 때, 내성은 각 요소 목록에 대해 소스의 동일한 순서로 항목을 반환해야 합니다: 객체 필드, 입력 객체 필드, 인수, 열거형 값, 지시문, 유니언 멤버 타입, 그리고 구현된 인터페이스.

스키마 내성 스키마

스키마 내성 시스템 자체는 GraphQL 스키마로 표현됩니다. 아래는 스키마 내성을 제공하는 전체 타입 시스템 정의 집합으로, 아래 섹션들에서 완전하게 정의되어 있습니다.

type __Schema {
  description: String
  types: [__Type!]!
  queryType: __Type!
  mutationType: __Type
  subscriptionType: __Type
  directives: [__Directive!]!
}

type __Type {
  kind: __TypeKind!
  name: String
  description: String
  # may be non-null for custom SCALAR, otherwise null.
  specifiedByURL: String
  # must be non-null for OBJECT and INTERFACE, otherwise null.
  fields(includeDeprecated: Boolean! = false): [__Field!]
  # must be non-null for OBJECT and INTERFACE, otherwise null.
  interfaces: [__Type!]
  # must be non-null for INTERFACE and UNION, otherwise null.
  possibleTypes: [__Type!]
  # must be non-null for ENUM, otherwise null.
  enumValues(includeDeprecated: Boolean! = false): [__EnumValue!]
  # must be non-null for INPUT_OBJECT, otherwise null.
  inputFields(includeDeprecated: Boolean! = false): [__InputValue!]
  # must be non-null for NON_NULL and LIST, otherwise null.
  ofType: __Type
  # must be non-null for INPUT_OBJECT, otherwise null.
  isOneOf: Boolean
}

enum __TypeKind {
  SCALAR
  OBJECT
  INTERFACE
  UNION
  ENUM
  INPUT_OBJECT
  LIST
  NON_NULL
}

type __Field {
  name: String!
  description: String
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
  type: __Type!
  isDeprecated: Boolean!
  deprecationReason: String
}

type __InputValue {
  name: String!
  description: String
  type: __Type!
  defaultValue: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __EnumValue {
  name: String!
  description: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __Directive {
  name: String!
  description: String
  isRepeatable: Boolean!
  locations: [__DirectiveLocation!]!
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
}

enum __DirectiveLocation {
  QUERY
  MUTATION
  SUBSCRIPTION
  FIELD
  FRAGMENT_DEFINITION
  FRAGMENT_SPREAD
  INLINE_FRAGMENT
  VARIABLE_DEFINITION
  SCHEMA
  SCALAR
  OBJECT
  FIELD_DEFINITION
  ARGUMENT_DEFINITION
  INTERFACE
  UNION
  ENUM
  ENUM_VALUE
  INPUT_OBJECT
  INPUT_FIELD_DEFINITION
}

4.2.1__Schema 타입

__Schema 타입은 __schema 메타 필드에서 반환되며 GraphQL 서비스의 스키마에 관한 모든 정보를 제공합니다.

필드:

  • description은 String 또는 null을 반환할 수 있습니다.
  • queryType은 쿼리 연산의 루트 타입입니다.
  • mutationType은 뮤테이션 연산의 루트 타입이며, 지원되지 않으면 null입니다.
  • subscriptionType은 subscription 연산의 루트 타입이며, 지원되지 않으면 null입니다.
  • types는 이 스키마에 포함된 모든 명명된 타입의 집합을 반환해야 합니다. 어떤 내성 타입의 필드를 통해 찾을 수 있는 모든 명명된 타입은 이 집합에 포함되어야 합니다.
  • directives는 내장 지시문을 포함한 이 스키마 내에서 사용 가능한 모든 지시문의 집합을 반환해야 합니다.

4.2.2__Type 타입

__Type은 타입 내성 시스템의 핵심입니다. 이 타입은 시스템의 모든 타입을 나타냅니다: 명명된 타입(예: Scalar 및 Object 타입)과 타입 수정자(예: List 및 Non-Null 타입) 둘 다 포함합니다.

타입 수정자는 필드 ofType에 표시된 타입을 수정하는 데 사용됩니다. 이 수정된 타입은 재귀적으로 또 다른 수정된 타입일 수 있으며, 리스트나 non-null 타입 등을 표현하며 결국에는 명명된 타입을 수정합니다.

여러 종류의 타입이 있으며, 각 종류마다 유효한 필드가 다릅니다. 모든 가능한 종류는 __TypeKind 열거형에 나열되어 있습니다.

아래 각 하위 섹션은 __TypeKind 열거형의 각 가능한 값에 대해 __Type의 예상 필드를 정의합니다:

  • "SCALAR"
  • "OBJECT"
  • "INTERFACE"
  • "UNION"
  • "ENUM"
  • "INPUT_OBJECT"
  • "LIST"
  • "NON_NULL"
스칼라

Int, String, Boolean 같은 스칼라 타입을 나타냅니다. 스칼라는 필드를 가질 수 없습니다.

또한 사용자 정의 스칼라를 나타낼 수 있으며, 이 경우 specifiedByURL을 제공할 수 있습니다(스칼라 명세 URL).

필드:

  • kind__TypeKind.SCALAR을 반환해야 합니다.
  • name은 String을 반환해야 합니다.
  • description은 String 또는 null을 반환할 수 있습니다.
  • specifiedByURL은 사용자 정의 스칼라에 대해 URL 형식의 String을 반환할 수 있으며, 그렇지 않으면 null이어야 합니다.
  • 다른 모든 필드는 null을 반환해야 합니다.
오브젝트

오브젝트 타입은 필드 집합의 구체적 인스턴스를 나타냅니다. 내성 타입들(예: __Type, __Field 등)은 오브젝트의 예입니다.

필드:

  • kind__TypeKind.OBJECT을 반환해야 합니다.
  • name은 String을 반환해야 합니다.
  • description은 String 또는 null을 반환할 수 있습니다.
  • fields는 이 타입에서 선택 가능한 필드들의 집합을 반환해야 합니다.
    • includeDeprecated 인수를 허용하며 기본값은 false입니다. true이면 폐기된 필드도 반환됩니다.
  • interfaces는 이 오브젝트가 구현하는 인터페이스들의 집합을 반환해야 합니다(없으면 빈 집합을 반환해야 합니다).
  • }
유니언

유니언은 공통 필드가 선언되어 있지 않은 추상 타입입니다. 유니언이 가질 수 있는 가능한 타입들은 possibleTypes에 명시적으로 나열됩니다. 객체 타입은 해당 타입을 수정하지 않고도 유니언의 멤버가 될 수 있습니다.

필드:

  • kind__TypeKind.UNION을 반환해야 합니다.
  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • possibleTypes는 이 유니언 내에서 표현될 수 있는 타입들의 목록을 반환합니다. 이들은 객체 타입이어야 합니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.
인터페이스

인터페이스는 공통 필드가 선언된 추상 타입입니다. 인터페이스를 구현하는 모든 타입은 인터페이스에 명시된 이름 있는 모든 필드를 정의해야 하며, 각 구현 필드의 타입은 인터페이스 타입과 같거나(또는 공변적으로) 서브타입이어야 합니다. 이 인터페이스를 구현하는 타입들은 possibleTypes에 명시적으로 나열됩니다.

필드:

  • kind__TypeKind.INTERFACE을 반환해야 합니다.
  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • fields는 이 인터페이스가 요구하는 필드 집합을 반환해야 합니다.
    • 기본값이 falseincludeDeprecated 인수를 허용합니다. true이면 폐기된 필드도 반환됩니다.
  • interfaces는 이 인터페이스가 구현하는 인터페이스들의 집합을 반환해야 합니다(없으면 빈 집합을 반환해야 합니다).
  • possibleTypes는 이 인터페이스를 구현하는 타입들의 목록을 반환합니다. 이들은 객체 타입이어야 합니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.
열거형

열거형은 정의된 값들의 집합만 가질 수 있는 특별한 스칼라입니다.

필드:

  • kind__TypeKind.ENUM을 반환해야 합니다.
  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • enumValues__EnumValue 목록으로서 열거형 값들의 집합을 반환해야 합니다. 값은 최소 하나 이상이어야 하며 고유한 이름을 가져야 합니다.
    • 기본값이 falseincludeDeprecated 인수를 허용합니다. true이면 폐기된 열거형 값도 반환됩니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.
입력 객체

입력 객체는 명명된 입력 값들의 목록으로 정의된 복합 타입입니다. 입력 객체는 인수와 변수에 대한 입력으로만 사용되며 필드 반환 타입이 될 수 없습니다.

예를 들어 입력 객체 Point는 다음과 같이 정의될 수 있습니다:

Example № 104input Point {
  x: Int
  y: Int
}

필드:

  • kind__TypeKind.INPUT_OBJECT을 반환해야 합니다.
  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • inputFields__InputValue 목록으로서 입력 필드 집합을 반환해야 합니다.
    • 기본값이 falseincludeDeprecated 인수를 허용합니다. true이면 폐기된 입력 필드도 반환됩니다.
  • isOneOf는 해당 입력 객체가 OneOf Input Object를 나타낼 때 true를 반환해야 하며, 그렇지 않으면 false를 반환해야 합니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.
리스트

리스트는 GraphQL에서 값들의 연속을 나타냅니다. 리스트 타입은 타입 수정자이며, 각 항목의 타입을 정의하는 ofType 필드로 다른 타입 인스턴스를 래핑합니다.

ofType 필드의 수정된 타입은 자체적으로 수정된 타입일 수 있어, 리스트의 리스트나 Non-Null의 리스트 등을 표현할 수 있습니다.

필드:

  • kind__TypeKind.LIST을 반환해야 합니다.
  • ofType은 어떤 종류의 타입이든 반환해야 합니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.
Non-Null

GraphQL 타입은 기본적으로 널 허용입니다. 필드 타입에 대해 null 값은 유효한 응답입니다.

Non-Null 타입은 타입 수정자로서 ofType 필드에 다른 타입 인스턴스를 래핑합니다. Non-Null 타입은 응답으로 null을 허용하지 않으며, 인수 및 입력 객체 필드에 대한 필수 입력을 나타냅니다.

ofType 필드의 수정된 타입은 자체적으로 수정된 리스트 타입일 수 있어 Non-Null인 리스트 등을 표현할 수 있습니다. 다만 중복된 Non-Null의 Non-Null과 같은 형태를 피하기 위해 ofType은 Non-Null로 수정된 타입일 수 없습니다.

필드:

  • kind__TypeKind.NON_NULL을 반환해야 합니다.
  • ofType은 Non-Null을 제외한 어떤 종류의 타입이든 반환해야 합니다.
  • 나머지 모든 필드는 null을 반환해야 합니다.

4.2.3__Field 타입

__Field 타입은 객체 또는 인터페이스 타입의 각 필드를 나타냅니다.

필드:

  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • args는 이 필드가 수용하는 인수들을 나타내는 __InputValue의 리스트를 반환합니다.
    • 기본값이 falseincludeDeprecated 인수를 허용합니다. true이면 폐기된 인수도 반환됩니다.
  • type은 이 필드가 반환하는 값의 타입을 나타내는 __Type을 반환해야 합니다.
  • isDeprecated는 이 필드를 더 이상 사용하지 말아야 하는 경우 true를, 그렇지 않으면 false를 반환합니다.
  • deprecationReason는 이 필드가 폐기된 이유를 반환하며, 이 필드가 폐기되지 않았다면 null을 반환합니다.

4.2.4__InputValue 타입

__InputValue 타입은 필드 및 지시문의 인수뿐만 아니라 입력 객체의 inputFields를 나타냅니다.

필드:

  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • type은 이 입력 값이 기대하는 타입을 나타내는 __Type을 반환해야 합니다.
  • defaultValue는 런타임에 값이 제공되지 않았을 때 이 입력 값이 사용하는 기본값을 GraphQL 언어로 인코딩한 문자열을 반환할 수 있습니다. 기본값이 없다면 null을 반환합니다.
  • isDeprecated는 해당 입력 필드나 인수를 더 이상 사용하지 말아야 할 경우 true를, 그렇지 않으면 false를 반환합니다.
  • deprecationReason는 해당 입력 필드나 인수가 폐기된 이유를 반환하며, 폐기되지 않았다면 null을 반환합니다.

4.2.5__EnumValue 타입

__EnumValue 타입은 열거형의 가능한 값 중 하나를 나타냅니다.

필드:

  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • isDeprecated는 해당 열거형 값이 더 이상 사용되지 않아야 하는 경우 true를, 그렇지 않으면 false를 반환합니다.
  • deprecationReason는 해당 열거형 값이 폐기된 이유를 반환하며, 폐기되지 않았다면 null을 반환합니다.

4.2.6__Directive 타입

__Directive 타입은 서비스가 지원하는 지시문을 나타냅니다.

이는 내장 지시문(built-in directive)과 사용자 정의 지시문(custom directive) 모두를 포함합니다.

각 지시문은 명시적으로 지원되는 위치에서만 사용될 수 있습니다. 가능한 모든 위치는 __DirectiveLocation 열거형에 나열되어 있습니다:

  • "QUERY"
  • "MUTATION"
  • "SUBSCRIPTION"
  • "FIELD"
  • "FRAGMENT_DEFINITION"
  • "FRAGMENT_SPREAD"
  • "INLINE_FRAGMENT"
  • "VARIABLE_DEFINITION"
  • "SCHEMA"
  • "SCALAR"
  • "OBJECT"
  • "FIELD_DEFINITION"
  • "ARGUMENT_DEFINITION"
  • "INTERFACE"
  • "UNION"
  • "ENUM"
  • "ENUM_VALUE"
  • "INPUT_OBJECT"
  • "INPUT_FIELD_DEFINITION"

필드:

  • name은 문자열을 반환해야 합니다.
  • description은 문자열 또는 null을 반환할 수 있습니다.
  • locations는 이 지시문을 배치할 수 있는 유효한 위치들을 나타내는 __DirectiveLocation의 리스트를 반환합니다.
  • args는 이 지시문이 수용하는 인수들을 나타내는 __InputValue의 리스트를 반환합니다.
    • 기본값이 falseincludeDeprecated 인수를 허용합니다. true이면 폐기된 인수도 반환됩니다.
  • isRepeatable는 해당 지시문이 단일 위치에서 반복 사용될 수 있는지 여부를 나타내는 불리언을 반환해야 합니다.

5검증

GraphQL 서비스는 요청이 문법적으로 올바른지 여부만 확인하는 것이 아니라, 주어진 GraphQL 스키마의 문맥에서 모호함이나 실수가 없는지도 보장합니다.

유효하지 않은 요청은 기술적으로는 여전히 실행 가능하며, 실행 섹션의 알고리즘에서 정의된 대로 항상 안정적인 결과를 생성합니다. 다만 그 결과는 모호하거나 놀랍거나 예기치 않을 수 있으므로, 실행은 유효한 요청에 대해서만 이루어져야 합니다.

일반적으로 검증은 실행 직전에 요청의 문맥에서 수행되지만, 동일한 요청이 이전에 검증된 것으로 알려져 있는 경우 GraphQL 서비스는 명시적으로 다시 검증하지 않고 요청을 실행할 수 있습니다. 예를 들어: 개발 중에 요청이 검증되었고 이후 변경되지 않았다면 유효한 것으로 간주할 수 있으며, 서비스는 한 번 검증한 요청의 결과를 메모이제이션하여 동일한 요청을 반복적으로 검증하는 것을 피할 수 있습니다. 클라이언트 측이나 개발 시점 도구는 검증 오류를 보고하고 해당 시점에 유효하지 않은 요청의 구성 또는 실행을 허용해서는 안 됩니다.

타입 시스템 진화

GraphQL 타입 시스템 스키마는 새로운 타입과 필드를 추가하면서 시간이 지남에 따라 진화할 수 있으며, 이로 인해 이전에는 유효했던 요청이 나중에는 유효하지 않게 될 수 있습니다. 이전에 유효했던 요청을 무효로 만들 수 있는 모든 변경은 breaking change로 간주됩니다. GraphQL 서비스 및 스키마 유지관리자는 breaking change를 피하도록 권장되지만, 이러한 변화에 더 탄력적으로 대응하기 위해 고급 GraphQL 시스템은 어떤 시점에 검증 오류가 없었던 요청을 변경되지 않은 경우 계속 실행할 수 있도록 허용할 수 있습니다.

예제들

이 섹션의 예제들은 다음 타입들을 사용합니다:

Example № 105type Query {
  dog: Dog
  findDog(searchBy: FindDogInput): Dog
}

type Mutation {
  addPet(pet: PetInput!): Pet
  addPets(pets: [PetInput!]!): [Pet]
}

enum DogCommand {
  SIT
  DOWN
  HEEL
}

type Dog implements Pet {
  name: String!
  nickname: String
  barkVolume: Int
  doesKnowCommand(dogCommand: DogCommand!): Boolean!
  isHouseTrained(atOtherHomes: Boolean): Boolean!
  owner: Human
}

interface Sentient {
  name: String!
}

interface Pet {
  name: String!
}

type Alien implements Sentient {
  name: String!
  homePlanet: String
}

type Human implements Sentient {
  name: String!
  pets: [Pet!]
}

enum CatCommand {
  JUMP
}

type Cat implements Pet {
  name: String!
  nickname: String
  doesKnowCommand(catCommand: CatCommand!): Boolean!
  meowVolume: Int
}

union CatOrDog = Cat | Dog
union DogOrHuman = Dog | Human
union HumanOrAlien = Human | Alien

input FindDogInput {
  name: String
  owner: String
}

input CatInput {
  name: String!
  nickname: String
  meowVolume: Int
}

input DogInput {
  name: String!
  nickname: String
  barkVolume: Int
}

input PetInput @oneOf {
  cat: CatInput
  dog: DogInput
}

5.1문서

5.1.1실행 가능한 정의

형식 명세
설명

GraphQL 실행은 실행 가능한 정의인 Operation과 Fragment만 고려합니다. 타입 시스템 정의와 확장은 실행 가능하지 않으며 실행 중에 고려되지 않습니다.

모호함을 피하기 위해 TypeSystemDefinitionOrExtension을 포함하는 문서는 실행에 대해 무효입니다.

직접 실행할 의도가 없는 GraphQL 문서는 TypeSystemDefinitionOrExtension를 포함할 수 있습니다.

예를 들어, 다음 문서는 제공된 타입 확장을 원래 실행 스키마가 알지 못할 수 있기 때문에 실행에 대해 무효입니다:

Counter Example № 106query getDogName {
  dog {
    name
    color
  }
}

extend type Dog {
  color: String
}

5.2연산

5.2.1모든 연산 정의

5.2.1.1연산 타입의 존재

형식 명세
  • 문서의 각 연산 정의 operation에 대해:
    • operation의 종류에 해당하는 스키마의 root operation typerootOperationType이라 하자.
    • rootOperationType은 존재해야 합니다.
설명

스키마는 지원하는 각 연산 종류에 대해 root operation type을 정의합니다. 모든 스키마는 query 연산을 지원해야 하지만, mutationsubscription 연산에 대한 지원은 선택사항입니다.

문서에 정의된 연산 종류에 필요한 root operation type이 스키마에 포함되어 있지 않다면, 그 연산은 실행할 수 없으므로 무효입니다.

예를 들어 다음 스키마가 주어진다면:

Example № 107type Query {
  hello: String
}

다음 연산은 유효합니다:

Example № 108query helloQuery {
  hello
}

반면 다음 연산은 무효입니다:

Counter Example № 109mutation goodbyeMutation {
  goodbye
}

5.2.2이름 있는 연산 정의

5.2.2.1연산 이름의 고유성

형식 명세
  • 문서의 각 연산 정의 operation에 대해:
    • operationNameoperation의 이름이라 하자.
    • 만약 operationName이 존재하면:
      • operations를 문서 내에서 이름이 operationName인 모든 연산 정의로 두자.
      • operations는 하나의 원소로 이루어진 집합이어야 합니다.
설명

각 이름 있는 연산 정의는 문서 내에서 그 이름으로 참조될 때 유일해야 합니다.

예를 들어 다음 문서는 유효합니다:

Example № 110query getDogName {
  dog {
    name
  }
}

query getOwnerName {
  dog {
    owner {
      name
    }
  }
}

반면 다음 문서는 무효입니다:

Counter Example № 111query getName {
  dog {
    name
  }
}

query getName {
  dog {
    owner {
      name
    }
  }
}

각 연산의 타입이 서로 다르더라도 이 문서는 무효입니다:

Counter Example № 112query dogOperation {
  dog {
    name
  }
}

mutation dogOperation {
  mutateDog {
    id
  }
}

5.2.3익명 연산 정의

5.2.3.1단독 익명 연산

형식 명세
  • 문서의 모든 연산 정의를 operations라 하자.
  • 문서의 모든 익명 연산 정의를 anonymous라 하자.
  • 만약 operations의 원소 수가 1보다 크다면:
    • anonymous는 비어 있어야 합니다.
설명

GraphQL은 문서에 오직 하나의 연산만 존재할 때 그 연산을 정의하는 간편 표기(shorthand)를 허용합니다.

예를 들어 다음 문서는 유효합니다:

Example № 113{
  dog {
    name
  }
}

반면 다음 문서는 무효입니다:

Counter Example № 114{
  dog {
    name
  }
}

query getName {
  dog {
    owner {
      name
    }
  }
}

5.2.4구독 연산 정의

5.2.4.1단일 루트 필드

형식 명세
  • Let subscriptionType be the root Subscription type in schema.
  • 문서의 각각의 구독 연산 정의 subscription에 대해:
    • Let selectionSet be the top level selection set on subscription.
    • Let collectedFieldsMap be the result of CollectSubscriptionFields(subscriptionType, selectionSet).
    • collectedFieldsMap에는 정확히 하나의 항목이 있어야 하며, 그 항목은 내성(introspection) 필드여서는 안 됩니다.
CollectSubscriptionFields(objectType, selectionSet, visitedFragments)
  1. 만약 visitedFragments가 제공되지 않았다면, 이를 빈 집합으로 초기화합니다.
  2. collectedFieldsMap을 빈 순서 있는 집합들의 순서 있는 맵으로 초기화합니다.
  3. selectionSet의 각 selection에 대해 다음을 수행합니다:
    1. selection@skip 지시문을 제공해서는 안 됩니다.
    2. selection@include 지시문을 제공해서는 안 됩니다.
    3. 만약 selectionField라면:
      1. selectionresponse nameresponseName이라 합니다(별칭(alias)이 정의되어 있으면 그 별칭, 그렇지 않으면 필드 이름).
      2. collectedFieldsMap에서 키 responseName에 해당하는 field set 값을 fieldsForResponseKey라 합니다; 해당 키가 없으면 빈 순서 있는 집합으로 항목을 생성합니다.
      3. selectionfieldsForResponseKey에 추가합니다.
    4. 만약 selectionFragmentSpread라면:
      1. selection의 이름을 fragmentSpreadName이라 합니다.
      2. 만약 fragmentSpreadNamevisitedFragments에 있다면, 다음 selection으로 진행합니다.
      3. fragmentSpreadNamevisitedFragments에 추가합니다.
      4. 현재 문서에서 이름이 fragmentSpreadName인 Fragment를 fragment라 합니다.
      5. 만약 그러한 fragment가 존재하지 않으면, 다음 selection으로 진행합니다.
      6. fragment의 타입 조건을 fragmentType이라 합니다.
      7. 만약 DoesFragmentTypeApply(objectType, fragmentType)false이면, 다음 selection으로 진행합니다.
      8. fragment의 최상위 선택 집합을 fragmentSelectionSet이라 합니다.
      9. fragmentCollectedFieldsMapCollectSubscriptionFields(objectType, fragmentSelectionSet, visitedFragments)의 결과라 합니다.
      10. fragmentCollectedFieldsMap의 각 responseNamefragmentFields에 대해:
        1. collectedFieldsMap에서 키 responseName에 해당하는 field set 값을 fieldsForResponseKey라 합니다; 해당 항목이 없으면 빈 순서 있는 집합으로 항목을 생성합니다.
        2. fragmentFields의 각 항목을 fieldsForResponseKey에 추가합니다.
    5. 만약 selectionInlineFragment이라면:
      1. selection의 타입 조건을 fragmentType이라 합니다.
      2. 만약 fragmentTypenull이 아니고, DoesFragmentTypeApply(objectType, fragmentType)false이면, 다음 selection으로 진행합니다.
      3. selection의 최상위 선택 집합을 fragmentSelectionSet이라 합니다.
      4. fragmentCollectedFieldsMapCollectSubscriptionFields(objectType, fragmentSelectionSet, visitedFragments)의 결과라 합니다.
      5. fragmentCollectedFieldsMap의 각 responseNamefragmentFields에 대해:
        1. collectedFieldsMap에서 키 responseName에 해당하는 field set 값을 fieldsForResponseKey라 합니다; 해당 항목이 없으면 빈 순서 있는 집합으로 항목을 생성합니다.
        2. fragmentFields의 각 항목을 fieldsForResponseKey에 추가합니다.
  4. collectedFieldsMap을 반환합니다.
참고 이 알고리즘은 CollectFields()와 매우 유사합니다. 차이점은 런타임 변수를 참조할 수 없기 때문에 루트에서 @skip@include 지시문을 사용할 수 없다는 점입니다.
설명

구독 연산은 정확히 하나의 루트 필드를 가져야 합니다.

런타임 변수를 사용할 수 없는 상황에서도 이를 결정할 수 있도록, 루트 선택 집합에서는 @skip@include 지시문을 금지해야 합니다.

유효한 예:

Example № 115subscription sub {
  newMessage {
    body
    sender
  }
}
Example № 116subscription sub {
  ...newMessageFields
}

fragment newMessageFields on Subscription {
  newMessage {
    body
    sender
  }
}

무효한 예:

Counter Example № 117subscription sub {
  newMessage {
    body
    sender
  }
  disallowedSecondRootField
}
Counter Example № 118subscription sub {
  ...multipleSubscriptions
}

fragment multipleSubscriptions on Subscription {
  newMessage {
    body
    sender
  }
  disallowedSecondRootField
}

구독 연산의 루트에서 @skip@include 지시문을 허용하지 않습니다. 다음 예제 또한 무효입니다:

Counter Example № 119subscription requiredRuntimeValidation($bool: Boolean!) {
  newMessage @include(if: $bool) {
    body
    sender
  }
  disallowedSecondRootField @skip(if: $bool)
}

구독 연산의 루트 필드는 내성(introspection) 필드일 수 없습니다. 다음 예제도 무효입니다:

Counter Example № 120subscription sub {
  __typename
}
참고 각 구독은 정확히 하나의 루트 필드를 가져야 하지만, 문서에는 임의의 개수의 연산을 포함할 수 있으며 각 연산은 서로 다른 루트 필드를 가질 수 있습니다. 실행 시 여러 구독 연산을 포함하는 문서는 GetOperation()에서 설명된 바와 같이 연산 이름을 제공해야 합니다.

5.3필드

5.3.1필드 선택

필드 선택은 Object, Interface 및 Union 타입에 존재해야 합니다.

형식 명세
  • 문서의 각 selection에 대해:
    • 해당 selection의 대상 필드를 fieldName으로 둡니다.
    • fieldName은 해당 범위의 타입에 정의되어 있어야 합니다.
설명

필드 선택의 대상 필드는 선택 집합의 범위 타입에 정의되어 있어야 합니다. 별칭(alias) 이름에는 제한이 없습니다.

예를 들어 다음 프래그먼트는 검증을 통과하지 못합니다:

Counter Example № 121fragment fieldNotDefined on Dog {
  meowVolume
}

fragment aliasedLyingFieldTargetNotDefined on Dog {
  barkVolume: kawVolume
}

인터페이스에 관해서는, 직접적인 필드 선택은 인터페이스 자체의 필드에 대해서만 허용됩니다. 구체 구현 타입(implementer)의 필드는 해당 인터페이스형 선택 집합의 유효성에 영향을 주지 않습니다.

예를 들어 다음은 유효합니다:

Example № 122fragment interfaceFieldSelection on Pet {
  name
}

다음은 무효입니다:

Counter Example № 123fragment definedOnImplementersButNotInterface on Pet {
  nickname
}

유니언은 필드를 정의하지 않으므로, 메타 필드 __typename을 제외하고는 유니언 타입 선택 집합에서 필드를 직접 선택할 수 없습니다. 유니언 타입 선택 집합의 필드는 프래그먼트를 통해서만 간접적으로 질의해야 합니다.

예를 들어 다음은 유효합니다:

Example № 124fragment inDirectFieldSelectionOnUnion on CatOrDog {
  __typename
  ... on Pet {
    name
  }
  ... on Dog {
    barkVolume
  }
}

그러나 다음은 무효입니다:

Counter Example № 125fragment directFieldSelectionOnUnion on CatOrDog {
  name
  barkVolume
}

5.3.2필드 선택 병합

형식 명세
  • GraphQL 문서에 정의된 임의의 선택 집합을 set이라 합니다.
  • FieldsInSetCanMerge(set)는 참이어야 합니다.
FieldsInSetCanMerge(set)
  1. set에서 주어진 response name를 가진 선택들의 집합(프래그먼트와 인라인 프래그먼트를 방문하는 것을 포함)을 fieldsForName이라 합니다.
  2. fieldsForName의 서로 다른 임의의 두 멤버 fieldAfieldB에 대해:
    1. SameResponseShape(fieldA, fieldB)는 참이어야 합니다.
    2. 만약 fieldAfieldB의 부모 타입이 같거나 둘 중 하나가 Object 타입이 아닌 경우:
      1. fieldAfieldB는 동일한 필드 이름을 가져야 합니다.
      2. fieldAfieldB는 동일한 인수 집합을 가져야 합니다.
      3. fieldA의 선택 집합과 fieldB의 선택 집합을 더한 결과를 mergedSet이라 합니다.
      4. FieldsInSetCanMerge(mergedSet)는 참이어야 합니다.
SameResponseShape(fieldA, fieldB)
  1. fieldA의 반환 타입을 typeA라 합니다.
  2. fieldB의 반환 타입을 typeB라 합니다.
  3. 만약 typeA 또는 typeB가 Non-Null이라면:
    1. 만약 typeA 또는 typeB가 nullable이라면, false를 반환합니다.
    2. typeAtypeA의 nullable 타입으로 둡니다.
    3. typeBtypeB의 nullable 타입으로 둡니다.
  4. 만약 typeA 또는 typeB가 List라면:
    1. 만약 둘 중 하나가 List가 아니면, false를 반환합니다.
    2. typeAtypeA의 항목 타입으로 둡니다.
    3. typeBtypeB의 항목 타입으로 둡니다.
    4. 3단계부터 반복합니다.
  5. 만약 typeA 또는 typeB가 Scalar 또는 Enum이라면:
    1. 만약 typeAtypeB가 동일한 타입이면 true를 반환하고, 아니면 false를 반환합니다.
  6. 단언: typeA는 object, union 또는 interface 타입입니다.
  7. 단언: typeB는 object, union 또는 interface 타입입니다.
  8. fieldA의 선택 집합과 fieldB의 선택 집합을 더한 결과를 mergedSet이라 합니다.
  9. mergedSet에서 특정 response name를 가진 선택들의 집합(프래그먼트와 인라인 프래그먼트를 방문하는 것을 포함)을 fieldsForName이라 합니다.
  10. fieldsForName의 서로 다른 각 두 구성원 subfieldAsubfieldB에 대해:
    1. 만약 SameResponseShape(subfieldA, subfieldB)false이면, false를 반환합니다.
  11. true를 반환합니다.
참고 이전 명세 버전에서는 "composite"라는 용어가 Object, Interface 또는 Union 타입 중 하나를 나타내는 데 사용되었습니다.
설명

동일한 response name을 가진 여러 필드 선택이 실행 중에 동시에 마주치면, 실행할 필드와 인수 및 결과 값이 모호하지 않아야 합니다. 따라서 동일 객체에 대해 동시에 마주칠 수 있는 두 필드 선택은 동등한 경우에만 유효합니다.

실행 중에는 동일한 response name을 가진 필드들의 동시 실행을 위해 실행 전에 CollectSubfields()를 수행합니다.

단순 수작업으로 작성된 GraphQL에서는 이 규칙이 명백한 개발자 오류이지만, 중첩된 프래그먼트는 수동으로 이를 감지하기 어렵게 만들 수 있습니다.

다음 선택들은 올바르게 병합됩니다:

Example № 126fragment mergeIdenticalFields on Dog {
  name
  name
}

fragment mergeIdenticalAliasesAndFields on Dog {
  otherName: name
  otherName: name
}

다음은 병합할 수 없습니다:

Counter Example № 127fragment conflictingBecauseAlias on Dog {
  name: nickname
  name
}

동일한 필드는 동일한 인수를 가질 때도 병합됩니다. 값과 변수 모두 올바르게 병합될 수 있습니다.

예를 들어 다음은 올바르게 병합됩니다:

Example № 128fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
  doesKnowCommand(dogCommand: SIT)
  doesKnowCommand(dogCommand: SIT)
}

fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
  doesKnowCommand(dogCommand: $dogCommand)
  doesKnowCommand(dogCommand: $dogCommand)
}

다음은 올바르게 병합되지 않습니다:

Counter Example № 129fragment conflictingArgsOnValues on Dog {
  doesKnowCommand(dogCommand: SIT)
  doesKnowCommand(dogCommand: HEEL)
}

fragment conflictingArgsValueAndVar on Dog {
  doesKnowCommand(dogCommand: SIT)
  doesKnowCommand(dogCommand: $dogCommand)
}

fragment conflictingArgsWithVars on Dog {
  doesKnowCommand(dogCommand: $varOne)
  doesKnowCommand(dogCommand: $varTwo)
}

fragment differingArgs on Dog {
  doesKnowCommand(dogCommand: SIT)
  doesKnowCommand
}

다음 필드들은 함께 병합되지 않지만, 동일 객체에 대해 동시에 마주칠 수 없으므로 안전합니다:

Example № 130fragment safeDifferingFields on Pet {
  ... on Dog {
    volume: barkVolume
  }
  ... on Cat {
    volume: meowVolume
  }
}

fragment safeDifferingArgs on Pet {
  ... on Dog {
    doesKnowCommand(dogCommand: SIT)
  }
  ... on Cat {
    doesKnowCommand(catCommand: JUMP)
  }
}

하지만 필드 응답은 병합 가능한 형태여야 합니다. 예를 들어 리프(leaf) 타입은 서로 달라서는 안 됩니다. 다음 예에서 someValueString이거나 Int일 수 있습니다:

Counter Example № 131fragment conflictingDifferingResponses on Pet {
  ... on Dog {
    someValue: nickname
  }
  ... on Cat {
    someValue: meowVolume
  }
}

5.3.3리프 필드 선택

형식 명세
  • 문서의 각 selection에 대해:
    • 해당 selection의 래핑이 제거된 결과 타입을 selectionType이라 합니다.
    • 만약 selectionType이 스칼라 또는 열거형이라면:
      • 그 선택의 하위 선택 집합은 비어 있어야 합니다.
    • 만약 selectionType이 인터페이스, 유니언 또는 오브젝트라면:
      • 그 선택의 하위 선택 집합은 비어 있어서는 안 됩니다.
설명

리프 필드에는 하위 선택(subselection)을 허용하지 않습니다. 리프 필드는 래핑이 제거된 타입이 스칼라 또는 열거형인 필드를 말합니다.

다음은 유효합니다.

Example № 132fragment scalarSelection on Dog {
  barkVolume
}

다음은 무효입니다.

Counter Example № 133fragment scalarSelectionsNotAllowedOnInt on Dog {
  barkVolume {
    sinceWhen
  }
}

반대로, 비-리프 필드는 하위 선택을 포함해야 합니다. 비-리프 필드는 래핑이 제거된 타입이 오브젝트, 인터페이스 또는 유니언인 모든 필드입니다.

다음은 스키마의 쿼리 루트에 다음 항목들이 추가되었다고 가정합니다:

Example № 134extend type Query {
  human: Human
  pet: Pet
  catOrDog: CatOrDog
}

다음 예제들은 비-리프 필드가 하위 선택 없이 포함되어 있기 때문에 무효입니다.

Counter Example № 135query directQueryOnObjectWithoutSubFields {
  human
}

query directQueryOnInterfaceWithoutSubFields {
  pet
}

query directQueryOnUnionWithoutSubFields {
  catOrDog
}

그러나 다음 예제는 하위 선택을 포함하므로 유효합니다.

Example № 136query directQueryOnObjectWithSubFields {
  human {
    name
  }
}

5.4인자

인수는 필드와 지시문 양쪽에 제공됩니다. 다음의 검증 규칙은 두 경우 모두에 적용됩니다.

5.4.1인자 이름

형식 명세
  • 문서의 각 argument에 대해:
    • 해당 argument의 이름을 argumentName이라 합니다.
    • 부모 필드 또는 정의가 제공하는 인수 정의를 argumentDefinition이라 합니다(이때 이름은 argumentName입니다).
    • argumentDefinition는 반드시 존재해야 합니다.
설명

필드나 지시문에 제공된 모든 인수는 해당 필드나 지시문이 허용하는 인수 집합에 정의되어 있어야 합니다.

예를 들어 다음은 유효합니다:

Example № 137fragment argOnRequiredArg on Dog {
  doesKnowCommand(dogCommand: SIT)
}

fragment argOnOptional on Dog {
  isHouseTrained(atOtherHomes: true) @include(if: true)
}

다음은 무효입니다(왜냐하면 commandDogCommand에 정의되어 있지 않기 때문입니다).

Counter Example № 138fragment invalidArgName on Dog {
  doesKnowCommand(command: CLEAN_UP_HOUSE)
}

다음도 무효입니다. unless@include에 정의되어 있지 않기 때문입니다.

Counter Example № 139fragment invalidArgName on Dog {
  isHouseTrained(atOtherHomes: true) @include(unless: false)
}

더 복잡한 인수 예제를 살펴보기 위해, 타입 시스템에 다음을 추가합시다:

Example № 140type Arguments {
  multipleRequirements(x: Int!, y: Int!): Int!
  booleanArgField(booleanArg: Boolean): Boolean
  floatArgField(floatArg: Float): Float
  intArgField(intArg: Int): Int
  nonNullBooleanArgField(nonNullBooleanArg: Boolean!): Boolean!
  booleanListArgField(booleanListArg: [Boolean]!): [Boolean]
  optionalNonNullBooleanArgField(optionalBooleanArg: Boolean! = false): Boolean!
}

extend type Query {
  arguments: Arguments
}

인수의 순서는 중요하지 않습니다. 그러므로 다음 두 예제는 모두 유효합니다.

Example № 141fragment multipleArgs on Arguments {
  multipleRequirements(x: 1, y: 2)
}

fragment multipleArgsReverseOrder on Arguments {
  multipleRequirements(y: 2, x: 1)
}

5.4.2인자 고유성

필드와 지시문은 인수를 이름→값 매핑으로 취급합니다. 하나의 인수 집합 내에서 동일한 이름의 인수가 둘 이상 있으면 모호하며 무효입니다.

형식 명세
  • 문서 내의 각 argument에 대해:
    • 해당 argument의 이름을 argumentName이라 합니다.
    • 해당 argument이 포함된 인수 집합에서 이름이 argumentName인 모든 인수를 arguments라 합니다.
    • arguments는 오직 해당 argument만을 포함하는 집합이어야 합니다.

5.4.3필수 인자

  • 문서의 각 필드 또는 지시문에 대해:
    • 해당 필드 또는 지시문이 제공한 인수들을 arguments라 합니다.
    • 해당 필드 또는 지시문의 인수 정의 집합을 argumentDefinitions라 합니다.
    • argumentDefinition에 대해:
      • 해당 인수 정의의 예상 타입을 type이라 합니다.
      • 해당 인수 정의의 기본값을 defaultValue라 합니다.
      • 만약 type이 Non-Null이고 defaultValue가 존재하지 않는다면:
        • 해당 인수 정의의 이름을 argumentName이라 합니다.
        • 해당 인수 집합 arguments에서 이름이 argumentName인 인수를 argument라 합니다.
        • argument는 반드시 존재해야 합니다.
        • 해당 argument의 값을 value라 합니다.
        • value는 리터럴 null이 아니어야 합니다.
설명

인수는 필수일 수 있습니다. 인수의 타입이 non-null이며 기본값이 없으면 그 인수는 필수입니다. 그렇지 않으면 선택적입니다.

예를 들어 다음은 유효합니다:

Example № 142fragment goodBooleanArg on Arguments {
  booleanArgField(booleanArg: true)
}

fragment goodNonNullArg on Arguments {
  nonNullBooleanArgField(nonNullBooleanArg: true)
}

nullable 인수의 경우 필드에서 인수를 생략할 수 있습니다.

따라서 다음 프래그먼트는 유효합니다:

Example № 143fragment goodBooleanArgDefault on Arguments {
  booleanArgField
}

그러나 필수 인수에 대해서는 이것이 유효하지 않습니다.

Counter Example № 144fragment missingRequiredArg on Arguments {
  nonNullBooleanArgField
}

명시적으로 null을 제공하는 것도 유효하지 않습니다. 필수 인수는 항상 non-null 타입을 가지기 때문입니다.

Counter Example № 145fragment missingRequiredArg on Arguments {
  nonNullBooleanArgField(nonNullBooleanArg: null)
}

5.5프래그먼트

5.5.1프래그먼트 선언

5.5.1.1프래그먼트 이름의 고유성

형식 명세
  • 문서의 각 프래그먼트 정의 fragment에 대해:
    • 해당 fragment의 이름을 fragmentName이라 합니다.
    • 문서 내에서 이름이 fragmentName인 모든 프래그먼트 정의들을 fragments라 합니다.
    • fragments는 단일 원소로 이루어진 집합이어야 합니다.
설명

프래그먼트 정의는 프래그먼트 스프레드에서 이름으로 참조됩니다. 모호성을 피하기 위해 각 프래그먼트 이름은 문서 내에서 유일해야 합니다.

인라인 프래그먼트는 프래그먼트 정의로 간주되지 않으며 이 검증 규칙의 영향을 받지 않습니다.

예를 들어 다음 문서는 유효합니다:

Example № 146{
  dog {
    ...fragmentOne
    ...fragmentTwo
  }
}

fragment fragmentOne on Dog {
  name
}

fragment fragmentTwo on Dog {
  owner {
    name
  }
}

반면 다음 문서는 무효입니다:

Counter Example № 147{
  dog {
    ...fragmentOne
  }
}

fragment fragmentOne on Dog {
  name
}

fragment fragmentOne on Dog {
  owner {
    name
  }
}

5.5.1.2프래그먼트 스프레드 타입 존재

형식 명세
  • 문서의 각 명명된 스프레드 namedSpread에 대해:
    • 해당 namedSpread의 대상인 프래그먼트를 fragment라 합니다.
    • 그 프래그먼트의 대상 타입은 스키마에 정의되어 있어야 합니다.
설명

프래그먼트는 스키마에 존재하는 타입에 대해 명시되어야 합니다. 이는 명명된 프래그먼트와 인라인 프래그먼트 모두에 적용됩니다. 스키마에 정의되어 있지 않다면 해당 프래그먼트는 무효입니다.

예를 들어 다음 프래그먼트들은 유효합니다:

Example № 148fragment correctType on Dog {
  name
}

fragment inlineFragment on Dog {
  ... on Dog {
    name
  }
}

fragment inlineFragment2 on Dog {
  ... @include(if: true) {
    name
  }
}

다음은 유효하지 않습니다:

Counter Example № 149fragment notOnExistingType on NotInSchema {
  name
}

fragment inlineNotExistingType on Dog {
  ... on NotInSchema {
    name
  }
}

5.5.1.3오브젝트, 인터페이스 또는 유니언 타입에 대한 프래그먼트

형식 명세
  • 문서에 정의된 각 프래그먼트 fragment에 대해:
    • 프래그먼트의 대상 타입은 UNION, INTERFACE 또는 OBJECT이어야 합니다.
설명

프래그먼트는 유니언, 인터페이스 및 오브젝트에만 선언할 수 있습니다. 스칼라에는 선언할 수 없습니다. 또한 리프가 아닌 필드에만 적용할 수 있습니다. 이 규칙은 인라인 및 명명된 프래그먼트 모두에 적용됩니다.

다음 프래그먼트 선언들은 유효합니다:

Example № 150fragment fragOnObject on Dog {
  name
}

fragment fragOnInterface on Pet {
  name
}

fragment fragOnUnion on CatOrDog {
  ... on Dog {
    name
  }
}

다음은 무효입니다:

Counter Example № 151fragment fragOnScalar on Int {
  something
}

fragment inlineFragOnScalar on Dog {
  ... on Boolean {
    somethingElse
  }
}

5.5.1.4프래그먼트는 사용되어야 함

형식 명세
  • 문서에 정의된 각 프래그먼트 fragment는:
    • fragment가 문서 내에서 최소 하나의 스프레드의 대상이어야 합니다.
설명

정의된 프래그먼트는 문서 내에서 사용되어야 합니다.

예를 들어 다음은 무효 문서입니다:

Counter Example № 152fragment nameFragment on Dog { # unused
  name
}

{
  dog {
    name
  }
}

5.5.2프래그먼트 스프레드

필드 선택은 프래그먼트를 서로 스프레드하여 결정되기도 합니다. 대상 프래그먼트의 선택 집합은 그 프래그먼트가 참조된 레벨의 선택 집합에 결합됩니다.

5.5.2.1프래그먼트 스프레드 대상 정의됨

형식 명세
  • 문서의 모든 namedSpread에 대해:
    • 해당 namedSpread의 대상인 프래그먼트를 fragment라 합니다.
    • fragment는 문서에 정의되어 있어야 합니다.
설명

명명된 프래그먼트 스프레드는 문서 내에 정의된 프래그먼트를 참조해야 합니다. 스프레드의 대상이 정의되어 있지 않으면 검증 오류입니다.

Counter Example № 153{
  dog {
    ...undefinedFragment
  }
}

5.5.2.2프래그먼트 스프레드는 사이클을 형성하면 안 됨

형식 명세
  • 문서의 각 프래그먼트 정의 fragmentDefinition에 대해:
    • visited를 빈 집합으로 초기화합니다.
    • DetectFragmentCycles(fragmentDefinition, visited)를 호출합니다.
DetectFragmentCycles(fragmentDefinition, visited)
  1. 해당 fragmentDefinition의 모든 프래그먼트 스프레드 하위 요소들을 spreads라 합니다.
  2. spread에 대해:
    1. visitedspread를 포함하고 있어서는 안 됩니다.
    2. nextVisitedspreadvisited의 합집합으로 둡니다.
    3. nextFragmentDefinitionspread의 대상 프래그먼트로 둡니다.
    4. DetectFragmentCycles(nextFragmentDefinition, nextVisited)를 재귀적으로 호출합니다.
설명

프래그먼트 스프레드의 그래프는 자기 자신을 포함한 어떤 사이클도 형성해서는 안 됩니다. 그렇지 않으면 연산이 무한히 스프레드되거나 순환 데이터에서 무한히 실행될 수 있습니다.

다음은 무한 스프레드를 초래하는 프래그먼트를 무효로 만듭니다:

Counter Example № 154{
  dog {
    ...nameFragment
  }
}

fragment nameFragment on Dog {
  name
  ...barkVolumeFragment
}

fragment barkVolumeFragment on Dog {
  barkVolume
  ...nameFragment
}

위 프래그먼트들을 인라인하면 다음과 같이 무한히 커지는 결과가 됩니다:

Example № 155{
  dog {
    name
    barkVolume
    name
    barkVolume
    name
    barkVolume
    name
    # forever...
  }
}

이 규칙은 순환 데이터에 대해 실행할 때 무한 재귀를 일으키는 프래그먼트도 무효로 만듭니다:

Counter Example № 156{
  dog {
    ...dogFragment
  }
}

fragment dogFragment on Dog {
  name
  owner {
    ...ownerFragment
  }
}

fragment ownerFragment on Human {
  name
  pets {
    ...dogFragment
  }
}

5.5.2.3프래그먼트 스프레드가 가능해야 함

형식 명세
  • 문서에 정의된 각 스프레드(명명된 것이든 인라인이든) spread에 대해:
    • 해당 spread의 대상 프래그먼트를 fragment라 합니다.
    • 프래그먼트의 타입 조건을 fragmentType이라 합니다.
    • 스프레드가 포함된 선택 집합의 타입을 parentType이라 합니다.
    • applicableTypesGetPossibleTypes(fragmentType)GetPossibleTypes(parentType)의 교집합으로 정의합니다.
    • applicableTypes는 비어 있으면 안 됩니다.
GetPossibleTypes(type)
  1. 만약 type이 오브젝트 타입이면, 그 타입 자체를 포함하는 집합을 반환합니다.
  2. 만약 type이 인터페이스 타입이면, 그 인터페이스를 구현하는 타입들의 집합을 반환합니다.
  3. 만약 type이 유니언 타입이면, 그 유니언의 가능한 타입들의 집합을 반환합니다.
설명

프래그먼트는 특정 타입에 선언되며 런타임 객체 타입이 타입 조건과 일치할 때만 적용됩니다. 또한 스프레드는 부모 타입의 문맥 내에서 발생합니다. 스프레드가 유효하려면 그 타입 조건이 부모 타입 내에서 언제든지 적용될 수 있어야 합니다.

5.5.2.3.1오브젝트 범위에서의 오브젝트 스프레드

오브젝트 타입의 범위에서는, 유효한 오브젝트 타입 프래그먼트 스프레드는 그 범위에 있는 동일한 타입에 적용되는 것만 허용됩니다.

예를 들어

Example № 157fragment dogFragment on Dog {
  ... on Dog {
    barkVolume
  }
}

다음은 무효입니다

Counter Example № 158fragment catInDogFragmentInvalid on Dog {
  ... on Cat {
    meowVolume
  }
}
5.5.2.3.2오브젝트 범위에서의 추상 스프레드

오브젝트 타입의 범위에서는, 해당 오브젝트가 인터페이스를 구현하거나 유니언의 멤버인 경우 유니언 또는 인터페이스 스프레드를 사용할 수 있습니다.

예를 들어

Example № 159fragment petNameFragment on Pet {
  name
}

fragment interfaceWithinObjectFragment on Dog {
  ...petNameFragment
}

이는 DogPet을 구현하기 때문에 유효합니다.

마찬가지로

Example № 160fragment catOrDogNameFragment on CatOrDog {
  ... on Cat {
    meowVolume
  }
}

fragment unionWithObjectFragment on Dog {
  ...catOrDogNameFragment
}

이는 DogCatOrDog 유니언의 멤버이기 때문에 유효합니다. 참고 만약 CatOrDogNameFragment의 내용을 들여다보면 어떤 유효한 결과도 반환하지 않을 것 같지만, 우리는 프래그먼트 선언 자체만 고려하므로 이를 무효로 규정하지 않습니다.

5.5.2.3.3추상 범위에서의 오브젝트 스프레드

유니언 또는 인터페이스 스프레드는 오브젝트 타입 프래그먼트 문맥 내에서 사용할 수 있지만, 그 오브젝트 타입이 해당 인터페이스나 유니언의 가능한 타입들 중 하나여야 합니다.

예를 들어, 다음 프래그먼트들은 유효합니다:

Example № 161fragment petFragment on Pet {
  name
  ... on Dog {
    barkVolume
  }
}

fragment catOrDogFragment on CatOrDog {
  ... on Cat {
    meowVolume
  }
}

petFragmentDog가 인터페이스 Pet를 구현하므로 유효합니다. catOrDogFragmentCatCatOrDog 유니언의 멤버이므로 유효합니다.

대조적으로 다음 프래그먼트들은 무효입니다:

Counter Example № 162fragment sentientFragment on Sentient {
  ... on Dog {
    barkVolume
  }
}

fragment humanOrAlienFragment on HumanOrAlien {
  ... on Cat {
    meowVolume
  }
}

Dog는 인터페이스 Sentient를 구현하지 않으므로 sentientFragment는 의미 있는 결과를 반환할 수 없습니다. 따라서 무효입니다. 마찬가지로 Cat는 유니언 HumanOrAlien의 멤버가 아니므로 무효입니다.

5.5.2.3.4추상 범위에서의 추상 스프레드

인터페이스나 유니언 프래그먼트는 서로 내에서 사용할 수 있습니다. 범위와 스프레드의 가능한 타입들의 교집합에 적어도 하나의 오브젝트 타입이 존재하면 스프레드는 유효하다고 간주됩니다.

예를 들어

Example № 163fragment unionWithInterface on Pet {
  ...dogOrHumanFragment
}

fragment dogOrHumanFragment on DogOrHuman {
  ... on Dog {
    barkVolume
  }
}

이는 Dog가 인터페이스 Pet를 구현하고 DogOrHuman의 멤버이기 때문에 유효합니다.

그러나

Counter Example № 164fragment nonIntersectingInterfaces on Pet {
  ...sentientFragment
}

fragment sentientFragment on Sentient {
  name
}

위 예제는 유효하지 않습니다. 왜냐하면 PetSentient 둘 다를 구현하는 타입이 존재하지 않기 때문입니다.

구현된 인터페이스 범위 내의 인터페이스 스프레드

추가로, 인터페이스 타입 프래그먼트는 그것이 구현하는 인터페이스 범위 내에 항상 스프레드될 수 있습니다.

아래 예제에서 ...resourceFragment 스프레드는 ResourceNode를 구현하므로 유효합니다.

Example № 165interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

fragment interfaceWithInterface on Node {
  ...resourceFragment
}

fragment resourceFragment on Resource {
  url
}

5.6

5.6.1올바른 타입의 값

형식 명세
  • 문서의 각 리터럴 입력 값 value에 대해:
    • 해당 value가 위치한 자리에서 기대되는 타입을 type이라 합니다.
    • valuetype으로 강제 변환(coercible) 가능해야 합니다(이때 value 내에 중첩된 모든 variableUsage는 해당 위치에서 런타임에 유효한 값으로 대체된다고 가정합니다).
설명

리터럴 값은 타입 시스템 장에서 정의된 강제 변환 규칙에 따라 그 값이 위치한 자리에서 기대되는 타입과 호환되어야 합니다.

참고 ListValueObjectValue는 중첩된 입력 값들을 포함할 수 있으며, 그 일부는 변수 사용(variable usage)일 수 있습니다. All Variable Usages Are Allowed 검증 규칙은 각 variableUsage가 그 위치에서 허용된 타입임을 보장합니다. Coercing Variable Values 알고리즘은 변수의 런타임 값들이 올바르게 강제 변환됨을 보장합니다. 그러므로 이 검증 규칙의 "강제 변환 가능(coercible)" 주장에서는 각 variableUsage의 런타임 값이 그 위치에서 사용하기에 유효하다고 가정할 수 있습니다.

위치에서 기대되는 타입에는 값이 제공되는 인수(argument)에 정의된 타입, 값이 제공되는 입력 객체 필드에 정의된 타입, 그리고 기본값이 제공되는 변수 정의의 타입이 포함됩니다.

다음 예제들은 값 리터럴의 올바른 사용 예입니다:

Example № 166fragment goodBooleanArg on Arguments {
  booleanArgField(booleanArg: true)
}

fragment coercedIntIntoFloatArg on Arguments {
  # Note: The input coercion rules for Float allow Int literals.
  floatArgField(floatArg: 123)
}

query goodComplexDefaultValue($search: FindDogInput = { name: "Fido" }) {
  findDog(searchBy: $search) {
    name
  }
}

mutation addPet($pet: PetInput! = { cat: { name: "Brontie" } }) {
  addPet(pet: $pet) {
    name
  }
}

문자열을 정수로 강제 변환하는 것처럼 강제 변환 불가능한 값은 무효입니다. 다음 예제들은 무효입니다:

Counter Example № 167fragment stringIntoInt on Arguments {
  intArgField(intArg: "123")
}

query badComplexValue {
  findDog(searchBy: { name: 123 }) {
    name
  }
}

mutation oneOfWithNoFields {
  addPet(pet: {}) {
    name
  }
}

mutation oneOfWithTwoFields($dog: DogInput) {
  addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) {
    name
  }
}

mutation listOfOneOfWithNullableVariable($dog: DogInput) {
  addPets(pets: [{ dog: $dog }]) {
    name
  }
}

5.6.2입력 객체 필드 이름

형식 명세
  • 문서의 각 입력 객체 필드 inputField에 대해:
    • 해당 inputField의 이름을 inputFieldName이라 합니다.
    • 해당 부모 입력 객체 타입에 의해 제공되는 입력 필드 정의를 inputFieldDefinition이라 합니다(이때 이름은 inputFieldName입니다).
    • inputFieldDefinition는 반드시 존재해야 합니다.
설명

입력 객체 값에 제공된 모든 입력 필드는 그 입력 객체의 기대 타입이 허용하는 가능한 필드 집합에 정의되어 있어야 합니다.

예를 들어 다음 입력 객체 예제는 유효합니다:

Example № 168{
  findDog(searchBy: { name: "Fido" }) {
    name
  }
}

다음 입력 객체 예제는 기대 타입에 정의되지 않은 필드 "favoriteCookieFlavor"를 사용하고 있으므로 무효입니다:

Counter Example № 169{
  findDog(searchBy: { favoriteCookieFlavor: "Bacon" }) {
    name
  }
}

5.6.3입력 객체 필드 고유성

형식 명세
  • 문서의 각 입력 객체 값 inputObject에 대해:
    • 해당 inputObject의 각 입력 필드 inputField에 대해:
      • 해당 inputField의 이름을 name이라 합니다.
      • 해당 inputObject에서 이름이 name인 모든 입력 객체 필드들을 fields라 합니다.
      • fields는 오직 해당 inputField만을 포함하는 집합이어야 합니다.
설명

입력 객체는 동일한 이름의 필드를 둘 이상 포함해서는 안 됩니다. 그렇지 않으면 문법의 일부가 무시되는 모호성이 발생합니다.

예를 들어 다음 문서는 검증을 통과하지 못합니다.

Counter Example № 170{
  field(arg: { field: true, field: false })
}

5.6.4입력 객체 필수 필드

형식 명세
  • 문서의 각 입력 객체에 대해:
    • 해당 입력 객체가 제공한 필드들을 fields라 합니다.
    • 해당 입력 객체의 입력 필드 정의 집합을 fieldDefinitions라 합니다.
    • fieldDefinition에 대해:
      • 해당 필드 정의의 기대 타입을 type이라 합니다.
      • 해당 필드 정의의 기본값을 defaultValue라 합니다.
      • 만약 type이 Non-Null이고 defaultValue가 존재하지 않으면:
        • 해당 필드 정의의 이름을 fieldName이라 합니다.
        • 해당 입력 객체의 필드들(fields)에서 이름이 fieldName인 입력 필드를 field라 합니다.
        • field는 반드시 존재해야 합니다.
        • 해당 field의 값을 value라 합니다.
        • value는 리터럴 null이 아니어야 합니다.
설명

입력 객체 필드는 필수일 수 있습니다. 필드의 인수가 필수일 수 있는 것과 마찬가지로, 입력 객체는 필수 필드를 가질 수 있습니다. 입력 필드는 타입이 non-null이고 기본값이 없으면 필수이며, 그렇지 않으면 선택적입니다.

5.7지시문

5.7.1지시문 정의됨

형식 명세
  • 문서의 모든 directive에 대해:
    • 해당 directive의 이름을 directiveName이라 합니다.
    • 이름이 directiveName인 지시문 정의를 directiveDefinition이라 합니다.
    • directiveDefinition는 반드시 존재해야 합니다.
설명

GraphQL 서비스는 지원하는 지시문을 정의합니다. 지시문을 사용할 때마다 해당 지시문이 서비스에서 사용 가능해야 합니다.

5.7.2지시문이 유효한 위치에 있음

형식 명세
  • 문서의 모든 directive에 대해:
    • 해당 directive의 이름을 directiveName이라 합니다.
    • 이름이 directiveName인 지시문 정의를 directiveDefinition이라 합니다.
    • 해당 directiveDefinition의 유효한 위치들을 locations라 합니다.
    • 해당 지시문이 영향을 미치는 AST 노드를 adjacent라 합니다.
    • adjacentlocations 내의 항목으로 표현되어야 합니다.
설명

GraphQL 서비스는 어떤 지시문을 지원하며 어디에서 지원하는지 정의합니다. 지시문을 사용할 때마다 서비스가 해당 위치에서 그 지시문을 지원한다고 선언했는지 확인해야 합니다.

예를 들어 다음 문서는 @skipQUERY를 유효한 위치로 제공하지 않으므로 검증을 통과하지 못합니다.

Counter Example № 171query @skip(if: $foo) {
  field
}

5.7.3위치별 지시문 고유성

형식 명세
  • 문서에서 지시문이 적용될 수 있는 각 location에 대해:
    • 해당 location에 적용되고 repeatable이 아닌 지시문들의 집합을 directives라 합니다.
    • directives의 각 directive에 대해:
      • 해당 directive의 이름을 directiveName이라 합니다.
      • 이름이 directiveName인 모든 지시문들을 namedDirectives라 합니다.
      • namedDirectives는 하나의 원소로 이루어진 집합이어야 합니다.
설명

GraphQL은 repeatable로 정의된 지시문이 동일한 정의에 대해 여러 번 사용되는 것을 허용합니다(각기 다른 인수와 함께). 반면 repeatable이 아닌 지시문은 위치별로 한 번만 사용되어야 합니다.

예를 들어 다음 문서는 동일한 필드에 대해 non-repeatable인 @skip가 두 번 사용되었으므로 검증을 통과하지 못합니다.

Counter Example № 172query ($foo: Boolean = true, $bar: Boolean = false) {
  field @skip(if: $foo) @skip(if: $bar)
}

그러나 다음 예제는 유효합니다. 비록 연산 내에서 그리고 동일한 이름의 필드에 대해 @skip가 두 번 사용되었지만, 각 사용이 위치별로는 한 번만 사용되었기 때문입니다.

Example № 173query ($foo: Boolean = true, $bar: Boolean = false) {
  field @skip(if: $foo) {
    subfieldA
  }
  field @skip(if: $bar) {
    subfieldB
  }
}

5.8변수

5.8.1변수 이름의 고유성

형식 명세
  • 문서의 모든 operation에 대하여:
    • operation에 정의된 모든 variable에 대하여:
      • 해당 variable의 이름을 variableName이라 합니다.
      • 문서의 해당 operation에서 이름이 variableName인 모든 변수들의 집합을 variables라 합니다.
      • variables는 단 하나의 원소로 이루어진 집합이어야 합니다.
설명

어떤 연산이 동일한 이름의 변수를 둘 이상 정의하면 모호하므로 무효입니다. 중복 변수의 타입이 동일하더라도 무효입니다.

Counter Example № 174query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

여러 연산이 같은 이름의 변수를 정의하는 것은 허용됩니다. 동일 프래그먼트를 참조하는 두 연산이 각각 변수 정의를 필요로 하는 경우가 있을 수 있습니다:

Example № 175query A($atOtherHomes: Boolean) {
  ...HouseTrainedFragment
}

query B($atOtherHomes: Boolean) {
  ...HouseTrainedFragment
}

fragment HouseTrainedFragment on Query {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

5.8.2변수는 입력 타입이어야 함

형식 명세
  • 문서의 모든 operation에 대하여:
    • operation의 각 variable에 대해:
      • 해당 variable의 타입을 variableType이라 합니다.
      • IsInputType(variableType)true여야 합니다.
설명

변수는 입력 타입만 될 수 있습니다. 오브젝트, 유니언, 인터페이스는 입력으로 사용할 수 없습니다.

다음 예제들을 위해 아래 타입 시스템 확장을 고려하십시오:

Example № 176extend type Query {
  booleanList(booleanListArg: [Boolean!]): Boolean
}

다음 연산들은 유효합니다:

Example № 177query takesBoolean($atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

query takesComplexInput($search: FindDogInput) {
  findDog(searchBy: $search) {
    name
  }
}

query TakesListOfBooleanBang($booleans: [Boolean!]) {
  booleanList(booleanListArg: $booleans)
}

다음 연산들은 무효입니다:

Counter Example № 178query takesCat($cat: Cat) {
  # ...
}

query takesDogBang($dog: Dog!) {
  # ...
}

query takesListOfPet($pets: [Pet]) {
  # ...
}

query takesCatOrDog($catOrDog: CatOrDog) {
  # ...
}

5.8.3모든 변수 사용이 정의되어 있음

형식 명세
  • 문서의 모든 operation에 대하여:
    • 해당 연산의 문맥에서 사용되는 각 variableUsage에 대해, 그 변수는 operation의 변수 목록에 있어야 합니다.
    • 해당 operation이 참조하는 모든 프래그먼트를 fragments라 합니다(전이적 참조 포함).
    • fragments의 각 fragment에 대해:
      • 해당 프래그먼트 문맥에서 사용하는 각 variableUsage는 반드시 그 operation의 변수 목록에 있어야 합니다.
설명

변수는 연산 단위로 스코프됩니다. 즉, 어떤 연산의 문맥 내에서 사용되는 변수는 그 연산의 최상위에서 정의되어야 합니다.

예를 들면:

Example № 179query variableIsDefined($atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

위 예제는 유효합니다. $atOtherHomes은 해당 연산에서 정의되어 있습니다.

반대로 다음 문서는 무효입니다:

Counter Example № 180query variableIsNotDefined {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

$atOtherHomes는 해당 연산에 의해 정의되어 있지 않습니다.

프래그먼트는 이 규칙을 복잡하게 만듭니다. 연산이 전이적으로 포함하는 모든 프래그먼트는 그 연산에서 정의한 변수를 접근할 수 있습니다. 프래그먼트는 여러 연산에서 사용될 수 있으므로, 프래그먼트 내부의 변수 사용은 그 프래그먼트를 사용하는 모든 연산에서 해당 변수 정의가 존재해야 합니다.

예를 들어 다음은 유효합니다:

Example № 181query variableIsDefinedUsedInSingleFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

이는 isHouseTrainedFragment가 해당 연산 variableIsDefinedUsedInSingleFragment의 문맥에서 사용되며, 그 연산이 변수를 정의하기 때문입니다.

반면, 어떤 프래그먼트가 변수를 참조하지만 그 변수를 정의하지 않는 연산 내에 포함되면 문서는 무효입니다.

Counter Example № 182query variableIsNotDefinedUsedInSingleFragment {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

이 규칙은 전이적으로도 적용되므로 다음 예제도 실패합니다:

Counter Example № 183query variableIsNotDefinedUsedInNestedFragment {
  dog {
    ...outerHouseTrainedFragment
  }
}

fragment outerHouseTrainedFragment on Dog {
  ...isHouseTrainedFragment
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

변수는 프래그먼트가 사용되는 모든 연산에서 정의되어야 합니다.

Example № 184query houseTrainedQueryOne($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query houseTrainedQueryTwo($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

그러나 다음 문서는 검증되지 않습니다:

Counter Example № 185query houseTrainedQueryOne($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query houseTrainedQueryTwoNotDefined {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

이는 houseTrainedQueryTwoNotDefined가 $atOtherHomes를 정의하지 않기 때문입니다. 그러나 그 변수는 포함된 isHouseTrainedFragment에서 사용되고 있습니다.

5.8.4모든 변수 사용

형식 명세
  • 문서의 모든 operation에 대하여:
    • 해당 operation이 정의한 변수 집합을 variables라 합니다.
    • variables의 각 variable는 그 연산 자체의 문맥이나 그 연산이 전이적으로 참조하는 프래그먼트 중 적어도 하나에서 적어도 한 번 사용되어야 합니다.
설명

연산이 정의한 모든 변수는 해당 연산이나 그 연산이 전이적으로 포함하는 프래그먼트에서 사용되어야 합니다. 사용되지 않는 변수는 검증 오류를 일으킵니다.

예를 들어 다음은 무효입니다:

Counter Example № 186query variableUnused($atOtherHomes: Boolean) {
  dog {
    isHouseTrained
  }
}

왜냐하면 $atOtherHomes가 참조되지 않았기 때문입니다.

이 규칙은 전이적 프래그먼트 스프레드에도 적용됩니다:

Example № 187query variableUsedInFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

위 예제는 유효합니다. $atOtherHomesisHouseTrainedFragment에서 사용되며, 그 프래그먼트는 연산 variableUsedInFragment에 의해 포함되기 때문입니다.

만약 그 프래그먼트가 $atOtherHomes를 사용하지 않는다면 무효가 됩니다:

Counter Example № 188query variableNotUsedWithinFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedWithoutVariableFragment
  }
}

fragment isHouseTrainedWithoutVariableFragment on Dog {
  isHouseTrained
}

문서 내 모든 연산은 자신이 정의한 모든 변수를 사용해야 합니다.

따라서 다음 문서는 검증되지 않습니다.

Counter Example № 189query queryWithUsedVar($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query queryWithExtraVar($atOtherHomes: Boolean, $extra: Int) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

이 문서는 queryWithExtraVar가 불필요한 변수를 정의하고 있기 때문에 유효하지 않습니다.

5.8.5모든 변수 사용은 허용되어야 함

형식 명세
  • 문서의 모든 operation에 대하여:
    • 해당 operation에 전이적으로 포함된 모든 변수 사용들을 variableUsages라 합니다.
    • variableUsages의 각 variableUsage에 대해:
      • 해당 variableUsage의 이름을 variableName이라 합니다.
      • 해당 이름을 가진 VariableDefinition을 그 operation 내에서 찾습니다(이를 variableDefinition이라 합니다).
      • IsVariableUsageAllowed(variableDefinition, variableUsage)true여야 합니다.
IsVariableUsageAllowed(variableDefinition, variableUsage)
  1. 해당 variableDefinition의 기대 타입을 variableType이라 합니다.
  2. 해당 variableUsage가 위치한 Argument, ObjectField 또는 ListValue 항목의 기대 타입을 locationType이라 합니다.
  3. 만약 IsNonNullPosition(locationType, variableUsage)가 참이고 variableType이 non-null 타입이 아니라면:
    1. hasNonNullVariableDefaultValuevariableDefinition에 기본값이 존재하고 그 값이 null이 아닌 경우 true입니다.
    2. hasLocationDefaultValue는 해당 variableUsage가 위치한 Argument 또는 ObjectField에 기본값이 존재하는 경우 true입니다.
    3. 만약 hasNonNullVariableDefaultValue도 아니고 hasLocationDefaultValue도 아니라면, false를 반환합니다.
    4. nullableLocationTypelocationType의 래핑이 제거된(nullable) 타입으로 둡니다.
    5. AreTypesCompatible(variableType, nullableLocationType)의 결과를 반환합니다.
  4. 그 외의 경우 AreTypesCompatible(variableType, locationType)의 결과를 반환합니다.
IsNonNullPosition(locationType, variableUsage)
  1. 만약 locationType이 non-null 타입이면 true를 반환합니다.
  2. 만약 variableUsage의 위치가 ObjectField라면:
    1. parentObjectValue를 그 ObjectValue로 둡니다.
    2. parentLocationType를 그 ObjectValue의 기대 타입으로 둡니다.
    3. 만약 parentLocationTypeOneOf Input Object 타입이면 true를 반환합니다.
  3. false를 반환합니다.
AreTypesCompatible(variableType, locationType)
  1. 만약 locationType이 non-null 타입이면:
    1. 만약 variableType이 non-null 타입이 아니면 false를 반환합니다.
    2. nullableLocationTypelocationType의 래핑이 제거된 타입으로 둡니다.
    3. nullableVariableTypevariableType의 래핑이 제거된 타입으로 둡니다.
    4. AreTypesCompatible(nullableVariableType, nullableLocationType)의 결과를 반환합니다.
  2. 그렇지 않고 variableType이 non-null 타입이면:
    1. nullableVariableTypevariableType의 nullable 타입으로 둡니다.
    2. AreTypesCompatible(nullableVariableType, locationType)의 결과를 반환합니다.
  3. 그렇지 않고 locationType이 리스트 타입이면:
    1. 만약 variableType이 리스트 타입이 아니면 false를 반환합니다.
    2. itemLocationTypelocationType의 항목 타입으로 둡니다.
    3. itemVariableTypevariableType의 항목 타입으로 둡니다.
    4. AreTypesCompatible(itemVariableType, itemLocationType)의 결과를 반환합니다.
  4. 그렇지 않고 variableType이 리스트 타입이면 false를 반환합니다.
  5. 그 외에는 variableTypelocationType이 동일하면 true를, 그렇지 않으면 false를 반환합니다.
설명

변수 사용은 해당 변수가 전달되는 인수들과 호환되어야 합니다.

타입이 완전히 일치하지 않거나, nullable 타입 변수가 non-null 인수 타입으로 전달되는 경우 검증 실패가 발생합니다.

타입은 일치해야 합니다:

Counter Example № 190query intCannotGoIntoBoolean($intArg: Int) {
  arguments {
    booleanArgField(booleanArg: $intArg)
  }
}

$intArgInt 타입이므로 Boolean 타입인 booleanArg에 사용할 수 없습니다.

리스트의 원소 개수 등 목록 관련 제약도 동일하게 적용됩니다. 예를 들어 리스트를 단일 값에 전달할 수 없습니다.

Counter Example № 191query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) {
  arguments {
    booleanArgField(booleanArg: $booleanListArg)
  }
}

null 허용성도 지켜져야 합니다. 일반적으로 nullable 변수는 non-null 인수에 전달될 수 없습니다.

Counter Example № 192query booleanArgQuery($booleanArg: Boolean) {
  arguments {
    nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
  }
}

리스트 타입의 경우 바깥층과 안쪽 원소의 null 허용성 규칙이 모두 적용됩니다. nullable 리스트는 non-null 리스트로 전달될 수 없고, nullable 원소의 리스트는 non-null 원소의 리스트로 전달될 수 없습니다. 다음은 유효합니다:

Example № 193query nonNullListToList($nonNullBooleanList: [Boolean]!) {
  arguments {
    booleanListArgField(booleanListArg: $nonNullBooleanList)
  }
}

하지만 nullable 리스트는 non-null 리스트로 전달할 수 없습니다:

Counter Example № 194query listToNonNullList($booleanList: [Boolean]) {
  arguments {
    nonNullBooleanListField(nonNullBooleanListArg: $booleanList)
  }
}

이는 [T][T]!에 전달할 수 없기 때문입니다. 마찬가지로 [T][T!]에 전달할 수 없습니다.

OneOf 입력 객체 필드에 사용되는 변수는 non-null이어야 합니다.

Example № 195mutation addCat($cat: CatInput!) {
  addPet(pet: { cat: $cat }) {
    name
  }
}

mutation addCatWithDefault($cat: CatInput! = { name: "Brontie" }) {
  addPet(pet: { cat: $cat }) {
    name
  }
}
Counter Example № 196mutation addNullableCat($cat: CatInput) {
  addPet(pet: { cat: $cat }) {
    name
  }
}
기본값이 존재할 때 선택적 변수를 허용함

일반적인 변수 타입 호환성의 주목할 만한 예외는, 변수 정의가 nullable 타입이더라도 그 변수나 해당 위치 중 하나가 기본값을 제공하면 non-null 위치에 제공되는 것을 허용하는 것입니다.

아래 예제에서는 선택적 변수 $booleanArg가 필드 인수 optionalBooleanArg의 non-null 위치에 사용되는 것이 허용됩니다. 그 이유는 해당 필드 인수가 스키마에서 기본값을 제공하여 선택적이기 때문입니다.

Example № 197query booleanArgQueryWithDefault($booleanArg: Boolean) {
  arguments {
    optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
  }
}

아래 예제에서는 선택적 변수 $booleanArg가 non-null 인수 nonNullBooleanArg에 사용되는 것이 허용됩니다. 그 이유는 해당 변수가 연산에서 기본값을 제공하기 때문입니다. 이 동작은 이전 명세와의 호환성을 위해 명시적으로 지원됩니다. GraphQL 도구는 이것을 경고로 표시하고 BooleanBoolean!로 바꾸도록 권장할 수 있습니다.

Example № 198query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
  arguments {
    nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
  }
}
참고 런타임에 이러한 변수에 대해 값 null이 제공될 수 있습니다. non-null 인수에 null이 제공되면 실행 중에 execution error가 발생해야 합니다.

6실행

GraphQL 서비스는 실행을 통해 요청으로부터 응답을 생성합니다.

실행을 위한 요청은 몇 가지 정보로 구성됩니다:

이 정보를 사용하면 ExecuteRequest(schema, document, operationName, variableValues, initialValue)의 결과가 응답을 생성하며, 아래의 응답(Response) 섹션에 따라 포맷됩니다.

구현체는 향후 GraphQL 명세 버전과 충돌할 수 있는 추가 속성을 request에 임의로 추가해서는 안 됩니다. 대신 구현별 추가 정보는 예약된 위치인 extensions에 제공해야 합니다. 존재하는 경우 extensions는 맵이어야 하지만 그 내용에 대한 추가 제한은 없습니다. 충돌을 피하기 위해 키는 고유한 접두어를 사용해야 합니다.

참고 GraphQL 요청은 특정 직렬화 형식이나 전송 메커니즘을 요구하지 않습니다. 메시지 직렬화 및 전송 방법은 구현 서비스에서 선택해야 합니다.
참고 실행 가능한 문서들(연산 정의, 프래그먼트 정의, 변수 정의)의 설명과 주석은 실행 중에 무시되어야 하며 GraphQL 문서의 관찰 가능한 실행, 검증 또는 응답에 영향을 주어서는 안 됩니다. 실행 가능한 문서에 대한 설명과 주석은 로깅이나 개발자 도구와 같은 비관찰 목적에는 사용될 수 있습니다.

6.1요청 실행

요청을 실행하려면 실행기는 파싱된 Document와, 문서가 여러 연산을 정의하는 경우 실행할 연산 이름을 알고 있어야 합니다. 문서에 단 하나의 연산만 포함되어 있다면 별도의 연산 이름 선택은 필요하지 않습니다. 요청의 결과는 아래 "연산 실행(Executing Operations)" 섹션에 따라 해당 연산을 실행한 결과로 결정됩니다.

ExecuteRequest(schema, document, operationName, variableValues, initialValue)
  1. operationGetOperation(document, operationName)의 결과입니다.
  2. coercedVariableValuesCoerceVariableValues(schema, operation, variableValues)의 결과입니다.
  3. 만약 operation이 쿼리 연산이라면:
    1. ExecuteQuery(operation, schema, coercedVariableValues, initialValue)을 반환합니다.
  4. 그렇지 않고 operation이 뮤테이션 연산이라면:
    1. ExecuteMutation(operation, schema, coercedVariableValues, initialValue)을 반환합니다.
  5. 그렇지 않고 operation이 구독 연산이라면:
    1. Subscribe(operation, schema, coercedVariableValues, initialValue)을 반환합니다.
GetOperation(document, operationName)
  1. 만약 operationNamenull이면:
    1. 만약 document가 정확히 하나의 연산을 포함하고 있다면:
      1. 문서에 포함된 그 연산을 반환합니다.
    2. 그렇지 않으면 operationName을 요구하는 request error를 발생시킵니다.
  2. 그렇지 않으면:
    1. 문서에서 이름이 operationName인 연산을 operation이라 합니다.
    2. 만약 해당 operation을 찾을 수 없다면 request error를 발생시킵니다.
    3. operation을 반환합니다.

6.1.1요청 검증

검증 섹션에서 설명한 바와 같이, 모든 검증 규칙을 통과한 요청만 실행되어야 합니다. 검증 오류가 있는 경우 응답의 "errors" 리스트에 보고되어야 하며 요청은 실행 없이 실패해야 합니다.

일반적으로 검증은 실행 직전에 요청 문맥에서 수행되지만, 동일한 요청이 이전에 정확히 검증된 것으로 알려져 있으면 즉시 검증하지 않고도 요청을 실행할 수 있습니다. GraphQL 서비스는 어떠한 시점에서든 검증 오류가 없다고 알려진 요청만 실행해야 하며 그 이후 요청이 변경되지 않아야 합니다.

예를 들어: 요청이 개발 중에 검증되었고 이후 변경되지 않았다면 그 요청을 실행할 수 있으며, 또는 서비스가 한 번 요청을 검증하고 결과를 메모이제이션하여 동일한 요청을 다시 검증하지 않도록 할 수 있습니다.

6.1.2변수 값 강제 변환

연산이 변수를 정의한 경우, 그 변수들에 대한 값은 변수에 선언된 타입의 입력 강제 변환 규칙을 사용하여 강제 변환되어야 합니다. 변수 값의 입력 강제 변환 중에 request error가 발생하면 연산은 실행 없이 실패합니다.

CoerceVariableValues(schema, operation, variableValues)
  1. coercedValues를 빈 비순서 맵으로 둡니다.
  2. variablesDefinitionoperation에 의해 정의된 변수들로 둡니다.
  3. variablesDefinition에 있는 각각의 variableDefinition에 대해:
    1. variableName을 해당 variableDefinition의 이름으로 둡니다.
    2. variableType을 해당 variableDefinition의 기대 타입으로 둡니다.
    3. 단언: IsInputType(variableType)는 참이어야 합니다.
    4. defaultValue를 해당 variableDefinition의 기본값으로 둡니다.
    5. hasValuevariableValues가 이름이 variableName인 값을 제공하는 경우 참으로 둡니다.
    6. valuevariableValues에서 이름이 variableName인 항목의 값으로 둡니다.
    7. 만약 hasValue가 참이 아니고 defaultValue가 존재하면(리터럴 null 포함):
      1. coercedDefaultValuedefaultValuevariableType의 입력 강제 변환 규칙에 따라 강제 변환한 결과로 둡니다.
      2. coercedValues에 이름이 variableName인 항목을 추가하고 그 값을 coercedDefaultValue로 둡니다.
    8. 그렇지 않고 variableType이 Non-Nullable 타입이며, hasValue가 참이 아니거나 valuenull이면, request error를 발생시킵니다.
    9. 그렇지 않고 hasValue가 참이면:
      1. 만약 valuenull이면:
        1. coercedValues에 이름이 variableName인 항목을 추가하고 값으로 null을 둡니다.
      2. 그렇지 않으면:
        1. 만약 valuevariableType의 입력 강제 변환 규칙에 따라 강제 변환될 수 없다면 request error를 발생시킵니다.
        2. coercedValuevaluevariableType의 입력 강제 변환 규칙에 따라 강제 변환한 결과로 둡니다.
        3. coercedValues에 이름이 variableName인 항목을 추가하고 값으로 coercedValue를 둡니다.
  4. coercedValues를 반환합니다.
참고 이 알고리즘은 CoerceArgumentValues()와 매우 유사합니다.

6.2연산 실행

명세의 "타입 시스템" 섹션에 설명된 바와 같이, 타입 시스템은 쿼리 루트 연산 타입을 제공해야 합니다. 만약 뮤테이션이나 구독을 지원하면 각각 뮤테이션 또는 구독 루트 연산 타입도 제공해야 합니다.

6.2.1쿼리

연산이 쿼리인 경우, 연산의 결과는 해당 연산의 root selection set을 쿼리 루트 연산 타입으로 실행한 결과입니다.

쿼리 연산을 실행할 때 초기 값이 제공될 수 있습니다.

ExecuteQuery(query, schema, variableValues, initialValue)
  1. queryTypeschema의 루트 Query 타입으로 둡니다.
  2. 단언: queryType은 Object 타입입니다.
  3. rootSelectionSetqueryroot selection set으로 둡니다.
  4. ExecuteRootSelectionSet(variableValues, initialValue, queryType, rootSelectionSet, "normal")을 반환합니다.

6.2.2뮤테이션

연산이 뮤테이션인 경우, 연산의 결과는 뮤테이션 루트 오브젝트 타입에서 해당 연산의 root selection set을 실행한 결과입니다. 이 선택 집합은 순차적으로(serial) 실행되어야 합니다.

뮤테이션 연산의 최상위 필드들은 기본 데이터 시스템에 부수 효과(side-effects)를 발생시키는 것이 기대됩니다. 제공된 뮤테이션을 순차적으로 실행하면 이러한 부수 효과 동안의 경쟁 상태(race conditions)를 방지할 수 있습니다.

ExecuteMutation(mutation, schema, variableValues, initialValue)
  1. mutationTypeschema의 루트 Mutation 타입으로 둡니다.
  2. 단언: mutationType은 Object 타입입니다.
  3. rootSelectionSetmutationroot selection set으로 둡니다.
  4. ExecuteRootSelectionSet(variableValues, initialValue, mutationType, rootSelectionSet, "serial")을 반환합니다.

6.2.3구독

연산이 구독이면, 결과는 각각의 이벤트에서 연산을 실행한 결과인 이벤트들의 시퀀스인 event stream이며, 이를 응답 스트림(response stream)이라 합니다.

구독 연산을 실행하면 서비스 상에 지속적인 함수가 생성되어 기본 source stream을 반환할 response stream에 매핑합니다.

Subscribe(subscription, schema, variableValues, initialValue)
  1. sourceStreamCreateSourceEventStream(subscription, schema, variableValues, initialValue)의 결과입니다.
  2. responseStreamMapSourceToResponseEvent(sourceStream, subscription, schema, variableValues)의 결과입니다.
  3. responseStream을 반환합니다.
참고 대규모 구독 시스템에서는 Subscribe()ExecuteSubscriptionEvent() 알고리즘을 서로 다른 서비스에서 실행하여 예측 가능한 확장 특성을 유지할 수 있습니다. 자세한 내용은 아래의 "대규모 구독 지원" 섹션을 참조하세요.

예를 들어 채팅 애플리케이션을 고려해 보십시오. 채팅방에 새 메시지가 게시되는 것을 구독하려면 클라이언트는 다음과 같은 요청을 보냅니다:

Example № 199subscription NewMessages {
  newMessage(roomId: 123) {
    sender
    text
  }
}

클라이언트가 구독 중인 동안, ID가 "123"인 채팅방에 새 메시지가 게시될 때마다 "sender"와 "text" 선택이 평가되어 클라이언트에 발행됩니다. 예를 들면:

Example № 200{
  "data": {
    "newMessage": {
      "sender": "Hagrid",
      "text": "You're a wizard!"
    }
  }
}

"채팅방에 새 메시지 게시"는 채팅방 ID를 "토픽"으로 하고 각 게시(publish)에 발행 값(발신자와 텍스트)이 포함되는 Pub-Sub 시스템을 사용할 수 있습니다.

이벤트 스트림

이벤트 스트림은 시간에 따라 관찰 가능한 이산적으로 방출되는 값들의 시퀀스를 나타냅니다. 예로, Pub-Sub 시스템은 "토픽 구독" 시 해당 토픽에 대한 각 "게시"마다 값을 방출하는 event stream을 생성할 수 있습니다.

이벤트 스트림은 언제든지 완료될 수 있으며, 이는 더 이상 이벤트가 발생하지 않기 때문일 수 있습니다. 어떤 경우에는 무한한 값의 시퀀스를 방출할 수 있어 결코 완료되지 않을 수도 있습니다. 만약 스트림이 오류를 만난다면, 그 오류와 함께 완료되어야 합니다.

관찰자는 언제든지 이벤트 스트림을 취소하여 더 이상 관찰하지 않을 수 있습니다. 스트림이 취소되면 스트림은 완료되어야 합니다.

내부 사용자 코드 또한 어떤 이유로든 이벤트 스트림을 취소할 수 있으며, 이는 해당 스트림이 완료되는 것으로 관찰됩니다.

대규모 구독 지원

쿼리와 뮤테이션 연산은 상태가 없으므로 GraphQL 서비스 인스턴스를 복제하여 확장할 수 있습니다. 반면 구독은 상태를 가지며 구독의 수명 동안 GraphQL 문서, 변수 및 기타 문맥을 유지해야 합니다.

단일 머신의 장애로 인해 상태가 손실될 경우 시스템의 동작을 고려하십시오. 내구성(durability)과 가용성(availability)은 구독 상태 및 클라이언트 연결 관리를 전담하는 별도의 서비스로 분리함으로써 향상될 수 있습니다.

전송 비종속성

GraphQL 구독은 특정 직렬화 형식이나 전송 메커니즘을 요구하지 않습니다. GraphQL은 응답 스트림 생성, 스트림의 각 페이로드 내용, 스트림 종료에 대한 알고리즘을 규정합니다. 메시지 승인, 버퍼링, 재전송 요청 또는 기타 QoS(서비스 품질) 세부사항에 대한 명세는 의도적으로 포함하지 않았습니다. 메시지 직렬화, 전송 메커니즘 및 QoS 세부 사항은 구현 서비스에서 선택해야 합니다.

6.2.3.1소스 스트림

소스 스트림은 GraphQL 실행을 트리거하는 루트 값들의 시퀀스를 나타내는 event stream입니다. 필드 값 해상처럼, source stream을 생성하는 로직은 애플리케이션별입니다.

CreateSourceEventStream(subscription, schema, variableValues, initialValue)
  1. subscriptionTypeschema의 루트 Subscription 타입으로 둡니다.
  2. 단언: subscriptionType은 Object 타입입니다.
  3. selectionSetsubscription의 최상위 선택 집합으로 둡니다.
  4. collectedFieldsMapCollectFields(subscriptionType, selectionSet, variableValues)의 결과로 둡니다.
  5. 만약 collectedFieldsMap의 항목 수가 정확히 1이 아니면 request error를 발생시킵니다.
  6. fieldscollectedFieldsMap의 첫 번째 항목 값으로 둡니다.
  7. fieldNamefields의 첫 번째 항목의 이름으로 둡니다(참고: alias 사용 여부와 무관합니다).
  8. fieldfields의 첫 번째 항목으로 둡니다.
  9. argumentValuesCoerceArgumentValues(subscriptionType, field, variableValues)의 결과로 둡니다.
  10. sourceStreamResolveFieldEventStream(subscriptionType, initialValue, fieldName, argumentValues)의 결과로 둡니다.
  11. sourceStream을 반환합니다.
ResolveFieldEventStream(subscriptionType, rootValue, fieldName, argumentValues)
  1. resolversubscriptionType이 제공하는, 이름이 fieldName인 구독 필드의 이벤트 스트림을 결정하는 내부 함수로 둡니다.
  2. resolver를 호출하여 rootValueargumentValues를 제공한 결과를 반환합니다.
참고ResolveFieldEventStream() 알고리즘은 모든 연산 타입에서 일관된 리졸버 정의를 가능하게 하기 위해 ResolveFieldValue()와 의도적으로 유사합니다.

6.2.3.2응답 스트림

기저의 source stream에서 나오는 각 이벤트는 그 이벤트의 값을 initialValue로 사용하여 구독의 selection set을 실행하게 합니다.

MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues)
  1. responseStream을 새 event stream으로 둡니다.
  2. sourceStreamsourceValue를 방출할 때:
    1. executionResultExecuteSubscriptionEvent(subscription, schema, variableValues, sourceValue)를 실행한 결과입니다.
    2. 만약 내부 error가 발생하면:
      1. sourceStream을 취소합니다.
      2. responseStream을 그 error와 함께 완료합니다.
    3. 그렇지 않으면 executionResultresponseStream에 방출합니다.
  3. sourceStream가 정상적으로 완료되면: responseStream을 정상적으로 완료합니다.
  4. sourceStreamerror와 함께 완료되면: responseStream을 그 error와 함께 완료합니다.
  5. responseStream가 취소되면:
    1. sourceStream을 취소합니다.
    2. responseStream을 정상적으로 완료합니다.
  6. responseStream을 반환합니다.
참고 ExecuteSubscriptionEvent()가 모든 execution error를 처리하고, request error는 오직 CreateSourceEventStream() 중에만 발생하므로, ExecuteSubscriptionEvent()에서 처리되는 남은 오류 조건은 이 명세에서 설명하지 않은 내부 예외적 오류들뿐입니다.
ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue)
  1. subscriptionTypeschema의 루트 Subscription 타입으로 둡니다.
  2. 단언: subscriptionType은 Object 타입입니다.
  3. rootSelectionSetsubscriptionroot selection set으로 둡니다.
  4. ExecuteRootSelectionSet(variableValues, initialValue, subscriptionType, rootSelectionSet, "normal")을 반환합니다.
참고 ExecuteSubscriptionEvent() 알고리즘은 각 이벤트 결과가 어떻게 생성되는지를 반영하여 ExecuteQuery()와 의도적으로 유사합니다.

6.2.3.3구독 취소

구독 취소는 클라이언트가 더 이상 구독에 대한 페이로드를 수신하기 원하지 않을 때 응답 스트림을 취소합니다. 이는 차례로 Source Stream도 취소하며, 구독에서 사용된 다른 리소스를 정리(clean up)할 수 있는 좋은 기회입니다.

Unsubscribe(responseStream)
  1. responseStream을 취소합니다.

6.3선택 집합 실행

GraphQL 연산의 실행은 재귀적으로 연산에서 선택된 모든 필드를 수집하고 실행합니다. 먼저 연산의 최상위 루트 선택 집합에서 초기 선택된 모든 필드를 수집한 다음 각 필드를 실행합니다. 각 필드가 완료되면 그 하위 필드들이 수집되고 각각 실행됩니다. 이 과정은 더 이상 수집하고 실행할 하위 필드가 없을 때까지 계속됩니다.

6.3.1루트 선택 집합 실행

루트 선택 집합은 GraphQL 연산이 제공하는 최상위 선택 집합입니다. 루트 선택 집합은 항상 루트 연산 타입에서 선택합니다.

루트 선택 집합을 실행하려면 평가되는 초기 값과 루트 타입, 그리고 필드들을 순차적으로 실행해야 하는지 또는 모든 필드를 병렬로(또는 일반적으로) 실행해야 하는지(참조: Normal and Serial Execution)를 알아야 합니다.

쿼리(병렬), 뮤테이션(순차), 구독(기저 Source Stream의 각 이벤트마다 실행) 모두에서 루트 선택 집합을 실행하는 방식은 유사합니다.

먼저 선택 집합collected fields map으로 수집한 뒤 그 맵을 실행하여 결과 dataerrors를 반환합니다.

ExecuteRootSelectionSet(variableValues, initialValue, objectType, selectionSet, executionMode)
  1. collectedFieldsMapCollectFields(objectType, selectionSet, variableValues)의 결과로 둡니다.
  2. dataExecuteCollectedFields(collectedFieldsMap, objectType, initialValue, variableValues)의 실행 결과로 둡니다. 이때 executionMode"serial"이면 순차적으로 실행하고, 그렇지 않으면 일반적으로 (병렬화를 허용) 실행합니다.
  3. errors를 선택 집합 실행 중 발생한 모든 execution error들의 리스트로 둡니다.
  4. dataerrors를 포함하는 비정렬 맵을 반환합니다.

6.3.2필드 수집

실행 전에 각 선택 집합은 동일한 응답 이름을 가진 모든 필드(참조된 프래그먼트에 있는 필드 포함)를 개별적인 field set으로 수집하여 collected fields map으로 변환됩니다. 이렇게 하면 동일한 응답 이름을 가진 필드에 대한 여러 참조가 한 번만 실행되도록 보장됩니다.

collected fields map은 각 항목이 response name과 그에 연관된 field set인 순서 있는 맵입니다. collected fields mapCollectFields()를 통해 선택 집합으로부터 생성되거나 CollectSubfields()을 통해 field set의 모든 선택 집합으로부터 생성될 수 있습니다.

field set은 동일한 response name(별칭이 정의된 경우 별칭, 그렇지 않으면 필드 이름)을 공유하는 선택된 필드들의 순서 있는 집합입니다. 검증은 집합 내 각 필드가 동일한 이름과 인수를 가지도록 보장하지만, 각 필드는 서로 다른 하위 필드를 가질 수 있습니다(참조: Field Selection Merging).

참고 collected fields mapfield set에서의 필드 선택 순서는 중요하므로, 이 명세의 알고리즘은 이를 순서 있는 맵과 순서 있는 집합으로 모델링합니다.

예를 들어, 이 쿼리의 선택 집합을 수집하면 두 개의 항목 "a""b"를 가진 collected fields map이 생성되고, 필드 a의 인스턴스가 두 번, 필드 b가 한 번 포함됩니다:

Example № 201{
  a {
    subfield1
  }
  ...ExampleFragment
}

fragment ExampleFragment on Query {
  a {
    subfield2
  }
  b
}

CollectFields()가 생성한 각 field set의 깊이 우선 탐색 순서는 실행을 통해 유지되어, 필드들이 응답에 안정적이고 예측 가능한 순서로 나타나도록 합니다.

CollectFields(objectType, selectionSet, variableValues, visitedFragments)
  1. 만약 visitedFragments가 제공되지 않았다면, 이를 빈 집합으로 초기화합니다.
  2. collectedFieldsMap을 빈 순서 있는 맵(순서 있는 집합들을 값으로 가짐)으로 초기화합니다.
  3. selectionSet의 각 selection에 대해:
    1. 만약 selection@skip 지시문을 제공하면, 그 지시문을 skipDirective라 합니다.
      1. 만약 skipDirectiveif 인수가 true이거나 변수값 variableValues에 있는 변수로서 값이 true이면, 다음 selection으로 계속 진행합니다.
    2. 만약 selection@include 지시문을 제공하면, 그 지시문을 includeDirective라 합니다.
      1. 만약 includeDirectiveif 인수가 true가 아니고, 또한 variableValues에 있는 변수로서 값이 true가 아니라면, 다음 selection으로 계속 진행합니다.
    3. 만약 selectionField이면:
      1. responseName을 해당 selectionresponse name(별칭이 정의된 경우 별칭, 그렇지 않으면 필드 이름)으로 둡니다.
      2. fieldsForResponseNamecollectedFieldsMap에서 키 responseName에 해당하는 field set 값으로 둡니다; 존재하지 않으면 빈 순서 있는 집합으로 항목을 생성합니다.
      3. selectionfieldsForResponseName에 추가합니다.
    4. 만약 selectionFragmentSpread이면:
      1. fragmentSpreadName을 그 selection의 이름으로 둡니다.
      2. 만약 fragmentSpreadNamevisitedFragments에 있다면, 다음 selection으로 계속합니다.
      3. fragmentSpreadNamevisitedFragments에 추가합니다.
      4. fragment을 현재 문서에서 이름이 fragmentSpreadName인 프래그먼트로 둡니다.
      5. 만약 그런 fragment가 존재하지 않으면, 다음 selection으로 계속합니다.
      6. fragmentType을 해당 fragment의 타입 조건으로 둡니다.
      7. 만약 DoesFragmentTypeApply(objectType, fragmentType)false이면, 다음 selection으로 계속합니다.
      8. fragmentSelectionSet을 해당 fragment의 최상위 선택 집합으로 둡니다.
      9. fragmentCollectedFieldsMapCollectFields(objectType, fragmentSelectionSet, variableValues, visitedFragments)의 결과로 둡니다.
      10. fragmentCollectedFieldsMap의 각 responseNamefragmentFields에 대해:
        1. fieldsForResponseNamecollectedFieldsMap에서 키 responseName에 해당하는 field set 값으로 둡니다; 존재하지 않으면 빈 순서 있는 집합으로 항목을 생성합니다.
        2. fragmentFields의 각 항목을 fieldsForResponseName에 추가합니다.
    5. 만약 selectionInlineFragment이면:
      1. fragmentType을 해당 selection의 타입 조건으로 둡니다.
      2. 만약 fragmentTypenull이 아니고 DoesFragmentTypeApply(objectType, fragmentType)false이면, 다음 selection으로 계속합니다.
      3. fragmentSelectionSet을 해당 selection의 최상위 선택 집합으로 둡니다.
      4. fragmentCollectedFieldsMapCollectFields(objectType, fragmentSelectionSet, variableValues, visitedFragments)의 결과로 둡니다.
      5. fragmentCollectedFieldsMap의 각 responseNamefragmentFields에 대해:
        1. fieldsForResponseNamecollectedFieldsMap에서 키 responseName에 해당하는 field set 값으로 둡니다; 존재하지 않으면 빈 순서 있는 집합으로 항목을 생성합니다.
        2. fragmentFields의 각 항목을 fieldsForResponseName에 추가합니다.
  4. collectedFieldsMap을 반환합니다.
DoesFragmentTypeApply(objectType, fragmentType)
  1. 만약 fragmentType이 Object Type이면:
    1. 만약 objectTypefragmentType가 동일한 타입이면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
  2. 만약 fragmentType이 Interface Type이면:
    1. 만약 objectTypefragmentType의 구현체이면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
  3. 만약 fragmentType이 Union이면:
    1. 만약 objectTypefragmentType의 가능한 타입 중 하나이면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
참고 CollectFields()에서 @skip@include 지시문을 평가하는 단계는 교환법적으로 적용될 수 있으므로 어느 순서로 적용해도 됩니다.
선택 집합 병합

객체 타입 필드의 하위 선택들을 실행하기 위해, 부모 field set에서 동일한 응답 이름을 갖는 각 필드의 모든 선택 집합을 다음에 실행할 하위 필드를 나타내는 단일 collected fields map으로 병합합니다.

동일한 이름의 병렬 필드와 하위 선택을 보여주는 예제 연산입니다.

위의 예를 계속하면,

Example № 202{
  a {
    subfield1
  }
  ...ExampleFragment
}

fragment ExampleFragment on Query {
  a {
    subfield2
  }
  b
}

필드 "a"의 값을 해석한 후, 다음과 같은 여러 선택 집합들이 수집되어 병합되므로 "subfield1""subfield2"가 동일한 단계에서 동일한 값으로 해석됩니다.

CollectSubfields(objectType, fields, variableValues)
  1. collectedFieldsMap을 빈 순서 있는 맵으로 둡니다.
  2. fields의 각 field에 대해:
    1. fieldSelectionSet을 해당 field의 선택 집합으로 둡니다.
    2. 만약 fieldSelectionSet이 null이거나 비어 있으면, 다음 필드로 계속합니다.
    3. fieldCollectedFieldsMapCollectFields(objectType, fieldSelectionSet, variableValues)의 결과로 둡니다.
    4. fieldCollectedFieldsMap의 각 responseNamesubfields에 대해:
      1. fieldsForResponseNamecollectedFieldsMap에서 키 responseName에 해당하는 field set 값으로 둡니다; 존재하지 않으면 빈 순서 있는 집합으로 항목을 생성합니다.
      2. subfields의 각 필드를 fieldsForResponseName에 추가합니다.
  3. collectedFieldsMap을 반환합니다.
참고 fields로 전달되는 모든 필드는 동일한 response name을 공유합니다.

6.3.3수집된 필드 실행

collected fields map을 실행하려면 평가되는 객체 타입과 런타임 값, 그리고 변수들의 런타임 값들이 알려져야 합니다.

실행은 수집된 필드 맵의 각 항목의 값을 재귀적으로 해석하고 완료하여, 동일한 response name 키로 결과 맵에 항목을 생성합니다.

ExecuteCollectedFields(collectedFieldsMap, objectType, objectValue, variableValues)
  1. resultMap을 빈 순서 있는 맵으로 초기화합니다.
  2. collectedFieldsMap의 각 responseNamefields에 대해:
    1. fieldNamefields의 첫 번째 항목의 이름으로 둡니다. 참고: 이 값은 별칭이 사용되어도 영향을 받지 않습니다.
    2. fieldTypeobjectType의 필드 fieldName에 정의된 반환 타입으로 둡니다.
    3. 만약 fieldType이 정의되어 있으면:
      1. responseValueExecuteField(objectType, objectValue, fieldType, fields, variableValues)의 결과로 둡니다.
      2. responseValueresponseName의 값으로 resultMap에 설정합니다.
  3. resultMap을 반환합니다.
참고 resultMap은 연산에서 필드가 처음 등장한 순서대로 정렬됩니다. 이 내용은 필드 수집 섹션에서 더 자세히 설명됩니다.
오류와 Non-Null 타입

만약 ExecuteCollectedFields() 수행 중 non-null 타입을 가진 응답 위치에서 execution error가 발생하면, 그 오류는 부모 응답 위치(필드의 경우 해당 선택 집합 전체, 리스트 위치의 경우 해당 리스트 전체)로 전파되어, 허용되는 경우 null로 해석되거나 더 상위의 응답 위치로 추가로 전파되어야 합니다.

이러한 경우 아직 실행되지 않았거나 값을 아직 생성하지 않은 형제 응답 위치들은 불필요한 작업을 피하기 위해 취소될 수 있습니다.

참고 이 동작에 관해서는 Handling Execution Errors를 참조하세요.

6.3.4일반 실행과 순차 실행

일반적으로 실행기는 collected fields map의 항목들을 구현체가 선택한 어떤 순서로든(보통 병렬로) 실행할 수 있습니다. 최상위 뮤테이션 필드를 제외한 필드들의 해석은 항상 부수 효과가 없고 멱등성이 있어야 하므로, 실행 순서는 결과에 영향을 주어서는 안 되며, 따라서 서비스는 최적이라고 판단하는 어떤 순서로든 필드 항목들을 실행할 자유가 있습니다.

예를 들어, 다음과 같은 collected fields map이 일반적으로 실행되어야 한다고 가정하면:

Example № 203{
  birthday {
    month
  }
  address {
    street
  }
}

유효한 GraphQL 실행기는 네 개의 필드를 어떤 순서로든 해석할 수 있습니다(단, birthdaymonth보다 먼저, addressstreet보다 먼저 해석되어야 합니다).

뮤테이션을 실행할 때는 최상위 선택 집합의 선택들이 텍스트상 처음 등장한 필드부터 순차적으로 실행되어야 합니다.

collected fields map을 순차적으로 실행할 때, 실행기는 collected fields map에 제공된 순서대로 각 항목을 고려해야 합니다. 각 항목에 대해 결과 맵의 해당 항목이 완료될 때까지 처리한 뒤 다음 항목으로 진행해야 합니다.

예를 들어 다음 뮤테이션 연산이 주어지면, 루트 선택 집합은 순차적으로 실행되어야 합니다:

Example № 204mutation ChangeBirthdayAndAddress($newBirthday: String!, $newAddress: String!) {
  changeBirthday(birthday: $newBirthday) {
    month
  }
  changeAddress(address: $newAddress) {
    street
  }
}

따라서 실행기는 순차적으로 다음을 수행해야 합니다:

  • ExecuteField()changeBirthday에 대해 실행하고, CompleteValue() 단계에서 { month } 하위 선택 집합을 일반적으로 실행합니다.
  • ExecuteField()changeAddress에 대해 실행하고, CompleteValue() 단계에서 { street } 하위 선택 집합을 일반적으로 실행합니다.

예시로, 반환값에 단일 필드 theNumber를 포함하는 객체를 반환하는 뮤테이션 필드 changeTheNumber가 있다고 가정합시다. 다음 선택 집합을 순차적으로 실행하면:

Example № 205# Note: This is a selection set, not a full document using the query shorthand.
{
  first: changeTheNumber(newNumber: 1) {
    theNumber
  }
  second: changeTheNumber(newNumber: 3) {
    theNumber
  }
  third: changeTheNumber(newNumber: 2) {
    theNumber
  }
}

실행기는 다음을 순차적으로 실행합니다:

  • changeTheNumber(newNumber: 1) 필드를 해석합니다.
  • first{ theNumber } 하위 선택 집합을 일반적으로 실행합니다.
  • changeTheNumber(newNumber: 3) 필드를 해석합니다.
  • second{ theNumber } 하위 선택 집합을 일반적으로 실행합니다.
  • changeTheNumber(newNumber: 2) 필드를 해석합니다.
  • third{ theNumber } 하위 선택 집합을 일반적으로 실행합니다.

올바른 실행기는 해당 선택 집합에 대해 다음과 같은 결과를 생성해야 합니다:

Example № 206{
  "first": {
    "theNumber": 1
  },
  "second": {
    "theNumber": 3
  },
  "third": {
    "theNumber": 2
  }
}

6.4필드 실행

결과 맵의 각 항목은 collected fields map에서 해당 필드의 이름으로 선택된 객체 타입에서 필드를 실행한 결과입니다. 필드 실행은 먼저 제공된 인수 값을 강제 변환(coerce)하고, 그 다음 필드의 값을 해석(resolve)하며, 마지막으로 재귀적으로 다른 선택 집합을 실행하거나 스칼라 값을 강제 변환하여 그 값을 완성(completion)합니다.

ExecuteField(objectType, objectValue, fieldType, fields, variableValues)
  1. fieldfields의 첫 번째 항목으로 둔다.
  2. fieldNamefield의 필드 이름으로 둔다.
  3. argumentValuesCoerceArgumentValues(objectType, field, variableValues)의 결과로 둔다.
  4. resolvedValueResolveFieldValue(objectType, objectValue, fieldName, argumentValues)로 둔다.
  5. CompleteValue(fieldType, fields, resolvedValue, variableValues)의 결과를 반환한다.

6.4.1필드 인자 강제 변환

필드는 올바른 값을 생성하기 위해 런타임에 전달되는 인수를 포함할 수 있습니다. 이러한 인수들은 타입 시스템에서 특정 입력 타입으로 정의됩니다.

연산의 각 인수 자리에는 리터럴 Value가 있을 수 있고, 또는 런타임에 제공될 Variable일 수 있습니다.

CoerceArgumentValues(objectType, field, variableValues)
  1. coercedValues를 빈 비순서 Map으로 둔다.
  2. argumentValuesfield에 제공된 인자 값들로 둔다.
  3. fieldNamefield의 이름으로 둔다.
  4. argumentDefinitionsobjectTypefieldName 이름에 대해 정의한 인자들로 둔다.
  5. argumentDefinitions에 있는 각 argumentDefinition에 대해:
    1. argumentNameargumentDefinition의 이름으로 둔다.
    2. argumentTypeargumentDefinition의 예상 타입으로 둔다.
    3. defaultValueargumentDefinition의 기본값으로 둔다.
    4. argumentValueargumentValuesargumentName에 대해 제공된 값으로 둔다.
    5. 만약 argumentValueVariable이라면:
      1. variableNameargumentValue의 이름으로 둔다.
      2. 만약 variableValuesvariableName에 대해 값을 제공한다면:
        1. hasValuetrue로 둔다.
        2. valuevariableValuesvariableName에 대해 제공된 값으로 둔다.
    6. 아니면 argumentValuesargumentName에 대해 값을 제공한다면:
      1. hasValuetrue로 둔다.
      2. valueargumentValue로 둔다.
    7. hasValuetrue가 아니고 defaultValue가 존재( null 포함)한다면:
      1. coercedDefaultValueargumentType의 입력 강제 변환 규칙에 따라 defaultValue를 변환한 결과로 둔다.
      2. coercedValuesargumentName 이름과 coercedDefaultValue 값을 추가한다.
    8. 그렇지 않고 argumentType이 Non-Nullable 타입이고, hasValuetrue가 아니거나 valuenull이면, execution error를 발생시킨다.
    9. 그렇지 않고 hasValuetrue이면:
      1. valuenull이면:
        1. coercedValuesargumentName 이름과 null 값을 추가한다.
      2. 그렇지 않고 argumentValueVariable라면:
        1. coercedValuesargumentName 이름과 value 값을 추가한다.
      3. 그 밖의 경우:
        1. valueargumentType의 입력 강제 변환 규칙에 따라 강제 변환될 수 없다면, execution error를 발생시킨다.
        2. coercedValueargumentType의 입력 강제 변환 규칙에 따라 value를 강제 변환한 결과로 둔다.
        3. coercedValuesargumentName 이름과 coercedValue 값을 추가한다.
  6. coercedValues를 반환한다.

request errorCoerceArgumentValues() 수행 중 입력 강제 변환의 결과로 발생하면, 이는 대신 execution error로 처리되어야 합니다.

Note 변수 값은 연산을 실행하기 전에 CoerceVariableValues()에서 강제 변환되는 것으로 기대되기 때문에 강제 변환되지 않습니다. 유효한 연산은 적절한 타입의 변수를 사용하도록만 허용합니다.
Note 구현체는 인수의 기본값을 한 번만 강제 변환하고 그 결과된 coerced 값을 캐시하도록 최적화하는 것을 권장합니다.

6.4.2값 해결

GraphQL 실행의 대부분은 일반적으로 설명될 수 있지만, 궁극적으로 GraphQL 인터페이스를 노출하는 내부 시스템이 값을 제공해야 합니다. 이것은 주어진 타입의 특정 필드에 대한 값을 생성하는 ResolveFieldValue을 통해 노출됩니다.

예를 들어, 이 함수는 objectType으로 Person을 받고, field"soulMate"를 받으며, John Lennon을 나타내는 objectValue를 받아 Yoko Ono를 나타내는 값을 반환해야 합니다.

ResolveFieldValue(objectType, objectValue, fieldName, argumentValues)
  1. resolverobjectTypefieldName이라는 필드의 값을 결정하기 위해 제공하는 내부 함수로 둔다.
  2. resolver를 호출하여 objectValueargumentValues를 전달한 결과를 반환한다.
Note resolver는 일반적으로 기본 데이터베이스나 네트워크 서비스의 읽기에 의존하기 때문에 비동기적일 수 있습니다. 이는 GraphQL 실행기의 나머지 부분이 비동기 실행 흐름을 처리하도록 요구합니다. 필드가 리스트 타입인 경우 resolver가 반환한 값들의 각 항목도 비동기적으로 획득될 수 있습니다.

6.4.3값 완료

필드의 값을 해석한 후, 그 값이 기대되는 반환 타입을 준수하는지 확인하여 완료합니다. 반환 타입이 다른 객체(Object) 타입인 경우, 필드 실행 과정은 하위 필드들을 수집하고 실행함으로써 재귀적으로 계속됩니다.

CompleteValue(fieldType, fields, result, variableValues)
  1. 만약 fieldType이 Non-Null 타입이라면:
    1. innerTypefieldType의 내부 타입으로 둔다.
    2. completedResultCompleteValue(innerType, fields, result, variableValues)의 결과로 둔다.
    3. completedResultnull이라면 execution error를 발생시킨다.
    4. completedResult를 반환한다.
  2. resultnull이거나 (undefinednull과 유사한 내부 값)인 경우 null을 반환한다.
  3. 만약 fieldType이 List 타입이라면:
    1. result가 값들의 컬렉션이 아니라면 execution error를 발생시킨다.
    2. innerTypefieldType의 내부 타입으로 둔다.
    3. 각 리스트 항목마다 CompleteValue(innerType, fields, resultItem, variableValues)을 호출한 결과를 리스트로 반환한다. 여기서 resultItemresult의 각 항목이다.
  4. fieldType이 Scalar 또는 Enum 타입이라면:
    1. CoerceResult(fieldType, result)의 결과를 반환한다.
  5. fieldType이 Object, Interface, 또는 Union 타입이라면:
    1. fieldType이 Object 타입이라면:
      1. objectTypefieldType으로 둔다.
    2. 그렇지 않고 fieldType이 Interface 또는 Union 타입이라면:
      1. objectTypeResolveAbstractType(fieldType, result)로 둔다.
    3. collectedFieldsMapCollectSubfields(objectType, fields, variableValues)의 결과로 둔다.
    4. ExecuteCollectedFields(collectedFieldsMap, objectType, result, variableValues)일반적으로 실행(병렬 처리를 허용)한 결과를 반환한다.
Coercing Results

값 완성의 주요 목적은 필드 리졸버가 반환한 값들이 GraphQL 타입 시스템과 서비스의 스키마에 따라 유효한지 확인하는 것입니다. 이러한 "동적 타입 검사"는 내부 런타임이 어떠하든 일관된 반환 타입 보장을 제공합니다.

GraphQL의 내장 스칼라가 결과 값을 어떻게 강제 변환하는지에 대한 자세한 내용은 Scalars의 Result Coercion and Serialization 하위 섹션을 참조하세요.

CoerceResult(leafType, value)
  1. 단언: valuenull이 아니다.
  2. 타입 시스템이 제공하는 내부 메소드를 호출하여 leafType에 대해 value가 "결과 강제 변환(result coercion)"될 결과를 반환한다. 이 내부 메소드는 타입에 대해 유효한 값을 반환해야 하며, null을 반환해서는 안 된다. 그렇지 않으면 execution error를 발생시킨다.
Note 필드 리졸버가 null을 반환하면 이는 CompleteValue() 내에서 처리된 뒤 CoerceResult()가 호출됩니다. 따라서 CoerceResult()의 입력과 출력은 null이 아니어야 합니다.
Resolving Abstract Types

추상 반환 타입(Interface 또는 Union 타입)을 가진 필드를 완료할 때는 먼저 해당 추상 타입을 관련된 Object 타입으로 결정해야 합니다. 이 결정은 내부 시스템이 적절한 방식으로 수행합니다.

Note Java나 C# 같은 객체지향 환경에서 objectValue에 대한 Object 타입을 결정하는 일반적인 방법은 objectValue의 클래스 이름을 사용하는 것입니다.
ResolveAbstractType(abstractType, objectValue)
  1. 타입 시스템이 제공하는 내부 메서드를 호출하여 abstractType의 Object 타입을 objectValue 값으로 결정한 결과를 반환한다.

6.4.4실행 오류 처리

execution error는 필드 실행, 값 해석 또는 강제 변환 중 특정 response position에서 발생하는 오류입니다. 이 오류들은 응답에 보고되어야 하지만, 응답에서 부분적인 "data"를 생성하는 방식으로 "처리"됩니다.

Note 이는 데이터가 없는 request error 결과와는 구별됩니다.

필드 해석 중(직접이든 리스트 내부의 중첩이든) execution error가 발생하면, 해당 오류가 발생한 response positionnull로 해석된 것처럼 처리되며, 그 오류는 "errors" 리스트에 추가되어야 합니다.

만약 response position의 해석 결과가 null이고(이는 ResolveFieldValue()의 결과이거나 execution error로 인한 것일 수 있음), 그 위치가 Non-Null 타입이면, 해당 위치에서 execution error가 발생합니다. 그 오류는 실행 결과의 "errors" 리스트에 추가되어야 합니다.

만약 어떤 execution error로 인해 이미 실행 결과의 "errors" 리스트에 추가된 오류 때문에 response positionnull로 해석되었다면, 그 위치에 대해 오류 리스트는 더 이상 영향을 받아서는 안 됩니다. 즉, 각 response position당 오류는 한 개만 추가되어야 합니다.

Non-Null 응답 위치는 null이 될 수 없으므로, execution error는 부모 response position로 전파되어 처리됩니다. 부모 응답 위치가 null이 될 수 있다면 그 위치는 null로 해석되고, 그렇지 않고 부모가 다시 Non-Null 타입이라면 오류는 상위 부모로 계속 전파됩니다.

만약 리스트 타입이 Non-Null 타입을 감싸고 있고, 그 리스트의 요소들 중 하나의 response position 요소가 null로 해석되면, 그 전체 리스트 response positionnull로 해석되어야 합니다. 만약 그 리스트 타입 또한 Non-Null로 감싸여 있다면 execution error는 계속해서 위로 전파됩니다.

요청 루트에서 execution error의 근원까지의 모든 response positionNon-Null 타입이라면, 실행 결과의 "data" 항목은 null이어야 합니다.

7응답

GraphQL 서비스가 요청을 받으면, 올바르게 구성된 응답을 반환해야 합니다. 서비스의 응답은 요청된 연산을 성공적으로 실행한 결과를 설명하며, 요청 중 발생한 오류도 함께 설명합니다.

응답에는 실행 오류가 발생하고 null로 대체된 경우, 부분 응답과 오류 목록이 모두 포함될 수 있습니다.

7.1응답 형식

GraphQL 요청은 응답을 반환합니다. 응답실행 결과, 응답 스트림 또는 요청 오류 결과 중 하나입니다.

7.1.1실행 결과

GraphQL 요청이 쿼리 또는 변이이고 실행이 포함된 경우 실행 결과를 반환합니다. 또한 구독의 소스 스트림 내의 각 이벤트에 대해 응답 스트림실행 결과를 내보냅니다.

실행 결과는 반드시 맵(map)이어야 합니다.

실행 결과에는 "data" 키가 포함되어야 합니다. 이 값은 "데이터" 섹션에서 설명합니다.

실행 중 오류가 발생하면, 실행 결과에는 "errors" 키가 포함되어야 합니다. 이 값은 실행 중에 발생한 실행 오류의 비어 있지 않은 리스트여야 합니다. 각 오류는 아래 "오류" 섹션에서 설명한 맵이어야 합니다. 오류가 발생하지 않은 경우 이 항목은 없어야 합니다.

참고 "errors"실행 결과에 존재할 때, 오류가 있음을 쉽게 알 수 있도록 serialization 시 가장 먼저 나타나는 것이 도움이 될 수 있습니다.

실행 결과에는 extensions 키를 가진 항목도 포함될 수 있습니다. 이 값은 "확장" 섹션에서 설명합니다.

7.1.2응답 스트림

GraphQL 연산이 구독이고 실행이 포함된 경우, 응답 스트림을 반환합니다. 응답 스트림은 실행 결과의 스트림이어야 합니다.

7.1.3요청 오류 결과

GraphQL 요청에서 하나 이상의 요청 오류 결과가 발생하고 실행 전에 요청이 실패된 경우, 응답 데이터가 없습니다.

참고 요청 오류는 정보 누락, 구문 오류, 유효성 검사 실패, 강제 변환 실패 또는 구현에서 요청을 진행하지 못하도록 판단하는 기타 이유로 실행 전에 발생할 수 있습니다.

요청 오류 결과는 반드시 맵이어야 합니다.

요청 오류 결과 맵에는 반드시 "errors" 키의 항목이 포함되어야 하며, 이 값은 요청 오류의 비어 있지 않은 리스트여야 합니다. 적어도 하나의 요청 오류가 포함되어야 하며, 그 이유로 데이터가 반환되지 않음을 나타냅니다. 각 오류는 아래 "오류" 섹션에서 설명한 맵이어야 합니다.

참고 "errors" 키가 serialization 시 가장 먼저 나타나는 것이 오류를 쉽게 확인하는 데 도움이 될 수 있습니다.

요청 오류 결과 맵에는 "data" 키의 항목이 없어야 합니다.

요청 오류 결과 맵에는 extensions 키의 항목도 포함될 수 있습니다. 이 값은 "확장" 섹션에서 설명합니다.

7.1.4응답 위치

응답 위치는 실행 중에 생성된 응답 데이터 내에서 고유하게 식별 가능한 위치입니다. 이는 resultMap의 직접 항목이거나 중첩된 리스트 값 내의 위치입니다. 각 응답 위치는 응답 경로를 통해 고유하게 식별됩니다.

응답 경로응답 위치를 응답 루트에서 해당 응답 위치까지 경로 세그먼트(응답 이름 또는 리스트 인덱스) 목록으로 고유하게 식별합니다.

응답 경로의 값은 경로 세그먼트(문자열 혹은 0부터 시작하는 정수)의 리스트여야 하며, 별칭 필드와 관련된 경로 세그먼트는 반드시 별칭 이름을 써야 합니다.

응답 경로가 오류 결과에 존재할 경우, 해당 경로는 오류가 발생한 응답 위치를 나타냅니다.

단일 필드 실행으로 여러 응답 위치가 생성될 수 있습니다. 예를 들어,

예시 № 207{
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

hero의 이름은 응답 위치 응답 경로 ["hero", "name"]에서 확인할 수 있습니다. hero의 친구 목록은 ["hero", "friends"]에, 첫 번째 친구는 ["hero", "friends", 0]에, 그 친구 이름은 ["hero", "friends", 0, "name"]에 있습니다.

7.1.5데이터

"data" 항목은 실행 결과의 요청된 연산 실행 결과입니다. 연산이 쿼리일 경우 쿼리 루트 연산 타입의 객체이고, 변이일 경우 변이 루트 연산 타입의 객체입니다.

응답 데이터는 실행 중 모든 응답 위치의 해석된 결과를 누적하여 생성됩니다.

실행 시작 전에 오류가 발생하면 응답요청 오류 결과여야 하며, 응답 데이터가 없습니다.

실행 중 오류 발생으로 유효한 응답을 생성하지 못하면, 응답의 "data" 항목은 null이어야 합니다.

7.1.6오류

"errors" 항목은 실행 결과요청 오류 결과에 존재하며, 요청 중 발생한 오류의 비어 있지 않은 리스트입니다. 각각의 오류는 아래에 설명하는 오류 결과 형식의 맵입니다.

요청 오류

요청 오류요청 중 발생하며, 응답 데이터가 없는 오류입니다. 일반적으로 실행 시작 전에 발생하며, 파싱 문법 오류, Document의 유효성 검사 오류, 실행할 연산을 결정할 수 없음, 변수의 값이 잘못됨 등에서 발생할 수 있습니다.

요청 오류는 일반적으로 요청 클라이언트의 실수입니다.

요청 오류가 발생하면 응답요청 오류 결과여야 하며, 이 맵에 "data" 항목은 존재하지 않아야 하며 "errors" 항목에는 해당 오류가 포함되어야 하며, 요청 실행은 중지되어야 합니다.

실행 오류

실행 오류는 특정 필드 실행 도중 발생한 오류로, 부분 응답 데이터를 생성합니다. 필드의 인자를 강제 변환하는 데 실패했거나, 값 해석 중 내부 오류가 있거나, 결과 값 강제 변환에 실패하는 경우 발생할 수 있습니다.

참고 이전 버전 명세에서는 실행 오류필드 오류라고 불렀습니다.

실행 오류는 일반적으로 GraphQL 서비스의 실수입니다.

실행 오류는 반드시 특정 응답 위치에서 발생하며, 모든 응답 위치에서 발생할 수 있습니다. 실행 오류의 응답 위치는 오류 응답의 "path" 항목의 응답 경로로 표시됩니다.

특정 응답 위치에서 실행 오류가 발생하면, 해당 위치는 응답"data" 항목 내에는 (단 null 제외) 존재하지 않아야 하며, "errors" 항목에는 해당 오류가 포함되어야 합니다. 중첩 실행은 중단되고, 형제 실행은 계속되어 부분 결과를 생성합니다(자세한 내용은 실행 오류 처리 참고).

오류 결과 형식

각 오류에는 "message" 키가 포함되어야 하며, 개발자가 오류를 이해하고 수정하는 데 도움이 되는 문자열 설명을 제공합니다.

오류가 특정 GraphQL 문서의 위치와 연관될 수 있으면, "locations" 키로 위치 목록을 제공해야 하며, 각 위치는 "line""column" 키를 가진 맵이어야 하고, 모두 1부터 시작하는 양수입니다.

오류가 GraphQL 결과의 특정 필드와 연관될 수 있으면, "path" 키에 응답 경로가 있어야 하며, 오류가 발생한 응답 위치를 설명합니다. 이로 인해 클라이언트는 null 값이 실제 값인지, 실행 오류 결과인지 구분할 수 있습니다.

예를 들어, 친구의 이름을 가져오는 데 실패한 경우:

예시 № 208{
  hero(episode: $episode) {
    name
    heroFriends: friends {
      id
      name
    }
  }
}

응답 예시는 다음과 같습니다:

예시 № 209{
  "errors": [
    {
      "message": "ID 1002의 캐릭터의 이름을 가져올 수 없습니다.",
      "locations": [{ "line": 6, "column": 7 }],
      "path": ["hero", "heroFriends", 1, "name"]
    }
  ],
  "data": {
    "hero": {
      "name": "R2-D2",
      "heroFriends": [
        {
          "id": "1000",
          "name": "Luke Skywalker"
        },
        {
          "id": "1002",
          "name": null
        },
        {
          "id": "1003",
          "name": "Leia Organa"
        }
      ]
    }
  }
}

오류가 발생한 필드가 Non-Null로 선언되었다면, null 결과가 다음 nullable 필드로 전파됩니다. 이 경우 오류의 path에는 오류가 발생한 실제 결과 필드의 전체 경로가 포함되어야 하며, 그 필드가 응답에 나타나지 않아도 됩니다.

예를 들어, 위의 name 필드가 Non-Null 타입으로 선언된 경우 결과는 다르게 나타나지만 보고되는 오류는 동일합니다:

예시 № 210{
  "errors": [
    {
      "message": "ID 1002의 캐릭터의 이름을 가져올 수 없습니다.",
      "locations": [{ "line": 6, "column": 7 }],
      "path": ["hero", "heroFriends", 1, "name"]
    }
  ],
  "data": {
    "hero": {
      "name": "R2-D2",
      "heroFriends": [
        {
          "id": "1000",
          "name": "Luke Skywalker"
        },
        null,
        {
          "id": "1003",
          "name": "Leia Organa"
        }
      ]
    }
  }
}

GraphQL 서비스는 오류에 extensions 키의 추가 항목을 제공할 수 있습니다. 이 항목이 있으면 값은 반드시 맵이어야 하며, 구현자는 추가 정보를 자유롭게 포함할 수 있고, 그 내용에는 아무런 제한이 없습니다.

예시 № 211{
  "errors": [
    {
      "message": "ID 1002의 캐릭터의 이름을 가져올 수 없습니다.",
      "locations": [{ "line": 6, "column": 7 }],
      "path": ["hero", "heroFriends", 1, "name"],
      "extensions": {
        "code": "CAN_NOT_FETCH_BY_ID",
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]
}

GraphQL 서비스는 오류 형식에 추가 항목을 제공해서는 안 됩니다. 향후 명세에 추가될 항목과 충돌할 수 있기 때문입니다.

참고 이전 명세에서는 오류의 extensions 항목을 설명하지 않았습니다. 명세에 없는 항목이 있다고 해서 위반은 아니지만, 권장하지 않습니다.
반례 № 212{
  "errors": [
    {
      "message": "ID 1002의 캐릭터의 이름을 가져올 수 없습니다.",
      "locations": [{ "line": 6, "column": 7 }],
      "path": ["hero", "heroFriends", 1, "name"],
      "code": "CAN_NOT_FETCH_BY_ID",
      "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
    }
  ]
}

7.1.7확장

"extensions" 항목은 실행 결과요청 오류 결과에 존재할 수 있으며, 값은 반드시 맵이어야 합니다. 이 항목은 구현자가 프로토콜을 확장하기 위한 용도로 사용되며, 내용에는 제한이 없습니다.

7.1.8추가 항목

프로토콜의 향후 변경이 기존 서비스 및 클라이언트를 깨트리지 않도록, 실행 결과요청 오류 결과 맵에는 위에서 설명한 항목 외의 다른 항목이 포함되어서는 안 됩니다. 클라이언트는 위에서 설명하지 않은 항목들은 무시해야 합니다.

7.2직렬화 형식

GraphQL은 특정 직렬화 형식을 요구하지 않습니다. 그러나 클라이언트는 GraphQL 응답의 주요 원시 타입을 지원하는 직렬화 형식을 사용해야 합니다. 특히 직렬화 형식은 다음 네 가지 원시 타입을 반드시 표현할 수 있어야 합니다:

  • 맵(Map)
  • 리스트(List)
  • 문자열(String)
  • 널(Null)

직렬화 형식은 또한 Boolean, Int, Float, Enum 값 등 GraphQL 일반 스칼라 타입 각각을 표현할 수 있어야 하며, 직접 지원하지 않으면 문자열 또는 더 단순한 원시 타입을 대신 사용할 수 있습니다:

  • 불리언(Boolean)
  • 정수(Int)
  • 실수(Float)
  • 열거값(Enum Value)

이 목록은 직렬화 형식이 인코딩할 수 있는 모든 경우의 포괄적 목록이 아닙니다. 예를 들어 날짜, 시간, URI, 다른 정밀도의 숫자를 나타내는 사용자 정의 스칼라는 해당 직렬화 형식이 지원하는 형식으로 자유롭게 표현될 수 있습니다.

7.2.1JSON 직렬화

JSON은 GraphQL에서 가장 널리 사용되는 직렬화 형식입니다. 그러나 GraphQL은 특정 직렬화 형식을 요구하지 않습니다.

GraphQL 응답을 JSON으로 직렬화하는 경우, 다음 JSON 값으로 관련 GraphQL 값을 인코딩해야 합니다:

GraphQL 값 JSON 값
맵(Map) 객체(Object)
리스트(List) 배열(Array)
널(Null) null
문자열(String) 문자열(String)
불리언(Boolean) true 또는 false
정수(Int) 숫자(Number)
실수(Float) 숫자(Number)
열거값(Enum Value) 문자열(String)
참고 일관성과 표기 용이성을 위해, 예시들은 이 문서 전반에 걸쳐 JSON 형식으로 제공됩니다.

7.2.2직렬화된 맵의 순서

선택 집합의 평가 결과는 순서가 있으므로, 결과의 직렬화된 맵도 이 순서를 유지해야 하며, 필드는 선택 집합 실행에서 요청된 순서대로 맵 항목으로 기록되어야 합니다. 요청된 필드가 응답에 동일한 순서로 나타나면, 디버깅 시 인간이 읽기 편리하고, 속성 순서를 예측할 수 있어 응답 파싱이 더 효율적이 됩니다.

순서 있는 맵을 표현하는 직렬화 형식은 CollectFields()에서 정의된 대로 요청된 필드의 순서를 유지해야 합니다. 순서가 암시적으로만 표현되는 형식(JSON 등)도 텍스트 순서로 요청된 필드 순서를 유지해야 합니다.

예를 들어 요청이 { name, age }라면, GraphQL 서비스는 JSON으로 { "name": "Mark", "age": 30 }로 응답해야 하며, { "age": 30, "name": "Mark" }로 응답해서는 안 됩니다.

JSON 객체는 키-값 쌍의 순서 없는 컬렉션으로 지정되어 있지만, 쌍은 순서 있게 표현됩니다. 즉, { "name": "Mark", "age": 30 }{ "age": 30, "name": "Mark" }는 같은 값을 인코딩하지만, 속성 순서는 명백히 다릅니다.

참고 이는 JSON 명세 위반이 아니며, 클라이언트는 여전히 응답 내 객체를 순서 없는 맵으로 해석하여 올바른 값을 획득할 수 있습니다.

A부록: 준수

GraphQL을 준수하는 구현체는 모든 규범적 요구사항을 반드시 충족해야 합니다. 준수 요건은 이 문서에서 설명적인 진술과 명확한 의미를 가진 핵심 키워드를 통해 기술됩니다.

핵심 키워드 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”은 이 문서의 규범적인 부분에서 IETF RFC 2119에 설명된 대로 해석되어야 합니다. 이러한 키워드는 소문자로 나타날 수 있으며 명시적으로 비규범적으로 선언되지 않는 한 같은 의미를 지닙니다.

GraphQL을 준수하는 구현체는 추가 기능을 제공할 수 있지만, 명확히 허용되지 않은 곳이나 비준수로 이어질 수 있는 방식으로 제공해서는 안 됩니다.

적합한 알고리즘

명령문 방식으로 작성된 알고리즘 단계(예: “resolver를 호출한 결과를 반환한다”)는 그 알고리즘이 속한 전체 알고리즘과 동일한 수준의 요구 사항으로 해석됩니다. 알고리즘 단계에서 참조하는 다른 알고리즘(예: “CompleteValue()를 호출한 결과를 completedResult로 한다”) 또한 해당 단계가 포함된 알고리즘과 최소한 동일한 수준의 요구를 가집니다.

알고리즘과 데이터 컬렉션으로 표현되는 준수 요건은 구현체가 동일한 결과를 산출하는 한 어떤 방식으로든 이 명세를 준수할 수 있습니다. 이 문서의 알고리즘은 이해하기 쉽게 작성되었습니다. 구현자는, 동등하지만 최적화된 구현을 포함하는 것을 권장합니다.

알고리즘, 데이터 컬렉션, 기타 표기법에 대한 자세한 내용은 부록 A를 참고하세요.

비규범적 부분

명시적으로 비규범적으로 선언된 부분을 제외하면, 이 문서의 모든 내용은 규범적입니다.

예시는 규범적이지 않으며, 도입된 개념이나 명세의 규범적인 동작을 쉽게 이해할 수 있도록 제시됩니다. 예시는 문자 그대로 “예를 들어” 식으로 소개되거나 아래와 같이 예시 또는 반례 블록에 따로 제공됩니다:

예시 № 213이것은 비규범적 예시입니다.
반례 № 214이것은 비규범적 반례입니다.

노트는 규범적이지 않으며, 의도 명확화, 엣지케이스와 주의사항 알림, 구현 중 흔히 제기되는 질문의 해답 제공을 위해 제시됩니다. 노트는 “참고: ”라는 문장으로 도입되거나 아래와 같이 note 블록에 분리되어 있습니다:

참고 이것은 비규범적 노트의 예시입니다.

B부록: 표기법 규칙

이 명세 문서에서는 언어 문법, 의미론, 런타임 알고리즘 등 기술적 개념을 설명하는 여러 표기법 규칙을 사용합니다.

이 부록은 모호성을 방지하기 위해 표기법을 좀 더 자세하게 설명합니다.

B.1문맥 자유 문법

문맥 자유 문법은 여러 개의 생성 규칙으로 구성됩니다. 각 생성 규칙은 “비터미널”이라는 추상 기호를 좌변에 가지며, 비터미널 기호와/또는 터미널 문자의 0개 이상 시퀀스를 우변으로 가질 수 있습니다.

단일 목표 비터미널 기호에서 시작해 문맥 자유 문법은 언어 집합을 기술합니다. 이는 목표 시퀀스 내 비터미널을 반복적으로 그 정의된 시퀀스 중 하나로 치환해 모든 비터미널 기호가 터미널 문자로 치환될 때까지 이루어진 문자 시퀀스 집합입니다.

터미널은 이 문서에서 두 가지 형태의 고정폭 서체로 표현됩니다: 특정 유니코드 문자 또는 유니코드 문자 시퀀스(= 또는 terminal), 그리고 특정 유니코드 코드 포인트를 설명하는 산문 "Space (U+0020)". 유니코드 문자 시퀀스는 문법에서만 나타나며 그 특정 시퀀스의 Name 토큰을 의미합니다.

비터미널 생성 규칙은 아래와 같은 표기법으로 표현됩니다(단일 정의의 경우):

NonTerminalWithSingleDefinition
NonTerminalterminal

정의 리스트를 가진 생성 규칙은 다음 표기법을 사용합니다:

NonTerminalWithManyDefinitions
OtherNonTerminalterminal
terminal

정의가 자기 자신을 참조하는 경우 반복 패턴을 나타냅니다. 예:

B.2렉시컬 및 문법적 문법

GraphQL 언어는 문법적 문법에서 정의되며, 터미널 기호는 토큰입니다. 토큰은 소스 문자 패턴을 매칭하는 렉시컬 문법에서 정의됩니다. 소스 텍스트 유니코드 문자 시퀀스를 파싱하면 먼저 렉시컬 문법에 따라 렉시컬 토큰 시퀀스를 생성하고, 이어서 문법적 문법에 따라 추상 구문 트리(AST)를 생성합니다.

렉시컬 문법 생산식은 터미널 유니코드 문자 패턴을 갖는 비터미널 “토큰”을 기술합니다. 렉시컬 문법 생산식 내에서는 터미널 유니코드 문자 사이에 “공백” 등 무시되는 문자가 올 수 없습니다. 렉시컬 문법 생산식은 이중 콜론 :: 정의로 구분됩니다.

Word
Letterlist

문법적 문법 생산식은 터미널 토큰의 패턴으로 비터미널 “규칙”을 기술합니다. Whitespace 및 기타 Ignored 시퀀스는 터미널 Token 앞뒤에 올 수 있습니다. 문법적 문법 생산식은 싱글 콜론 : 정의로 구분됩니다.

Sentence
Wordlist.

B.3문법 표기법

이 명세에서는 optional, 반복, 또는 비터미널 정의의 파라미터화 변경과 같은 공통 패턴을 설명하기 위해 몇 가지 추가 표기법을 사용합니다. 이 섹션에서는 이러한 단축 표기와 문맥 자유 문법에서의 확장 정의를 설명합니다.

제한 조건

문법 생산식은 “but not” 표현과 제외시킬 확장을 명시하여 특정 확장이 허용되지 않음을 지정할 수 있습니다.

예를 들어, 아래 생산식은 SafeWord 비터미널은 Word가 자리할 수 있는 모든 문자 시퀀스로 대체될 수 있지만, 동일한 시퀀스가 SevenCarlinWords로는 대체될 수 없는 경우에만 해당함을 의미합니다.

SafeWord
WordSevenCarlinWords

문법은 “but not” 뒤에 “or”로 구분된 여러 제한을 늘어놓을 수도 있습니다.

예시:

NonBooleanName
Nametruefalse
룩어헤드 제한

문법 생산식은 NotAllowed 패턴을 사용하여 특정 문자나 토큰이 뒤따르는 것을 허용하지 않는다고 명시할 수 있습니다. 룩어헤드 제한은 종종 문법의 모호함을 방지하는 데 사용됩니다.

아래 예시는 Letterlist가 반드시 greedy임을 명확히 하며, Word 뒤에 추가 Letter가 올 수 없음을 의미합니다.

선택 및 리스트

아래 첨자 “Symbolopt”는 그 심볼이 포함된 경우와 그렇지 않은 경우 두 가지 시퀀스의 약어로 사용됩니다.

예시:

Sentence
NounVerbAdverbopt

이는 다음의 약어입니다:

Sentence
NounVerbAdverb
NounVerb

아래 첨자 “Symbollist”는 심볼 하나 이상 반복 리스트 패턴의 약어이며, 추가 재귀 생산식으로 표현됩니다.

예시:

Book
CoverPagelistCover

이는 다음의 약어입니다:

Book
CoverPage_listCover
Page_list
Page
파라미터화 문법 생산식

기호 정의의 첨자 파라미터({})는 SymbolParam 같이 두 개의 심볼 정의(파라미터 명이 붙은 것과 없는 것)의 약어입니다. 동일한 첨자가 붙은 심볼은 그 변형 정의의 약어이며, 파라미터가 “?”로 시작하면 동일 파라미터의 심볼 정의에 있을 때 사용됩니다. [+Param], [~Param] 접두어로 일부 시퀀스를 조건적으로 포함/제외할 수 있습니다.

예시:

ExampleParam
A
BParam
CParam
ParamD
ParamE

다음의 약어입니다:

Example
A
B_param
C
E
Example_param
A
B_param
C_param
D

B.4문법 의미론

이 명세는 많은 문법 생산식의 의미값을 알고리즘 단계 목록 형태로 기술합니다.

예를 들면, 파서가 문자열 리터럴을 해석하는 방식을 아래와 같이 설명합니다:

StringValue
""
  1. 빈 유니코드 문자 시퀀스를 반환합니다.
StringValue
  1. 모든 StringCharacter의 유니코드 문자 값 시퀀스를 반환합니다.

B.5알고리즘

이 명세는 정적/런타임 의미론에 사용되는 몇 가지 알고리즘을 설명하며, 함수와 유사한 구문, 알고리즘 이름, 인자, 순서대로 실행할 단계 목록 등으로 정의합니다. 각 단계는 값 참조, 조건 확인, 다른 알고리즘 호출, 반환값 산출 등 역할을 합니다.

예를 들어 아래 예시에서는 Fibonacci란 알고리즘이 number라는 인자를 받고, 알고리즘 단계는 피보나치 수열의 다음 숫자를 산출합니다:

Fibonacci(number)
  1. number0이면:
    1. 1을 반환합니다.
  2. number1이면:
    1. 2를 반환합니다.
  3. previousNumbernumber - 1로 둡니다.
  4. previousPreviousNumbernumber - 2로 둡니다.
  5. Fibonacci(previousNumber) + Fibonacci(previousPreviousNumber)을 반환합니다.
참고 이 문서의 알고리즘들은 이해하기 쉽게 작성되었습니다. 구현자는 동등하되 더 최적화된 구현을 적용하는 것이 권장됩니다.

B.6데이터 컬렉션

이 명세 내 알고리즘은 규범적 구조, 고유성, 순서 요건을 표현하기 위해 추상 데이터 컬렉션 타입을 참조합니다. 알고리즘 내 임시 데이터 컬렉션은 기대 동작을 명확히 설명하기 위해 이 타입들을 사용하지만, 구현자는 동등한 동작을 보장하면서 더 최적화된 구현을 선택할 수 있습니다. 어떠한 자료구조라도 기대되는 조건만 충족한다면 사용할 수 있습니다.

리스트

리스트는 중복값이 올 수 있는 순서가 있는 값 집합입니다. 값이 리스트에 추가될 때 기존 값 뒤에 순서대로 배치됩니다.

집합

집합은 중복값을 포함할 수 없는 값의 집합입니다.

순서집합은 순서가 정의된 집합입니다. 새 값이 기존 값에 포함되어 있지 않을 때, 기존 값 뒤에 순서대로 추가됩니다.

은 각 항목이 키와 값으로 이루어진 컬렉션입니다. 각 항목은 고유한 키를 가져야 하며, 그 키로 직접 참조할 수 있습니다.

순서맵은 순서가 정의된 맵입니다. 기존에 해당 키가 없는 새 항목은 기존 항목 뒤에 순차적으로 배치됩니다.

참고 이 명세는 엄격하게 요구될 때만 순서 데이터 컬렉션을 정의합니다. 순서가 관찰가능하다면 구현체는 더 잘 읽히고 안정적인 출력을 위해 이 순서를 보존해야 합니다. 예를 들어 입력 문자열에 대해 문법을 적용해서 집합 요소를 얻는 경우, 직렬화 시 해당 요소를 소스순서대로 내보내야 합니다.

C부록: 문법 요약

C.1원본 텍스트

SourceCharacter
임의의 유니코드 스칼라 값

C.2무시되는 토큰

UnicodeBOM
바이트 순서 표시(Byte Order Mark, U+FEFF)
Whitespace
수평 탭(U+0009)
공백(U+0020)
LineTerminator
개행(New Line, U+000A)
캐리지 리턴(Carriage Return, U+000D)개행(New Line, U+000A)
캐리지 리턴(Carriage Return, U+000D)개행(New Line, U+000A)
Comma
,

C.3렉시컬 토큰

Punctuator
! $ & ( ) ... : = @ [ ] { | }
Letter
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m
n o p q r s t u v w x y z
Digit
0 1 2 3 4 5 6 7 8 9
Sign
+ -
HexDigit
0 1 2 3 4 5 6 7 8 9
A B C D E F
a b c d e f
EscapedCharacter
" \ / b f n r t
참고 블록 문자열 값은 초기 및 끝의 빈 줄을 제외하고, BlockStringValue()에 따라 균일한 들여쓰기를 제거한 것으로 해석됩니다.

C.4문서 구문

OperationType
query mutation subscription
ArgumentsConst
(ArgumentConstlist)
ArgumentConst
Name:ValueConst
BooleanValue
true false
NullValue
null
EnumValue
Nametruefalsenull
ListValueConst
[]
[ValueConstlist]
ObjectValueConst
{}
{ObjectFieldConstlist}
ObjectFieldConst
Name:ValueConst
DirectivesConst
DirectiveConstlist
DirectiveConst
@NameArgumentsConstopt
SchemaExtension
extendschemaDirectivesConstopt{RootOperationTypeDefinitionlist}
extendschemaDirectivesConst{
ExecutableDirectiveLocation
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
TypeSystemDirectiveLocation
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION

C.5스키마 좌표 표기법

참고 스키마 좌표는 Ignored를 포함해서는 안 됩니다.

D부록: 타입 시스템 정의

이 부록에는 이 문서에 명시된 모든 타입 시스템 정의가 나열되어 있습니다.

타입, 필드, 인자, 값 및 디렉티브의 순서는 비규범적입니다.

scalar String

scalar Int

scalar Float

scalar Boolean

scalar ID

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @deprecated(
  reason: String! = "No longer supported"
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE

directive @specifiedBy(url: String!) on SCALAR

directive @oneOf on INPUT_OBJECT

type __Schema {
  description: String
  types: [__Type!]!
  queryType: __Type!
  mutationType: __Type
  subscriptionType: __Type
  directives: [__Directive!]!
}

type __Type {
  kind: __TypeKind!
  name: String
  description: String
  specifiedByURL: String
  fields(includeDeprecated: Boolean! = false): [__Field!]
  interfaces: [__Type!]
  possibleTypes: [__Type!]
  enumValues(includeDeprecated: Boolean! = false): [__EnumValue!]
  inputFields(includeDeprecated: Boolean! = false): [__InputValue!]
  ofType: __Type
  isOneOf: Boolean
}

enum __TypeKind {
  SCALAR
  OBJECT
  INTERFACE
  UNION
  ENUM
  INPUT_OBJECT
  LIST
  NON_NULL
}

type __Field {
  name: String!
  description: String
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
  type: __Type!
  isDeprecated: Boolean!
  deprecationReason: String
}

type __InputValue {
  name: String!
  description: String
  type: __Type!
  defaultValue: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __EnumValue {
  name: String!
  description: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __Directive {
  name: String!
  description: String
  isRepeatable: Boolean!
  locations: [__DirectiveLocation!]!
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
}

enum __DirectiveLocation {
  QUERY
  MUTATION
  SUBSCRIPTION
  FIELD
  FRAGMENT_DEFINITION
  FRAGMENT_SPREAD
  INLINE_FRAGMENT
  VARIABLE_DEFINITION
  SCHEMA
  SCALAR
  OBJECT
  FIELD_DEFINITION
  ARGUMENT_DEFINITION
  INTERFACE
  UNION
  ENUM
  ENUM_VALUE
  INPUT_OBJECT
  INPUT_FIELD_DEFINITION
}

§색인

  1. Alias
  2. AreTypesCompatible()
  3. Argument
  4. ArgumentCoordinate
  5. Arguments
  6. ArgumentsDefinition
  7. BlockString
  8. BlockStringCharacter
  9. BlockStringValue()
  10. BooleanValue
  11. 내장 디렉티브
  12. CoerceArgumentValues()
  13. CoerceResult()
  14. CoerceVariableValues()
  15. 수집된 필드 맵
  16. CollectFields()
  17. CollectSubfields()
  18. CollectSubscriptionFields()
  19. Comma
  20. Comment
  21. CommentChar
  22. CompleteValue()
  23. 포함 요소
  24. CreateSourceEventStream()
  25. 사용자 정의 디렉티브
  26. 기본 루트 타입 이름
  27. DefaultValue
  28. Definition
  29. Description
  30. DetectFragmentCycles()
  31. Digit
  32. Directive
  33. DirectiveArgumentCoordinate
  34. DirectiveCoordinate
  35. DirectiveDefinition
  36. DirectiveLocation
  37. DirectiveLocations
  38. Directives
  39. Document
  40. DoesFragmentTypeApply()
  41. EnumTypeDefinition
  42. EnumTypeExtension
  43. EnumValue
  44. EnumValueDefinition
  45. EnumValuesDefinition
  46. EscapedCharacter
  47. EscapedUnicode
  48. 이벤트 스트림
  49. ExecutableDefinition
  50. ExecutableDirectiveLocation
  51. ExecutableDocument
  52. ExecuteCollectedFields()
  53. ExecuteField()
  54. ExecuteMutation()
  55. ExecuteQuery()
  56. ExecuteRequest()
  57. ExecuteRootSelectionSet()
  58. ExecuteSubscriptionEvent()
  59. 실행 오류
  60. 실행 결과
  61. ExponentIndicator
  62. ExponentPart
  63. Field
  64. 필드 집합
  65. FieldDefinition
  66. FieldsDefinition
  67. FieldsInSetCanMerge()
  68. FloatValue
  69. FractionalPart
  70. FragmentDefinition
  71. FragmentName
  72. FragmentSpread
  73. GetOperation()
  74. GetPossibleTypes()
  75. HexDigit
  76. Ignored
  77. ImplementsInterfaces
  78. InlineFragment
  79. 입력 객체
  80. InputFieldDefaultValueHasCycle()
  81. InputFieldsDefinition
  82. InputObjectDefaultValueHasCycle()
  83. InputObjectTypeDefinition
  84. InputObjectTypeExtension
  85. InputValueDefinition
  86. IntegerPart
  87. InterfaceTypeDefinition
  88. InterfaceTypeExtension
  89. IntValue
  90. IsInputType()
  91. IsNonNullPosition()
  92. IsOutputType()
  93. IsSubType()
  94. IsValidImplementation()
  95. IsValidImplementationFieldType()
  96. IsVariableUsageAllowed()
  97. Letter
  98. 줄 구분자
  99. ListType
  100. ListValue
  101. MapSourceToResponseEvent()
  102. MemberCoordinate
  103. Name
  104. NameContinue
  105. NamedType
  106. NameStart
  107. NegativeSign
  108. NonNullType
  109. NonZeroDigit
  110. NullValue
  111. ObjectField
  112. ObjectTypeDefinition
  113. ObjectTypeExtension
  114. ObjectValue
  115. OneOf 입력 객체
  116. OperationDefinition
  117. OperationType
  118. Punctuator
  119. 요청
  120. 요청 오류
  121. 요청 오류 결과
  122. ResolveAbstractType()
  123. ResolveFieldEventStream()
  124. ResolveFieldValue()
  125. 응답
  126. 응답 이름
  127. 응답 경로
  128. 응답 위치
  129. 응답 스트림
  130. 루트 연산 타입
  131. 루트 선택 집합
  132. RootOperationTypeDefinition
  133. SameResponseShape()
  134. scalar specification URL
  135. ScalarTypeDefinition
  136. ScalarTypeExtension
  137. 스키마 좌표
  138. 스키마 요소
  139. SchemaCoordinate
  140. SchemaCoordinatePunctuator
  141. SchemaCoordinateToken
  142. SchemaDefinition
  143. SchemaExtension
  144. Selection
  145. 선택 집합
  146. SelectionSet
  147. Sign
  148. 소스 스트림
  149. SourceCharacter
  150. StringCharacter
  151. StringValue
  152. Subscribe()
  153. Token
  154. Type
  155. TypeCondition
  156. TypeCoordinate
  157. TypeDefinition
  158. TypeExtension
  159. TypeSystemDefinition
  160. TypeSystemDefinitionOrExtension
  161. TypeSystemDirectiveLocation
  162. TypeSystemDocument
  163. TypeSystemExtension
  164. TypeSystemExtensionDocument
  165. 유니코드 텍스트
  166. UnicodeBOM
  167. UnionMemberTypes
  168. UnionTypeDefinition
  169. UnionTypeExtension
  170. Unsubscribe()
  171. Value
  172. Variable
  173. VariableDefinition
  174. VariablesDefinition
  175. 공백
  1. 1개요
  2. 2언어
    1. 2.1소스 텍스트
      1. 2.1.1공백 문자
      2. 2.1.2줄 끝
      3. 2.1.3주석
      4. 2.1.4의미 없는 쉼표
      5. 2.1.5어휘 토큰
      6. 2.1.6무시된 토큰
      7. 2.1.7구두점
      8. 2.1.8이름
    2. 2.2설명
    3. 2.3문서
    4. 2.4연산
    5. 2.5선택 집합
    6. 2.6필드
    7. 2.7인자
    8. 2.8필드 별칭
    9. 2.9프래그먼트
      1. 2.9.1타입 조건
      2. 2.9.2인라인 프래그먼트
    10. 2.10입력 값
      1. 2.10.1정수 값
      2. 2.10.2실수 값
      3. 2.10.3불리언 값
      4. 2.10.4문자열 값
      5. 2.10.5Null 값
      6. 2.10.6열거값
      7. 2.10.7리스트 값
      8. 2.10.8입력 객체 값
    11. 2.11변수
    12. 2.12타입 참조
    13. 2.13지시문
    14. 2.14스키마 좌표
  3. 3타입 시스템
    1. 3.1타입 시스템 확장
    2. 3.2타입 시스템 설명
    3. 3.3스키마
      1. 3.3.1루트 연산 타입
      2. 3.3.2스키마 확장
    4. 3.4타입
      1. 3.4.1래핑 타입
      2. 3.4.2입력 및 출력 타입
      3. 3.4.3타입 확장
    5. 3.5스칼라
      1. 3.5.1Int
      2. 3.5.2Float
      3. 3.5.3String
      4. 3.5.4Boolean
      5. 3.5.5ID
      6. 3.5.6Scalar 확장
    6. 3.6객체
      1. 3.6.1필드 인자
      2. 3.6.2필드 폐기
      3. 3.6.3객체 확장
    7. 3.7인터페이스
      1. 3.7.1인터페이스 확장
    8. 3.8유니온
      1. 3.8.1유니온 확장
    9. 3.9열거형
      1. 3.9.1열거형 확장
    10. 3.10입력 객체
      1. 3.10.1OneOf 입력 객체
      2. 3.10.2입력 객체 확장
    11. 3.11리스트
    12. 3.12Non-Null
      1. 3.12.1리스트와 Non-Null 결합
    13. 3.13지시문
      1. 3.13.1@skip
      2. 3.13.2@include
      3. 3.13.3@deprecated
      4. 3.13.4@specifiedBy
      5. 3.13.5@oneOf
  4. 4내성
    1. 4.1타입 이름 내성
    2. 4.2스키마 내성
      1. 4.2.1The __Schema 타입
      2. 4.2.2The __Type 타입
      3. 4.2.3The __Field 타입
      4. 4.2.4The __InputValue 타입
      5. 4.2.5The __EnumValue 타입
      6. 4.2.6The __Directive 타입
  5. 5검증
    1. 5.1문서
      1. 5.1.1실행 가능한 정의
    2. 5.2연산
      1. 5.2.1모든 연산 정의
        1. 5.2.1.1연산 타입의 존재
      2. 5.2.2이름 있는 연산 정의
        1. 5.2.2.1연산 이름의 고유성
      3. 5.2.3익명 연산 정의
        1. 5.2.3.1단독 익명 연산
      4. 5.2.4구독 연산 정의
        1. 5.2.4.1단일 루트 필드
    3. 5.3필드
      1. 5.3.1필드 선택
      2. 5.3.2필드 선택 병합
      3. 5.3.3리프 필드 선택
    4. 5.4인자
      1. 5.4.1인자 이름
      2. 5.4.2인자 고유성
      3. 5.4.3필수 인자
    5. 5.5프래그먼트
      1. 5.5.1프래그먼트 선언
        1. 5.5.1.1프래그먼트 이름의 고유성
        2. 5.5.1.2프래그먼트 스프레드 타입 존재
        3. 5.5.1.3오브젝트, 인터페이스 또는 유니언 타입에 대한 프래그먼트
        4. 5.5.1.4프래그먼트는 사용되어야 함
      2. 5.5.2프래그먼트 스프레드
        1. 5.5.2.1프래그먼트 스프레드 대상 정의됨
        2. 5.5.2.2프래그먼트 스프레드는 사이클을 형성하면 안 됨
        3. 5.5.2.3프래그먼트 스프레드가 가능해야 함
          1. 5.5.2.3.1오브젝트 범위에서의 오브젝트 스프레드
          2. 5.5.2.3.2오브젝트 범위에서의 추상 스프레드
          3. 5.5.2.3.3추상 범위에서의 오브젝트 스프레드
          4. 5.5.2.3.4추상 범위에서의 추상 스프레드
    6. 5.6
      1. 5.6.1올바른 타입의 값
      2. 5.6.2입력 객체 필드 이름
      3. 5.6.3입력 객체 필드 고유성
      4. 5.6.4입력 객체 필수 필드
    7. 5.7지시문
      1. 5.7.1지시문 정의됨
      2. 5.7.2지시문이 유효한 위치에 있음
      3. 5.7.3위치별 지시문 고유성
    8. 5.8변수
      1. 5.8.1변수 이름의 고유성
      2. 5.8.2변수는 입력 타입이어야 함
      3. 5.8.3모든 변수 사용이 정의되어 있음
      4. 5.8.4모든 변수 사용
      5. 5.8.5모든 변수 사용은 허용되어야 함
  6. 6실행
    1. 6.1요청 실행
      1. 6.1.1요청 검증
      2. 6.1.2변수 값 강제 변환
    2. 6.2연산 실행
      1. 6.2.1쿼리
      2. 6.2.2뮤테이션
      3. 6.2.3구독
        1. 6.2.3.1소스 스트림
        2. 6.2.3.2응답 스트림
        3. 6.2.3.3구독 취소
    3. 6.3선택 집합 실행
      1. 6.3.1루트 선택 집합 실행
      2. 6.3.2필드 수집
      3. 6.3.3수집된 필드 실행
      4. 6.3.4일반 실행과 순차 실행
    4. 6.4필드 실행
      1. 6.4.1필드 인자 강제 변환
      2. 6.4.2값 해결
      3. 6.4.3값 완료
      4. 6.4.4실행 오류 처리
  7. 7응답
    1. 7.1응답 형식
      1. 7.1.1실행 결과
      2. 7.1.2응답 스트림
      3. 7.1.3요청 오류 결과
      4. 7.1.4응답 위치
      5. 7.1.5데이터
      6. 7.1.6오류
      7. 7.1.7확장
      8. 7.1.8추가 항목
    2. 7.2직렬화 형식
      1. 7.2.1JSON 직렬화
      2. 7.2.2직렬화된 맵의 순서
  8. A부록: 준수
  9. B부록: 표기법 규칙
    1. B.1문맥 자유 문법
    2. B.2렉시컬 및 문법적 문법
    3. B.3문법 표기법
    4. B.4문법 의미론
    5. B.5알고리즘
    6. B.6데이터 컬렉션
  10. C부록: 문법 요약
    1. C.1원본 텍스트
    2. C.2무시되는 토큰
    3. C.3렉시컬 토큰
    4. C.4문서 구문
    5. C.5스키마 좌표 표기법
  11. D부록: 타입 시스템 정의
  12. E부록: 저작권 및 라이선스
  13. §색인