GraphQL의 이해
네이버 인턴쉽을 진행하면서 GraphQL
을 상당히 많이 사용했다. React 기반의 view에 필요한 데이터는 GraphQL
+ Apollo
조합으로 모두 호출했다. 하지만 활용에 비해 근본적인 이해를 하고 있는지 의심이 갔고, 이번 기회에 정리해볼까 한다.
GraphQL 이란
graphql
은 한 마디로 웹 클라이언트가 데이터를 서버로 부터 효율적으로 가져오기 위해 만들어진 쿼리언어 입니다.
따라서 클라이언트에서 gql
로 작성된 쿼리를 서버사이드로 넘기면, 서버는 쿼리를 입력으로 받고 처리한 후, 결과를 다시 클라이언트에게 넘기게 됩니다.
Rest API와의 비교
사실 양쪽 모두 존재 목적은 비슷합니다. 클라이언트에서 데이터를 요청하면, 서버에서 응답하고, 데이터를 넘겨주는 형식입니다.
하지만 명확한 차이가 있습니다.
- 엔드포인트:
Rest API
는 url과 method의 조합으로 다양한 엔드포인트가 존재합니다. 반면gql
은 스키마의 타입마다 쿼리가 달라지기 때문에 하나의 엔드포인트를 가집니다.
GraphQL의 쿼리
여기서 하나의 엔드포인트의 의미에 대해 알아봅시다.
클라이언트에서 필요한 데이터를 gql
이란 언어로 쿼리문을 작성해 서버사이드에 보내는 것까지는 알게되었습니다. 그럼 어떤 방식으로 쿼리문을 작성할까요? 예시를 보겠습니다.
query getStudentInfomation($studentId: ID){
personalInfo(studentId: $studentId) {
name
address1
address2
major
}
classInfo(year: 2018, studentId: $studentId) {
classCode
className
teacher {
name
major
}
classRoom {
id
maintainer {
name
}
}
}
SATInfo(schoolCode: 0412, studentId: $studentId) {
totalScore
dueDate
}
}
Rest API
와 같이 studentId
라는 클라이언트 사이드에서 만든 input
데이터를 넘겨 그에 맞는 데이터를 요청하고 있습니다. 하지만 특이한 점은 요청하는 데이터의 종류가 여러가지 입니다. personalInfo
,classInfo
,SATInfo
이 세가지 데이터 정보를 한 번에 요청하고 있습니다. Rest API
같으면 /personalInfo?studentId=123
,/classInfo?studentId=123
, ... 이렇게 3번에 걸쳐 요청해야겠지만, gql
은 하나의 엔드포인드에 필요한 여러 종류의 데이터를 요청해 효율적인 호출이 가능합니다.
이렇게 여러 쿼리를 함수를 이용해 한 번에 처리하는 방식을 오퍼레이션 네임 쿼리
라고 합니다.
이런 변화는 프론트엔드 개발자로 하여금, 백엔드의 전통적인 API 요청/응답 방법에 대한 의존성을 덜어내주고, 프론트엔드에 필요한 데이터 요청/응답 방식을 프론트엔드 개발자가 직접 커스터마이징하여 효율적인 자원으로 일을 진행하게 될 수 있습니다.
받은 쿼리문 처리하기
클라이언트에서 서버에서 정한 스키마에 맞추어 자유롭게 쿼리문을 작성하고, 성공적으로 서버에 도착했습니다. 이때, 서버에서는 이 쿼리문을 어떻게 처리할까요?
node.js와 mongoDB 환경기준
- 우선 받은 쿼리문을 파싱해야 합니다. 하지만 다행히
gql
라이브러리가 파싱을 도와줍니다. - 이 후, 각각의 쿼리문이 주어진
input
을 가지고, 스키마에서 보여준 데이터를 리턴하도록 함수를 서버에서 작성해야 합니다. 이때 이 함수를리졸버
라고 부릅니다.참고로 리졸버의 리턴값을 다른 리졸버의 출력 데이터 중 하나로 활용될 수 있습니다. 따라서 같은 종류의 데이터를 호출할 때는 이 방식으로 최적화가 가능합니다.
// user(id: ID)가 paymentsByUser(userId: ID) 안에서 호출될 수 있음
type Query {
users: [User]
user(id: ID): User
limits: [Limit]
limit(UserId: ID): Limit
paymentsByUser(userId: ID): [Payment]
}
type Payment {
id: ID!
limit: Limit!
user: User!
pg: PaymentGateway!
productName: String!
amount: Int!
ref: String
createdAt: String!
updatedAt: String!
}
리졸버
는 DB와 소통하며 스키마의 출력 데이터 타입에 맞추어, 요청받은 데이터를 DB로 부터 가져오거나, 혹은 수정(생성, 업데이트, 삭제)을 하는 역할을 수행해야 합니다.
예시를 보겠습니다.
export const getFood = async (_, { getFoodInput: { foodID: _id } }) => {
try {
const foodList = await Trip.find({ _id })
if (!(foodList?.length > 0)) return null
return foodList
} catch (err) {
throw new Error(err)
}
}
클라이언트로부터 받은 foodID
로 mongoDB
속 foodList
를 찾고 데이터를 리턴합니다. 이 때, mongoose
를 활용하므로 비동기 함수가 됩니다.
이렇게 직접적으로 DB에 요청할 수 있지만, http를 이용해 데이터를 fetch하거나 혹은 다양한 방법으로 데이터를 가져올 수 있습니다. 이러한 리졸버의 확장성은 legacy 코드의 마이그레이션을 도울 수 있습니다.
서버-클라이언트 협업
클라이언트에서는 Rest API
와 달리 API 명세서 없이, 스키마 만 있으면 원하는 데이터에 대한 쿼리문 작성이 가능합니다.
이때 이 스마카를 보기 좋게 정리해둔 쿼리용 IDE가 있습니다.
playground (apollo server 라이브러리)
클라이언트에서 GraphQL 사용법 (React 환경)
클라이언트에서 gql
을 사용한다는 의미는 다음과 같습니다.
- 쿼리문 작성
- 쿼리문 서버로 전송, 응답받기
- 변경된 데이터 React 컴포넌트 내에서 상태관리
쿼리문 작성
playground
등을 통해 스키마를 보고, 필요한 데이터를 조합해 쿼리문을 작성합니다.
쿼리문 서버로 전송, 응답받기
Rest API
와 마찬가지로 서버와는 http
로 통신합니다. Fetch
함수를 통해 쿼리문을 전달할 수도 있지만, 좀 더 직관적이고 추상화가 잘 된 라이브러리가 있습니다.
Apollo Client 입니다.
gql
을 사용해 쿼리한 데이터 상태관리 라이브러리 입니다.
Apollo client
의 hook을 이용한 함수 하나로 클라이언트에서 서버로 데이터 쿼리를 요청할 수 있습니다.
const { loading, error, data } = useQuery(GET_DOGS);
이때, 쿼리한 데이터는 반드시
Apollo Cache
라는 storage에 들어가게 되는데, 다음 쿼리 발생 시Apollo Cache
를 먼저 탐색함으로써 불필요한 서버의 호출을 방지합니다.
변경된 데이터 React 컴포넌트 내에서 상태관리
Apollo client
의 hook은 React hook
이기 때문에 쿼리를 할 때, 값이 바뀌면 리렌더링을 통해 즉각적인 UI 변화가 일어납니다.
reference
Author And Source
이 문제에 관하여(GraphQL의 이해), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@cramming/GraphQL의-이해저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)