01. What GraphQL?
graphQL는 Graph + Query Lenguage의 줄임말로 페이스북이 개발하여 오픈소스로 제공하는 쿼리 언어입니다. Server API를 통해 정보를 주고받기 위해 사용합니다. 즉 API를 위해 사용하는 쿼리 언어입니다.
Query Lenguage
서버에 CRUD를 요청하는 질의 언어입니다.
Graph형 자료구조를 사용하는 이유
GraphQL에서는 모든 데이터가 그래프 형태로 연결되어 있다고 전제합니다. 그래프라는 자료구조는 인간의 뇌 구조 및 언어적인 설명과 비슷하기 때문에 많은 분야에서 사용되는 방법입니다.
GraphQL는 그래프의 효율적인 탐색 방법을 차용하여 클라이언트 요청에 따라 유연하게 트리 구조의 JSON 데이터를 응답으로 전송합니다. 즉 클라이언트 요청에 따라 유연하게 자원을 가져올 수 있다는 이점이 있습니다.
그래프에서 트리 추출하는 방법
다음은 graphQL 쿼리와 그래프에서 트리를 추출하는 과정입니다.
// 요청을 보낸다. query { 책(ISBN:"9780674430006") { 책 이름 저자 { 이름 } } } // ISBN과 일치하는 값으로 응답을 받는다. { 책 : { 책 이름 : "GraphQL은 어렵지 않다", 저자 : [ { 이름 : "김코딩"}, { 이름 : "박해커"}, ] } }
GraphQL는 ISBN 번호로 선택한 “책" 노드부터 시작하여 중첩된 각 필드로 표시된 간선을 따라 그래프를 탐색하기 시작합니다. 즉 쿼리 내 중첩된 “책 이름” 필드를 통해 책의 제목이 있는 노드로 이동합니다. 그러면서 “저자”로 레이블이 지정된 “책”의 간선을 따라가 “저자” 노드를 가져오고, 각 저자의 “이름"을 얻어오는 것입니다.

02. GraphQL VS REST API
REST API의 한계
Overfetch — 요청하고자 하는 데이터 외의 데이터도 포함될 수 있습니다.
Underfetch — 하나의 속성 혹은 데이터의 여러 정보를 요청하기 위해 각각의 자원이 해당된 endpoint에 여러번 요청해야 합니다.
유지보수에 불리함 — 클라이언트 구조 변경 시 엔드포인트 변경 또는 데이터 수정이 필요합니다
GraphQL의 장점
No! under & overfetching — 하나의 endpoint로 원하는 데이터를 요청하고 응답받습니다.
강력한 playground — playground라는 GUI를 이용해 resolver 와 schema 를 한 눈에 보고 테스트 해 볼 수 있습니다. (POSTMAN과 유사)
유지보수에 용이함 — 클라이언트 구조가 바뀌어도 필요한 데이터를 결정하고 받는 주체가 클라이언트이기 때문에 서버에 지장이 없습니다.
GraphQL의 단점
캐싱이 REST보다 훨씬 복잡합니다 — File 전송 등 Text 만으로 하기 힘든 내용들을 처리하기 복잡하며 이를 보완하기 위해 Apollo 엔진의 캐싱과 영속 쿼리 등이 등장하게 되었습니다.
무거운 Query — 고정된 요청과 응답만 필요할 경우에는 Query로 인해 요청의 크기가 RESTful API의 경우보다 더 커집니다.
재귀적인 Query가 불가능합니다 — 결과에 따라 응답의 깊이가 얼마든지 깊어질 수 있는 API를 만들 수 없습니다.
03. Sprint: GraphQL 다루기
Getting Started
Creat-React-App — CRA를 설치합니다.
npx create-react-app 'app name'
graphQL 설치 — 하나의 속성 혹은 데이터의 여러 정보를 요청하기 위해 각각의 자원이 해당된 endpoint에 여러번 요청해야 합니다.
npm install @octokit/graphql
Github 토큰 생성 — Github endpoint를 불러오기 위해 토큰을 발급받습니다.
환경 변수 설정 — Node.js의 환경 변수로 발급받은 토큰을 root폴더 최상위단에 넣어줍니다. .env 작성 시 주의할 점은 값에 "" 혹은 ''를 사용하면 안됩니다. 또한 끝에 ;를 찍어서도 안됩니다.
// .env REACT_APP_TOKEN=발급받은 토큰
Bare Minimum Requirements
데이터 받아오기 — 배운 GraphQL 쿼리를 이용해 아고라 스테이츠에 접근하여 다양한 데이터를 조회해보았습니다.
//app.js
import { graphql } from "@octokit/graphql";
import { useEffect, useState } from "react";
import Output from "./Output";
function App() {
const token = process.env.REACT_APP_TOKEN;
// 환경변수에서 토큰값을 가져온다
const [repo, setRepo] = useState({});
const getRepo = async () => {
// 공식문서대로 graphql에서 구조분해할당해온 repository를 꼭 리턴해준다.
const { repository } = await graphql(
`
{
repository(owner: "codestates-seb", name: "agora-states-fe") {
discussions(first: 10) {
edges {
node {
id
title
createdAt
author {
avatarUrl
}
}
}
}
}
}
`,
{
headers: {
authorization: `token ${token}`,
},
}
);
return repository;
};
// 컴포넌트에 영향을 줄 수 있는 외부 데이터를 불러오는 것이기 때문에 useEffect로 제어한다. then의 인자로 들어오는 익명 함수의 인자인 data는 /graphql를 앤드포인트로 하여 요청된 repository의 데이터를 불러온다.
useEffect(() => {
getRepo()
.then((data) => setRepo(data))
.catch((error) => console.log(Error, error));
}, []);
return (
<div className="App">
<Output repo={repo} />
</div>
);
}
export default App;
컴포넌트 분리로 에러 핸들링하기 — discussion.edges가 새로고침 시 에러를 반환하며 값이 할당되지 않아 여러 방법을 찾아보다가 컴포넌트를 분리시켜 주었습니다.
//Output.js
const Output = ({ repo }) => {
let arr = [];
if (repo.discussions) {
// props로 전달받은 데이터에 discussions이 담겨있을 때 빈 배열에 edges를 할당
arr = repo.discussions.edges;
}
return (
<ul>
{arr.map((el) => {
const data = el.node;
return <li key={data.id}>{data.title}</li>;
})}
</ul>
);
};
export default Output;
참조