본문 바로가기
Tech/GraphQL

GraphQL 스카마 & 타입

by egas 2021. 6. 24.

스키마 & 타입

타입 시스템 

GraphQL 쿼리 언어는 기본적으로 객체의 필드를 선택한다.

{
  hero {
    name
    appearsIn
  }
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}
  1. root 객체로 시작한다.
  2. hero 필드를 선택한다.
  3. hero 에 의해 반환된 객체에 대해 name  appearIn 필드를 선택한다.

GraphQL 쿼리의 형태가 결과와 거의 일치하기 때문에 서버에 대해 모르는 상태에서 쿼리가 반환할 결과를 예측할 수 있다. 하지만 서버에 요청할 수 있는 데이터에 대한 정확한 표현을 갖는 것이 좋다. 어떤 필드를 선택할 수 있는지, 어떤 종류의 객체를 반환할 수 있는지, 하위 객체에서 사용할 수 있는 필드는 무엇인지, 이것들이 스키마가 필요한 이유이다.

 

모든 GraphQL 서비스는 해당 서비스에서 쿼리 가능한 데이터들을 완벽하게 설명하는 타입들을 정의하고, 쿼리가 들어오면 해당 스키마에 대해 유효성이 검사된 후 실행된다.

 

타입 언어

GraphQL 서비스는 어떤 언어로든 작성할 수 있다. GraphQL은 GraphQL 스키마 언어(GraphQL schema language) 를 사용한다.

 이것은 쿼리 언어와 비슷하고, GraphQL 스키마를 언어에 의존적이지 않은 방식으로 표현할 수 있게 해준다.

 

객체 타입과 필드

GraphQL 스키마의 가장 기본적인 구성 요소는 객체 타입이다. 객체 타입은 서비스에서 가져올 수 있는 객체의 종류와 그 객체의 필드를 나타낸다.

type Character {
  name: String!
  appearsIn: [Episode]!
}
type 타입 {
  필드: 스칼라
}
  • Character 는 GraphQL 객체 타입이다. 즉, 필드가 있는 타입입니다. 스키마의 대부분의 타입은 객체 타입이다.
  • name 과 appearIn 은 Character 타입의 필드이다. 즉 name 과 appearIn 은 GraphQL 쿼리의 Character 타입 어디서든 사용할 수 있는 필드이다.
  • String 은 내장된 스칼라 타입 중 하나이다. 이는 스칼라 객체로 해석되는 타입이며 쿼리에서 하위 선택을 할 수 없다. 스칼라 타입은 나중에 자세히 다룰 것이다.
  • String! 은 필드가 non-nullable 임을 의미한다. 즉, 이 필드를 쿼리할 때 GraphQL 서비스가 항상 값을 반환한다. 타입 언어에서는 이것을 느낌표로 나타낸다.
  • [Episode]!  Episode 객체의 배열(array) 을 나타낸다. 또한 non-nullable 이기 때문에 appearIn 필드를 쿼리할 때 항상(0개 이상의 아이템을 가진) 배열을 기대할 수 있다.

인자

GraphQL 객체 타입의 모든 필드는 0개 이상의 인수를 가질 수 있다. (예: 아래 length 필드)

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}

GraphQL에서 모든 인수에는 이름이 있다. 이 경우, length 필드는 하나의 인자 unit을 가진다. 인자는 옵셔널로 정의할 수도 있는데, 옵셔널일 경우 기본값을 정의할 수 있고, 위의 경우 METER 로 기본적으로 설정된다.

 

쿼리 타입 & 뮤테이션 타입

스키마 대부분의 타입은 일반 객체 타입이지만 스키마 내에는 특수한 두 가지 타입이 있다.

schema {
  query: Query
  mutation: Mutation
}

아래 GraphQL 서비스는 hero  droid 필드가 있는 Query 타입이 있어야한다.

출처: https://graphql-kr.github.io/learn/schema/

type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}

 

스키마에 대한 진입점이라는 특수한 점 이외의 쿼리 타입과 뮤테이션 타입은 다른 GraphQL 객체 타입과 동일하며 해당 필드는 정확히 동일한 방식으로 작동한다.

 

스칼라 타입

GraphQL 객체 타입은 이름과 필드를 가지지만, 어떤 시점에서 이 필드는 구체적인 데이터로 해석되어야한다. 스칼라 타입은 쿼리의 끝을 나타낸다. 다음 쿼리에서 name  appearIn 은 스칼라 타입으로 해석된다.

{
  hero {
    name
    appearsIn
  }
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}

GraphQL 에서는 스칼라 타입들이 기본 제공된다.

  • Int: 부호가 있는 32비트 정수.
  • Float: 부호가 있는 부동소수점 값.
  • String: UTF-8 문자열.
  • Boolean: true 또는 false.
  • ID: ID 스칼라 타입은 객체를 다시 요청하거나 캐시의 키로써 자주 사용되는 고유 식별자. ID 타입은 String 과 같은 방법으로 직렬화되지만, ID 로 정의하는 것은 사람이 읽을 수 있도록 하는 의도가 아니라는 것을 의미한다.

대부분의 GraphQL 구현에는 커스텀 스칼라 타입을 지정하는 방법이 있다. 예를 들면, Date 타입을 정의할 수 있다.

scalar Date

해당 타입을 직렬화, 역 직렬화, 유효성 검사하는 방법을 구현할 수 있다. 예를 들어, Date 타입을 항상 정수형 타임스탬프로 직렬화해야 한다는 것을 지정할 수 있다. 그리고 클라이언트는 모든 날짜 필드에 대해 해당 타입을 기대할 수 있을 것이다.

 

열거형 타입

Enums 라고도 하는 열거형 타입은 특정 값들로 제한되는 특별한 종류의 스칼라이다. 이를 통해 다음과 같은 작업을 할 수 있다.

 

  1. 타입의 인자가 허용된 값 중 하나임을 검증한다.
  2. 필드가 항상 값의 열거형 집합 중 하나가 될 것임을 타입 시스템을 통해 의사소통한다.

GraphQL 스키마 언어에서 열거형 타입 정의는 아래와 같다.

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

스키마에서 Episode 타입을 사용할 때마다 정확히 NEWHOPE, EMPIRE, JEDI 중 하나여야한다.

 

열거형 타입이 없는 JavaScript와 같은 언어에서 이러한 값은 내부적으로 정수 집합에 매핑될 수 있다. 하지만 이러한 세부 정보는 클라이언트에 노출되지 않고, 열거형 값의 문자열로만 작동한다.

리스트와 Non-Null 

객체 타입, 스칼라 타입, 열거형 타입은 GraphQL 에서 정의할 수 있는 유일한 타입이다. 하지만 스키마의 다른 부분이나 쿼리 변수 선언에서 타입을 사용하면 해당 값의 유효성 검사를 할 수 있는 타입 수정자(type modifiers) 를 적용할 수 있다.

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

String 타입을 사용하고 타입 뒤에 느낌표 ! 를 추가하여 Non-Null 로 표시했습니다. 즉, 서버는 항상 이 필드에 대해 null이 아닌 값을 반환할 것을 기대하며, null값이 발생되면 GraphQL 실행 오류가 발생하고, 클라이언트에게 무언가 잘못됨을 알린다.

 

예시 1)

myField: [String!]
myField: null // valid
myField: [] // valid
myField: ['a', 'b'] // valid
myField: ['a', null, 'b'] // error

예시 2)

myField: [String]!
myField: null // error
myField: [] // valid
myField: ['a', 'b'] // valid
myField: ['a', null, 'b'] // valid

 

인터페이스

여러 타입 시스템과 마찬가지로 GraphQL도 인터페이스를 지원한다. 인터페이스는 구현하기 위해 타입이 포함해야하는 특정 필드들을 포함하는 추상 타입이다.

 

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
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
}

특정 객체 타입의 필드를 요청하려면 인라인 프래그먼트을 사용해야한다.

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

유니온 타입

유니온 타입은 인터페이스와 매우 유사하지만, 타입 간에 공통 필드를 특정하지 않는다.

union SearchResult = Human | Droid | Starship

스키마에서 SearchResult 타입을 반환 할 때마다, Human, Droid, Starship 을 얻을 수 있다. 

 

입력 타입

지금까지는 열거형 타입이나 문자열과 같은 스칼라 값을 인자로 필드에 전달하는 방법에 대해서만 설명했다. 뮤테이션은 생성될 전체 객체를 전달하고자 할 수 있다. GraphQL 스키마 언어에서 입력 타입은 일반 객체 타입과 정확히 같지만, type 대신 input 을 사용한다.

input ReviewInput {
  stars: Int!
  commentary: String
}
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}
{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}
728x90

'Tech > GraphQL' 카테고리의 다른 글

GraphQL Schema  (0) 2021.07.01
GraphQL best practice  (0) 2021.06.24
GraphQL 쿼리, 뮤테이션  (0) 2021.06.24
GraphQL 간단 소개  (0) 2021.06.24

댓글