graphQL는 Graph + Query Lenguage의 줄임말로 페이스북이 개발하여 오픈소스로 제공하는 쿼리 언어입니다. Server API를 통해 정보를 주고받기 위해 사용합니다. 즉 API를 위해 사용하는 쿼리 언어입니다.
서버에 CRUD를 요청하는 질의 언어입니다.
// 요청을 보낸다.
query {
책(ISBN:"9780674430006") {
책 이름
저자 {
이름
}
}
}
// ISBN과 일치하는 값으로 응답을 받는다.
{
책 : {
책 이름 : "GraphQL은 어렵지 않다",
저자 : [
{ 이름 : "김코딩"},
{ 이름 : "박해커"},
]
}
}
GraphQL는 ISBN 번호로 선택한 “책" 노드부터 시작하여 중첩된 각 필드로 표시된 간선을 따라 그래프를 탐색하기 시작합니다. 즉 쿼리 내 중첩된 “책 이름” 필드를 통해 책의 제목이 있는 노드로 이동합니다. 그러면서 “저자”로 레이블이 지정된 “책”의 간선을 따라가 “저자” 노드를 가져오고, 각 저자의 “이름"을 얻어오는 것입니다.
Overfetch
요청하고자 하는 데이터 외의 데이터도 포함될 수 있습니다.
Underfetch
하나의 속성 혹은 데이터의 여러 정보를 요청하기 위해 각각의 자원이 해당된 endpoint에 여러번 요청해야 합니다.
유지보수에 불리함
클라이언트 구조 변경 시 엔드포인트 변경 또는 데이터 수정이 필요합니다
No! under & overfetching
하나의 endpoint로 원하는 데이터를 요청하고 응답받습니다.
강력한 playground
playground라는 GUI를 이용해 resolver 와 schema 를 한 눈에 보고 테스트 해 볼 수 있습니다. (POSTMAN과 유사)
유지보수에 용이함
클라이언트 구조가 바뀌어도 필요한 데이터를 결정하고 받는 주체가 클라이언트이기 때문에 서버에 지장이 없습니다.
캐싱이 REST보다 훨씬 복잡합니다
File 전송 등 Text 만으로 하기 힘든 내용들을 처리하기 복잡하며 이를 보완하기 위해 Apollo 엔진의 캐싱과 영속 쿼리 등이 등장하게 되었습니다.
무거운 Query
고정된 요청과 응답만 필요할 경우에는 Query로 인해 요청의 크기가 RESTful API의 경우보다 더 커집니다.
재귀적인 Query가 불가능합니다
결과에 따라 응답의 깊이가 얼마든지 깊어질 수 있는 API를 만들 수 없습니다.
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=발급받은 토큰
//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;