본문 바로가기

개발바닥/GraphQL

[개발] GraphQL - Schema & Type

안녕하세요. devport 입니다. 이번 포스팅에서는 GraphQL의 스키마가 어떻게 구성되고 있는지 알아보도록 하겠습니다.

스키마?

GraphQL은 강력한 형식의 스키마를 제공하고 있습니다. 스키마는 클라이언트와 서버간 알아 볼 수 있는 데이터의 계층 구조를 정의하고 있으며 이를 실행할 수 있는 Query 및 Mutation을 정의 할 수 있습니다.

스키마의 장점

  • 스키마는 클라이언트와 서버간의 데이터 및 전송방식을 정의합니다. - 명확하게 정의된 스키마를 사용하면 클라이언트와 서버간의 상호작용에 대한 이해가 더 쉬워집니다. API를 사용하는 동안 클라이언트 / 서버간의 상호 작용에서 발생할 수 있는 잘못된 부분들을 사전에 알 수 있게됩니다.
  • 에러를 사전에 확인 가능합니다. - 정의된 유형에 위배되는 형식으로 개발이 진행된다면 어느 부분에서 문제가 되는지 GraphQL에서 에러가 발생하기 때문에 개발에 실수를 줄여줄수 있습니다. 이는 스키마의 타입 특성으로 인해 API에 전달 된 잘못된 데이터가 넘어올 경우에 오류로 표시되기 때문입니다. 이를 통해 개발을 진행하는 동안 많은 시간과 노력을 절약할 수 있습니다.
  • 클라이언트와 서버 팀간의 독립적으로 작업을 할 수 있습니다. - 스키마를 명확하게 설계를 한다면 BackEnd 팀은 서버에서 스키마에 대한 구현, Frontend 팀에서는 스키마의 명세를 확인하여 필요 데이터를 요청하여 구현을 하는 작업으로 분업화 할 수 있습니다. 

GraphQL 스칼라 타입

GraphQL은 스칼라 타입의 데이터 유형을 가지고 있습니다. GraphQL에서 기본적으로 제공하는 데이터 유형은 다음과 같습니다.

  • Int - 부호있는 32비트 정수
  • Float - 부호있는 부동 소수점
  • String - UTF-8 문자열
  • bool - true, false
  • ID - 고유 식별자

위처럼 고유한 특성을 가진 타입들 있습니다. 위 타입들로 구성된 GraphQL 객체를 작성해 보겠습니다.

 

type Book {
  id: ID
  title: String
  author: String
}

type Author {
  name: String
  books: [Book]
}

 

위의 예에서는 Book, Autor타입의 개체를 생성하였습니다. Book 객체는 ID, 책의 제목, 책의 저자로 정의되어 있습니다. Autor 객체는 저자의 이름, 책의 목록 정보로 정의되어 있습니다.

 

GraphQL 스키마를 작성할 때 가장 먼저해야 할 일은 type을 정의하는 것입니다. GraphQL은 타입에 크게 의존하기 때문에 스키마를 생성하기 전 처음에 정의하게 됩니다.

이를 더 이해 하기 위하여 GraphQL에서 제공하고 있는 유형들을 살펴 보겠습니다.

GraphQL의 enum 유형

GraphQL은 enum 유형을 지원하며 다른 프로그래밍 언어에서 작동하는 방식과 동일하게 동작합니다. 열거형을 사용하면 모든 인수가 정의 된 값 중 하나를 가질 수 있는지 확인할 수 있습니다.

 

다음은 enum 타입의 예입니다.

enum BookColor {
  RED,
  GREEN,
  BLUE
}

위 예에서는 BookColor유형의 enum형을 정의하였습니다. BookColor의 유형은 이 3개의 값 중 하나를 가질 수 있습니다.

GraphqQL의 Array 유형

GraphQL 스키마는 목록을 추가 함으로써 여러개의 항목을 가질 수 있습니다. 예를 들어 정수 목록을 원하면 목록 유형임을 나타내는 [Int]로 선언할 수 있습니다.

 

원래 Author유형에 목록 유형을 추가해보겠습니다.

 

type Author {
  name: String
  books: [Book]
}

 

저자가 만든 책의 항목을 추가했으며 문자열 목록입니다. 즉, 여러 책의 목록으로 보유할 수 있습니다.

 

Query 및 Mutation 유형

GraphQL을 type으로 정의 하였다면 정의한 부분에 대한 데이터를 Query(조회), Mutation(추가,수정,삭제)을 할 수 있게 됩니다. 

 

GraphQL 스키마에서 Query 및 Mutation 유형을 정의하는 방법은 다음과 같습니다.

 

schema {
  query: Query
  mutation: Mutation
}

 

GraphQL에서 Query 및 Mutation 유형은 다른 데이터 유형과 마찬가지로 처리됩니다.

 

이를 더 잘이해하기 위해 몇가지 예를 살펴 보겠습니다. Query 및 Mutation 유형을 포함하도록 Book유형의 이전 예제를 확장하겠습니다.

 

type Query {
  book_info: [Book]
}

 

위의 예에서는 book_info라는 쿼리를 정의하였습니다. 이를 통해 클라이언트가 book_info 데이터를 검색을 하게되면  Book 유형의 데이터들을 배열에 담아져 반환하게 됩니다.

 

마찬가지로 Mutation 유형도 정의 할 수 있습니다.

 

type Mutation {
  add_book: (title: String, author: String) : Book
}

 

여기서 add_book Mutation을 사용하여 Book에 새항목을 추가할 수 있습니다.

 

마찬가지로 GraphQL API 에 대해 여러 Query 및 Mutation을 정의 할 수 있습니다.

Non-null 필드

GraphQL에서는 유형을 정의하고 null 허용 여부를 결정할 수 있습니다.

아래에서 ID 옆에 느낌표(!)를 추가하게 된다면 id에 널값이 포함될 수 없게 됩니다.

 

type Book {
  id: ID!
  title: String
  author: String
}

 

기본적으로 각 스칼라 유형은 null을 포함할 수 있도록 설정되어 있습니다.. 느낌표를 추가하게 된다면 해당 필드에 null값이 될 수 없다는 규칙을 설정할 수 있게됩니다. 이 기능은 클라이언트와 서버간의 요청을 명확하게 정의하기 때문에 API에서 필수 값을 포함하고 있는지 사전에 알 수 있게 됩니다.

목록과 널이 아닌 조합

좀 더 복잡한 스키마를 정의할 경우 다양한 방법으로 목록과 널이 아닌 항목을 조합할 수 있습니다. [myList] 라는 유형이 있다고 가정해보겠습니다. null이 아닌 목록 목록의 조합은 다음과 같습니다.

 

  • [myList!]는 목록 자체는 null일 수 있지만 목록안에는 null이 있을 수 없음을 의미합니다.
  • [myList]!는 목록 자체가 null일 수 없지만 목록안에는 null이 있을 수 있음을 의미합니다.
  • [myList!]!는 목록 자체가 null일 수 없지만 목록안도 null이 있을 수 없음을 의미합니다.
type author {
  name: String
  book: [String!]
}

input Type

input Type은 Query 및 Mutation에 대한 인자로 객체를 전달할 수 있는데 이에 대한 객체 유형입니다. 

예를 들어서 블로그 게시물을 만드는 Mutation을 아래와 같이 선언한다면

 

type Mutation {
  createPost(title: String, body: String, mediaUrls: [String]): Post
}

 

위처럼 세개의 인수를 전달받는 방식도 있지만 한개의 단일 입력 객체 인자를 전달 받아 인자를 전달할 수 도있습니다.

 

type Mutation {
  createPost(post: PostAndMediaInput): Post
}

input PostAndMediaInput {
  title: String
  body: String
  mediaUrls: [String]
}

 

전달인자는 type 형식을 사용할 수 없으며 input 키워드로 유형을 선언하여야 합니다. 

위 예 처럼 PostAndMediaInput이라는 input 유형을 선언하여 전달인자로 사용하게 된다면 javascript에 경우 객체의 반환인자를 Mutation 구현부에서 전달 받을 수 있게 됩니다.

주석

type, input 유형 내부에서 주석을 달게 된다면 ""을 사용하여 주석문을 추가할 수 있습니다.

 

input PostAndMediaInput {
  "A main title for the post"
  title: String
  "The text body of the post."
  body: String
  "A list of URLs to render in the post."
  mediaUrls: [String]
}