스키마 & 타입
타입 시스템
GraphQL 쿼리 언어는 기본적으로 객체의 필드를 선택한다.
{
hero {
name
appearsIn
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
}
}
- root 객체로 시작한다.
- hero 필드를 선택한다.
- 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 타입이 있어야한다.
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 라고도 하는 열거형 타입은 특정 값들로 제한되는 특별한 종류의 스칼라이다. 이를 통해 다음과 같은 작업을 할 수 있다.
- 타입의 인자가 허용된 값 중 하나임을 검증한다.
- 필드가 항상 값의 열거형 집합 중 하나가 될 것임을 타입 시스템을 통해 의사소통한다.
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!"
}
}
}
'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 |
댓글