저번 포스팅에 이어, GraphQL
에 대해 알아보고자 한다. 실제 구성요소
와 구현
방법등에 대하여 서술할 예정이다.
GraphQL는 크게 다음과 같은 구성 요소로 이루어져 있다.
GraphQL에서는 요청을 보내는 방법을 2가지로 정의하는데, 쿼리
와 뮤테이션
이다. 이 둘은 다른것 같지만, 실상은 별 차이가 없다.
쿼리
는 조회(R)
에 사용되고, 뮤테이션
은 데이터의 변조(CUD)
에 사용되는 개념적인 규약이다.
// 쿼리를 통한 데이터 조회
// 단순한 조회 작업
{
human(id: "1000") {
name
height
}
}
// 인자를 통한 조회
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
// mutation을 통한 데이터 변조
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
카카오 기술블로그에는 다음과 같은 설명이 첨부되어 있다.
데이터베이스 스키마(Schema)를 작성할 때의 경험을 SQL 쿼리 작성으로 비유한다면, gql 스키마를 작성할 때의 경험은 C, C++의
헤더파일
작성에 비유가 됩니다.
그러므로, 프로그래밍언어(C, C++, JAVA등)에 익숙한 프로그래머라면 스키마 정의 또한 쉽게 배우실 것입니다.
즉, 스키마
라는 것은 GraphQL에서 사용될 객체 타입을 사전에 지정하는 작업으로써 다음과 같은 형식을 지닌다.
type Character {
name: String!
appearsIn: [Episode]!
}
데이터베이스에는 데이터베이스 어플리케이션을 사용하여 데이터를 가져오는 구체적인 과정
이 구현되어 있다.
그러나 GraphQL에서는 데이터를 가져오는 구제적인 과정을 직접 구현
해야 하며 이는 리졸버(Resolver)
가 담당하게 된다.
이를 통해 데이터베이스뿐 아니라, 일반 파일 및 http SOAP 같은 네트워크 프로토콜을 활용하여 원격 데이터를 가져올 수 있다.
GraphQL에서는 각 필드마다 하나의 함수
가 존재하게 되고, 이 함수는 다음 타입을 반환하게 되며 이 각 함수를 리졸버
라 부른다.
필드가 스칼라 값(String, Int 같은 primtive 타입)인 경우에는 연쇄 호출이 중지되고, 종료된다.
type Query {
users: [User]
user(id: ID): User
limits: [Limit]
limit(UserId: ID): Limit
paymentsByUser(userId: ID): [Payment]
}
type User {
...
}
type Limit {
...
user: User
...
}
type Payment {
...
user: User!
...
}
위와 같은 코드에서 User와 Limit는 1:1, User와 Payment는 1:N의 관계이다.
이때 다음과 같은 동일한 쿼리명을 가진 2가지 쿼리를 살펴보자.
// 쿼리1. 필드값 2개
{
paymentsByUser(userId: 10) {
id
amount
}
}
// 쿼리2. 필드값 3개
{
paymentsByUser(userId: 10) {
id
amount
user {
name
phoneNumber
}
}
}
이 때 필드 1개당 리졸버 함수 1개가 불리게 되므로, 밑의 쿼리가 더 많은
함수를 호출하게 된다.
또한 각각의 리졸버 함수는 내부적으로 데이터베이스 쿼리가 존재하게 되는데, 이 두가지를 조합하면 다음과 같은 사실을 알 수 있다.
필요한만큼만
최적화하여 호출 가능즉 기존의 RESTful API는 정해진 쿼리가 무조건 호출됨에 비해, 리졸버 체인을 잘 활용하여 효율적인 설계
를 할 수 있다는 뜻이다.
GraphQL을 이용하여 비지니스 로직을 작성할 때, 실제 로직은 리졸버 함수에 담지 않는다
.
유효성 검사 및 권한 확인과 실제 로직은 전부 전용 비지니스 로직 레이어
내부에 담게 된다.
requestPaymentSession: async (parent, {
pgId, name, sex, birthDay, phoneNumber, amount, productName, ref
}, context, info) => {
// 실제 로직은 비지니스 레이어로
const ret = await requestPaymentSession({ pgId, name, birthDay, phoneNumber, sex, amount, productName, ref })
return removeSymbol(ret)
},
requestPaymentApprove: async (parent, {
sessionKey, authNumber
}, context, info) => {
// 실제 로직은 비지니스 레이어로
const ret = await requestApprovePayment({ sessionKey, authNumber })
return removeSymbol(ret)
}
이로써 기본적인 GraphQL의 구성
및 구현
방법에 대하여 알아보았다.
물론 실제로 코드에 위의 개념을 녹이는 것은 완전 다른 이야기지만..