Dev 달팽이 @_''

[GraphQL] 공식문서 - Schemas and Types 본문

GraphQL

[GraphQL] 공식문서 - Schemas and Types

다본죽 2022. 10. 5. 14:45

Schemas and Types

 

Object types and fields

 - GraphQL의 가장 기본 컴포넌트는 object type

 - Object type은 서비스에서 fetch할 수 있는 객체와 어떤 필드가 있는지로 구성

 - 예 :

type Character {
  name: String!
  appearsIn: [Episode!]!
}
  • Character는 GraphQL Object Type
  • name과 appearsIn은 Character type의 필드
  • String은 scalar 타입으로 query에서 하위 선택을 가질 수 없음
  • String! 은 해당 필드가 null이 될 수 없음을 뜻
  • [Episode!]! 는 Episode 객체의 배열을 표현, Episode! 이므로 항상 0개 또는 하나 이상의 배열이 리턴

Arguments

 - GraphQL에 모든 필드는 0개 또는 여러 개의 arguments를 가질 수 있음

 - 모든 Arguments는 이름을 가지고 있음

 

The Query and Mutation types

 - Schema에는 보통 일반 object type

 - 그러나 2개의 특별한 타입이 존재

schema {
 query: Query
 mutation: Mutation
}

 - 모든 GraphQL 서비스는 query type과 mutation type(선택)을 가지고 있음

 - 두 타입은 일반적인 object type과 같음

 - 그러나 모든 GraphQL query의 entry point(첫 부분)에서 정의된다는 점이 특별

 - 예 :

 

Request

query {
  hero {
    name
  }
  droid(id: "2000") {
    name
  }
}

Response

{
  "data": {
    "hero": {
      "name": "R2-D2"
    },
    "droid": {
      "name": "C-3PO"
    }
  }
}
type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}

 - 위의 GraphQL service의 의미는 Query type에 있어야하고 hero와 droid 필드가 포함되어 있다는 의미

 - Mutation도 비슷한 방법으로 동작

 - Muation type과 포함되는 필드를 정의하고 query에서 호출하여 사용

 

Scalar  types

 - GraphQL object type은 이과 필드를 가지고 있음

 - 그러나 이 필드들 역시 어느 시점에서는 구체적인 데이터를 다뤄야 함

 - 이 때, scalar 타입이 쓰일 수 있고 이들은 query의 leaves라고 함

 - GraphQL의 기본 Defualt Scalar

  • Int : A signed 32-bit integerd
  • Float : A signed double-precision floating-point valuee
  • String : A UTF-8 character sequence
  • Boolean : true or false
  • ID : unique identifier, 종종 object나 cache key를 refetch할 때 사용

 - 대부분은 custom scalar types를 정의해서 사용

 - 예 :

scalar Date

 - 어떻게 타입이 serialized/deserialized/validated 되어야 하는지를 정의하는 것은 구현에 달려있음

 

Enumeration types

 - Enums이라 불리는 enumeration types은 특별한 종류의 scalar가 존재

 - enum의 효과

  1. 이 타입의 모든 arguments가 허용된 값 중 하낭인지 유효성 검사
  2. 어떠한 필드가 항상 유한한 값들의 집합 중 하나가 될 것이라는 것을 타입 시스템을 통해 전달

 - 예 :

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

 - 위의 의미는 schema에서 Episode type을 사용하며, NEWHOPE, EMPIRE, JEDI 중 하나의 값

 

Lists and Non-Null

 - Object types, scalars, enums은 GraphQL에서 정의할 수 있는 유일한 타입

 - schema의 다른 파트에서 타입을 쓸 때나, query variable 선언에서 해당 값들의 유효성에 영향을 주는 추가적인 타입 modifier들을 적용 가능

 - 예 :

type Character {
  name: String!
  appearsIn: [Episode]!
}

 - 위의 예에서 String 뒤에 !를 추가함으로써 Non-Null 값을 명시

 - Non-Null modifier는 필드에 argument를 정의할 때에도 사용 가능

 - 예 :

 

Request

query DroidById($id: ID!) {
  droid(id: $id) {
    name
  }
}

// Variables
{
  "id": null
}

Response

{
  "errors": [
    {
      "message": "Variable \"$id\" of non-null type \"ID!\" must not be null.",
      "locations": [
        {
          "line": 1,
          "column": 17
        }
      ]
    }
  ]
}

 - List도 비슷한 방식으로 동작

 

Interfaces

 - GraphQL도 interface를 지원

 - Interface는 abstract type이며 반드시 포함해야 하는 특정 필드 집합을 정의

 - 예 :

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}

 - 위의 예에서 Character를 구현하는 어떤 타입이건 이 필드들 그리고 arguments와 리턴 타입을 똑같이 가지고 있어야 함을 의미

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}

 - 위에 예에서 둘 다 Character의 필드들을 모두 가지고 있으며 각각 추가적인 필드가 존재

 - Interface는 다른 타입이지만 객체나 객체의 셋을 리턴하고자 할 때 유용

 - 예 :

 

Request

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    primaryFunction
  }
}

// Variables
{
  "ep": "JEDI"
}

Response

{
  "errors": [
    {
      "message": "Cannot query field \"primaryFunction\" on type \"Character\". Did you mean to use an inline fragment on \"Droid\"?",
      "locations": [
        {
          "line": 4,
          "column": 5
        }
      ]
    }
  ]
}

 - 위에 예에서 error를 리턴(hero 필드는 Character 타입을 반환, episode에 따라 Human이 되거나 Droid가 될 수 있음)

 - Character interface에만 존재하는 피드만 요청 가능ㅇ

 - 특정 객체 타입에만 있는 필드를 요청하기 위해서는 inline fragment를 사용

 - 예 :

 

Request

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
  }
}

// Variables
{
  "ep": "JEDI"
}

Response

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}

Union types

 - Union type은 interface와 비슷하나 타입 간 공통 필드를 명시하지 않음

 - 예 :

union SearchResult = Human | Droid | Starship

 - Schema에서 SearchResult 타입을 리턴할 때마다 Human, Droid 또는 StarShip을 얻을 수 있음

 - union type의 구성 요소들은 반드시 구체적인 객체 타입이어야 함

 - interface 혹은 다른 union 타입 불가

 - 이 경우, union type의 SearchResult를 리턴하는 필드에 쿼리를 넣는다면, 필드에 쿼리를 하기 위해서 반드시 조건부 fragment를 사용 해야함

 - 예 :

 

Request

{
  search(text: "an") {
    __typename
    ... on Human {
      name
      height
    }
    ... on Droid {
      name
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}

Response

{
  "data": {
    "search": [
      {
        "__typename": "Human",
        "name": "Han Solo",
        "height": 1.8
      },
      {
        "__typename": "Human",
        "name": "Leia Organa",
        "height": 1.5
      },
      {
        "__typename": "Starship",
        "name": "TIE Advanced x1",
        "length": 9.2
      }
    ]
  }
}

 - __typename 필드는 클라이언트에서 서로 다른 데이터 유형을 구별할 수 있는 문자열

 - 위의 예에서 Human과 Droid는 공통 interface(Character)를 공유하므로 여러 유형에서 동일한 필드는 한 곳에서 쿼리 가능

 - 예 :

 

Request

{
  search(text: "an") {
    __typename
    ... on Character {
      name
    }
    ... on Human {
      #name
      height
    }
    ... on Droid {
      #name
      primaryFunction
    }
    ... on Starship {
      #name
      length
    }
  }
}

Response

{
  "data": {
    "search": [
      {
        "__typename": "Human",
        "name": "Han Solo",
        "height": 1.8
      },
      {
        "__typename": "Human",
        "name": "Leia Organa",
        "height": 1.5
      },
      {
        "__typename": "Starship",
        "length": 9.2
      }
    ]
  }
}

Input types

 - Arguments로 복합 Object를 넘길 수 있음

 - 특히 mutation에서 중요

 - 키워드가 type이 아니라 input

 - 예 :

input ReviewInput {
  stars: Int!
  commentary: String
}

Request

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

// Variables
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

Response

{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

 - Input 객체 타입의 필드는 input 객체 타입을 참조할 수 있지만, Schema에 input과 output 타입을 섞을 순 없음

 - Input 객체 타입은 필드에 argument를 가질 수 없음

 

'GraphQL' 카테고리의 다른 글

[GraphQL] 공식문서 - Queries and Mutations  (1) 2022.10.05