- 오브젝트 타입 : Character
- 필드 : name, appearsIn
- 스칼라 타입 : String, ID, Int 등
- 느낌표(!) : 필수 값을 의미(non-nullable)
- 대괄호([, ]) : 배열을 의미(array)
gql 쿼리에서는 각각의 필드마다 함수가 하나씩 존재 한다고 생각하면 됩니다. 이 함수는 다음 타입을 반환합니다. 이러한 각각의 함수를 리졸버(resolver)라고 합니다. 만약 필드가 스칼라 값(문자열이나 숫자와 같은 primitive 타입)인 경우에는 실행이 종료됩니다. 즉 더 이상의 연쇄적인 리졸버 호출이 일어나지 않습니다. 하지만 필드의 타입이 스칼라 타입이 아닌 우리가 정의한 타입이라면 해당 타입의 리졸버를 호출되게 됩니다.
이러한 연쇄적 리졸버 호출은 DFS(Depth First Search)로 구현 되어있을것으로 추측합니다. 이점이 바로 gql이 Graph라는 단어를 쓴 이유가 아닐까 생각합니다. 연쇄 리졸버 호출은 여러모로 장점이 있습니다. 연쇄 리졸버 특성을 잘 활용하면 DBMS의 관계에 대한 쿼리를 매우 쉽고, 효율적으로 처리 할 수 있습니다. 예를들어 gql의 query에서 어떤 타입의 필드 중 하나가 해당 타입과 1:n의 관계를 맺고 있다고 가정해보겠습니다.
type Query {
users: [User]
user(id: ID): User
limits: [Limit]
limit(UserId: ID): Limit
paymentsByUser(userId: ID): [Payment]
}
type User {
id: ID!
name: String!
sex: SEX!
birthDay: String!
phoneNumber: String!
}
type Limit {
id: ID!
UserId: ID
max: Int!
amount: Int
user: User
}
type Payment {
id: ID!
limit: Limit!
user: User!
pg: PaymentGateway!
productName: String!
amount: Int!
ref: String
createdAt: String!
updatedAt: String!
}
여기에서는 User와 Limit는 1:1의 관계이고 User와 Payment는 1:n의 관계입니다.
{
paymentsByUser(userId: 10) {
id
amount
}
}
{
paymentsByUser(userId: 10) {
id
amount
user {
name
phoneNumber
}
}
}
위 두 쿼리는 동일한 쿼리명을 가지고 있지만, 호출 되는 리졸버 함수의 갯수는 아래가 더 많습니다. 각각의 리졸버 함수에는 내부적으로 데이터베이스 쿼리가 존재합니다. 이 말인즉, 쿼리에 맞게 필요한 만큼만 최적화하여 호출 할 수 있다는 의미입니다. 내부적으로 로직 설계를 어떻게 하느냐에 따라서 달라 질 수 있겠지만, 이러한 재귀형의 리졸버 체인을 잘 활용 한다면, 효율적인 설계가 가능 합니다. (기존에 REST API 시대에는 정해진 쿼리는 무조건 전부 호출이 되었습니다.)
리졸버 함수는 다음과 같이 총 4개의 인자를 받습니다.
Query: {
paymentsByUser: async (parent, { userId }, context, info) => {
const limit = await Limit.findOne({ where: { UserId: userId } })
const payments = await Payment.findAll({ where: { LimitId: limit.id } })
return payments
},
},
Payment: {
limit: async (payment, args, context, info) => {
return await Limit.findOne({ where: { id: payment.LimitId } })
}
}
- 첫번째 인자는 parent로 연쇄적 리졸버 호출에서 부모 리졸버가 리턴한 객체입니다. 이 객체를 활용해서 현재 리졸버가 내보낼 값을 조절 할 수 있습니다.
- 두번째 인자는 args로 쿼리에서 입력으로 넣은 인자입니다.
- 세번째 인자는 context로 모든 리졸버에게 전달이 됩니다. 주로 미들웨어를 통해 입력된 값들이 들어 있습니다. 로그인 정보 혹은 권한과 같이 주요 컨텍스트 관련 정보를 가지고 있습니다.
- 네번째 인자는 info로 스키마 정보와 더불어 현재 쿼리의 특정 필드 정보를 가지고 있습니다. 잘 사용하지 않는 필드입니다.
인트로스펙션(introspection)
기존 서버-클라이언트 협업 방식에서는 연동규격서라고 하는 API 명세서를 주고 받는 절차가 반드시 필요 했습니다. 프로젝트 관리 측면에서 관리해야 할 대상의 증가는 작업의 복잡성 및 효율성 저해를 의미합니다. 이 API 명세서는 때때로 관리가 제대로 되지 않아, 인터페이스 변경 사항을 제때 문서에 반영하지 못하기도 하고, 제 타이밍에 전달 못하곤 합니다.
이러한 REST의 API 명세서 공유와 같은 문제를 해결하는 것이 gql의 인트로스펙션 기능 입니다. gql의 인트로스펙션은 서버 자체에서 현재 서버에 정의된 스키마의 실시간 정보를 공유를 할 수 있게 합니다. 이 스키마 정보만 알고 있으면 클라이언트 사이드에서는 따로 연동규격서를 요청 할 필요가 없게 됩니다. 클라이언트 사이드에서는 실시간으로 현재 서버에서 정의하고 있는 스키마를 의심 할 필요 없이 받아들이고, 그에 맞게 쿼리문을 작성하면 됩니다.
이러한 인트로스펙션용 쿼리가 따로 존재합니다. 일반 gql 쿼리문을 작성하듯이 작성하면 됩니다. 다만 실제로는 굳이 스키마 인트로스펙션을 위해 gql 쿼리문을 작성할 필요가 없습니다. 대부분의 서버용 gql 라이브러리에는 쿼리용 IDE를 제공합니다. 다음 화면은 apollo server라는 서버용 gql 라이브러리에 포함 되어있는 웹 IDE 화면입니다.

위의 화면을 참고하면, 프로그래머는 인트로스펙션을 활용하여, 직접 쿼리 및 뮤테이션, 필드 스키마를 확인 할 수 있습니다. 물론 보안상의 이슈로 상용환경에서는 이러한 스키마의 공개는 신중해야 합니다. 대부분의 라이브러리는 해당기능을 켜고 끄게 하는 옵션이 존재합니다.
GraphQL을 활용 할 수 있게 도와주는 다양한 라이브러리들
gql 자체는 쿼리 언어입니다. 이것 만으로는 할 수 있는 것이 없습니다. gql을 실제 구체적으로 활용 할 수 있도록 도와주는 라이브러리들이 몇가지 존재 합니다. gql 자체는 개발 언어와 사용 네트워크에 완전히 독립적입니다. 이를 어떻게 활용 할지는 여러분에게 달려 있습니다.
대표적인 gql 라이브러리 셋에 대한 링크는 2개를 소개합니다. 릴레이는 GraphQL의 어머니인 Facebook이 만들었습니다. 하지만 개인적인 의견으로는 현재(2019년 7월)버전의 릴레이는 사용하기 매우 번거롭게 디자인 되어 있다고 생각합니다. 개인적으로는 아폴로가 사용하기 편했습니다.
실제 GraphQL로 비지니스 로직 작성하기

앞서서 gql에 대한 개념을 익혔고, 비지니스 로직 작성을 간단히 보여드리겠습니다. 구현시, 비지니스 로직은 실제 리졸버 함수에 담지 않습니다. 로직은 비지니스 로직 레이어 (다른 파일의 다른 함수)에 작성을 하는 것을 권장합니다. 위 그림에도 나왔지만 이는 REST API를 제작할때 사용하는 패턴과 동일한 패턴입니다.
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)
}
실제 구현한 비지니스 로직 관련 리졸버
정리
gql은 퍼포먼스적인 장점이 분명 존재합니다. 하지만 개인적으로 더 관심이 가는 장점은 바로 생산성 향상입니다. gql은 기존 백앤드-프론트앤드 협업 문화를 많이 바꿀것으로 예상합니다. gql의 협업 구조상 프론트앤드쪽에 조금 더 할일이 많아지고 힘이 실리는 느낌입니다. 에자일하게 웹사이트 프로젝트를 진행하는데 gql이 많은 도움이 될 것이라고 생각합니다.
- GraphQL는 API를 만들 때 사용할 수 있는 쿼리언어입니다. 그와 동시에 쿼리에 대한 데이터를 받을 수 있는 런터임이기도 합니다.
- GraphQL은 SQL과 같은 쿼리언어지만 구조적으로 다르다.
- sql의 문장(statement)은 주로 백앤드 시스템에서 작성하고 호출 하는 반면, gql의 문장은 주로 클라이언트 시스템에서 작성하고 호출 - GraphQL vs REST API
- GraphQL은 하나의 엔드포인트를 가지며 POST요청으로 이루어져 있는 반면, REST API는 여러 개의 메소드를 가지고 있으며 여러 엔드포인트를 가진다.
- REST API는 엔드포인트에 따라 데이터베이스의 쿼리가 바뀌며, gql은 스키마에 따라 쿼리가 바뀐다.
- GraphQL의 구조는 크게 쿼리와 뮤테이션으로 이루어져 있으며 쿼리는 Read, 뮤테이션은 CUD를 담당한다. 각각의 구조는 비슷하며 역할만 다르다.
- GraphQL-Resolver
- GraphQL(gql)에서는 데이터를 가져오는 구체적인 과정을 직접 구현 해야 한다. gql 쿼리문 파싱은 대부분의 gql라이브러리에서 처리해주고, gql에서 데이터를 가져오는 구체적인 과정은 resolver 담당한다. - resolver를 통해 데이터를 데이터 베이스에서 가져올 수 있고, 일반 파일에서도 가져 올 수있다. 심지어 http,soap 와 같은 네트워크 프로토콜을 활용해서 원격 데이터 또한 가져올 수 있다.
- resolver 특성을 통해 ****legacy 시스템을 gql 기반으로 바꾸는데에도 활용이 가능하다.
- GraphQL의 인트로스펙션(introspection)을 통해 doc를 실시간 정보 공유가 가능하며 연동규격서를 따로 작성할 필요가 없어진다.
'IT Tech & Terms' 카테고리의 다른 글
팝업(Popup)과 모달(Modal) (4) | 2024.01.26 |
---|---|
프레임워크 vs 라이브러리 (8) | 2024.01.22 |
Platform - Vendure Architecture (3) | 2023.12.27 |
Git command 정리 (1) | 2023.12.26 |
.hbs, CSR, SSR, SSG, Jam stack (0) | 2023.12.26 |