
※ GraphQL을 들어가기 전에
GraphQl이 무엇인지와 이의 필요성에 대해 공부해 보고, React와 Apollo Client를 함께 사용 하는 방법을 Hook기반의 케이스로 공부하고 실습해봤던 내용을 소개하려고 한다. 먼저 GraphQl이 무엇인지 알아보기 전에 REST API 을 한번 짚어보자.
REST API는 GET(Read), POST(Create), PUT(Update), DELETE(Delete)등 HTTP method들을 보고서 url의 요청을 알아보기 쉽게 해주는 것이다. 이렇게 잘 디자인 된 REST는 이해하기 쉬워 많은 기업들, 서버, 많은 사람들이 REST API를 사용하고 있다.
그럼에도 불구하고 왜 GraphQL을 사용하는 것인가??
GraphQL이란 무엇인가?
GraphQL이 기존 REST API의 한계점을 극복하기 위해 Facebook에서 개발한 쿼리 언어이다. 그렇다면 REST API의 어떤 부분이 불편해서 다른 방법을 찾으려고 했는지를 먼저 알아보고 그 문제점을 알게 되면, graphQL의 존재 의의와 왜, 언제 사용해야 하는지에 대한 것도 자연스럽게 알 수 있을 것이다.
1. Over-Fething 이슈 해결
REST API를 이용하면 백엔드(BE)가 URL의 각 리소스에 사용할 수있는 데이터를 정의하는 반면, 프론트엔드(FE)는 리소스의 일부만 필요하더라도 항상 리소스의 모든 정보를 요청해야 한다. 하지만 GraphQL을 사용하면 필요한 필드만 명시적으로 요청할 수 있기 때문에 해당 문제를 근본적으로 해결한다고 할 수 있다.
위 내용을 간단한 예시로 설명하자면.
REST API
- 학생에 대한 정보를 알려줘
GraphQL
- 학생의 정보 중에 키와 몸무게만 알려줘
공식문서를 통해 GraphQL의 장점을 확인해 볼 수 있다.

//GraphQL query
{
student {
height
weight
}
}
//JSON result
{
"student":{
"height": 1.72,
"weight": 63
}
}
2. Under-Fetching 이슈 해결
일반적으로 REST API에서는 특정 데이터를 사용하려면 여러 엔드포인트에 액세스하여 데이터를 받아와야 한다.
예를 들어, 위에서 말했던 학생 정보를 가져온다면,
- 첫번째 - /student/<id> 엔드 포인트
- 두번째 - 학생의 정보를 가져오기 위해서 /student/<id>/info 엔드 포인트
- 세번째 - 학생의 친구 목록을 가져오기 위한 /student/<id>/friends 엔드 포인트
위에서와는 다르게 필요한 모든 데이터를 한 번의 요청으로 가져올 수 없고, 여러 엔드포인트에 대한 여러 번의 요청이 필요한 상황이 발생하기 때문에 네트워크 지연과 요청 처리 시간 증가를 초래할 수 있다.
※그렇다면 GraphQL을 사용한다면 어떻게 받아오는 걸까?
- GraphQL은 단일 쿼리에 구체적인 데이터 요구 사항을 적어 GraphQL 서버에 보내기만 하면 된다.
query {
student(id: "20180000"){
name
height
weight
friends{
name
}
}
- 그러면 서버에서는 요구에 맞게 데이터를 Json 객체로 보내준다.
Apollo Client 란?
Apollo Client는 Javascript/Typescript에서 클라이언트 측 상태 관리 및 GraphQL 작업 처리를 위한 라이브러리다. GraphQL 쿼리를 작성하기만하면 Apollo Client가 데이터를 요청하고 캐싱하고 UI까지 업데이트해준다. Apollo Client는 React, Angular, VUE, Ember 등 다양한 플랫폼을 지원한다..
※ 아폴로 클라이언트를 알아보았으니 React에서 Apollo Client를 이용해서 GraphQL을 이용해 서버와 연동해 보자
Apollo Client 설치
// npm
npm install @apollo/client graphql
// yarn
yarn add @apollo/client graphql
Apollo Client 폴더 생성
apollo client 설정을 위해 src/api/apollo 폴더 생성하고 client.ts를 생성한다.
src/api/apollo/client.ts
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
// Apollo Client 인스턴스를 GraphQL API와 연결하는 http Link생성한다.
const httpLink = createHttpLink({
uri: '서버 주소',
});
// httpLink 및 InMemoryCache의 새 인스턴스를 전달하여 ApolloClient를 인스턴스화한다.
export const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
연동하고 싶은 서버의 EndPoint를 uri에 넣고 cache에 InMemoryCache를 사용하여 ApolloClient를 인스턴스화한다.
그러면 gql의 결과를 InMemortCache에 저장하고 불필요한 네트워크 요청을 하지 않는다고 한다.
gql query와 mutation을 만들어 주기위해서 apollo폴더 아래에 query폴더와 mutation 폴더를 생성해주고 각각 이름에 맞게 Query.gql.ts와 Mutatuion.gql.ts를 생성해준다.
src/api/apollo/mutation/AuthMutation.gql.ts
import { gql } from '@apollo/client';
// 로그인 Mutation 쿼리를 생성해준다.
export const LOGIN_REQUEST = gql`
mutation LOGIN($request: LoginRequest!) {
login(request: $request)
}
`;
src/api/apollo/query/ItemQuery.gql.ts
import { gql } from '@apollo/client';
// 카테고리 아이템을 가져오는 쿼리를 생성해 준다.
export const ALL_CATEGORY_TREE = gql`
query {
findAllItemCategoriesTree {
publicId
title
}
}
`;
위에 ItemQuery.gql.ts와 AuthMutation.gql.ts에서는 서버에서 작업한 로그인과 카테고리 조회 2가지 스키마에 gql를 작성했다. 다른 GraphQL 서버로 테스트할 경우 서버에서 정의한 스키마에 맞춰 gql를 작성하면 된다.
그리고 Rest Api를 Swagger를 통해 스키마를 확인하고 테스트 하는 것과 마찬가지로 GraphQL도 GrapiQL에서 스키마를 확인하고 테스트가 가능하다.
마지막으로 위에 두개의 설정을 해줬으면 마지막 index.txs를 설정해줘야 한다.
index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter} from "react-router-dom";
import {StyledEngineProvider} from '@mui/material';
import {client} from "./api/apollo/client";
import {CookiesProvider} from 'react-cookie';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {ApolloProvider} from "@apollo/client";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<StyledEngineProvider injectFirst>
<ApolloProvider client={client}>
<CookiesProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</CookiesProvider>
</ApolloProvider>
</StyledEngineProvider>,
);
reportWebVitals();
클라이언트에서 gql을 서버로 요청하여 데이터를 조회하고 요청해 보고 실습해보자.
Apollo.tsx
import React, {useEffect, useState} from "react";
import {Box, Button} from "@mui/material";
import {useMutation, useQuery} from "@apollo/client";
import {LOGIN_REQUEST} from "../api/apollo/mutation/AuthMutation.gql";
import {ALL_CATEGORY_TREE} from "../api/apollo/query/ItemQuery.gql";
const ApolloPage = () => {
const [isLogin, setIsLogin] = useState<boolean>(false)
const [login] = useMutation(LOGIN_REQUEST)
const {loading, error, refetch} = useQuery(ALL_CATEGORY_TREE)
const onLoginHandle = () => {
try {
login({
context: {
headers: {
'Authorization-mac': '00-00-00-00-00-00'
},
fetchOptions: {
credentials: 'include',
},
},
variables:{
request:{
pwd: '1234qwer@',
userId: 'asd1234',
sns: 'NORMAL',
}
},
onCompleted: loginSuccess,
})
}
catch (error){
console.log(error)
}
}
const loginSuccess = () => {
setIsLogin(true);
}
useEffect(() => {
refetch({
request:'01J5AAJE09VM9XQ16RF497DRAH'
})
}, [isLogin]);
const onLogoutHandle = () => {
setIsLogin(false)
}
if (loading) return <p>loading....</p>
if (error) return <p>Error :(</p>
return (
<Box>
{isLogin ? <Button onClick={onLogoutHandle}>로그아웃</Button> :
<Button onClick={onLoginHandle}>로그인</Button>}
</Box>
)
}
export default ApolloPage
실행결과
1. 카테고리 조회

2. 로그인

3. 화면 결과

실행 결과에서 확인할 수 있듯이 로그인 할 때 로그인 gql과 카테고리 gql 스키마를 서버로 요청하여 두번의 graphql 요청이 발생하는 것을 확인 할 수 있었다. 그리고 useQuery는 조회의 목적으로 useMutation은 삭제와 수정, 등록을 목적으로 사용 된다는 것을 알 수 있었다. 다음에는 useQuery, useLazyQuery, useMutation에 대한 Hook들에 대해 알아 보려고 한다.
참고
출처: 얄코 GraphQL 강의
React와 Apollo Client
어려운 프로그래밍 개념들을 쉽게 설명해주는 유튜브 채널 '얄팍한 코딩사전'. 영상에서 다 알려주지 못한 정보들이나 자주 묻는 질문들의 답변들, 예제 코드들을 얄코에서 확인하세요!
www.yalco.kr
출처: GraphQL 공식 문서
GraphQL | A query language for your API
Evolve your API without versions Add new fields and types to your GraphQL API without impacting existing queries. Aging fields can be deprecated and hidden from tools. By using a single evolving version, GraphQL APIs give apps continuous access to new feat
graphql.org
Introduction to Apollo Client
Search Apollo content (Cmd+K or /)
www.apollographql.com