
ReactJS 기반의 온라인 강의 영상을 보며 Apollo Client의 보일러플레이트를 해부하려 한다. 물론 백엔드에서 GQL이 구현 되어있음을 전제로 하는 예제 코드다.
root 파일에 해당하는 app.tsx에 보일러플레이트와 Provider를 주입해야 한다. 이 코드가 root 파일에 있기 때문에, 그 하위 컴포넌트 어디에서든 GQL로 데이터를 조회할 수 있게 된다!
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
HttpLink,
from,
} from "@apollo/client";
import { onError } from "@apollo/client/link/error";
const errorLink = onError(({ graphqlErrors, networkError }) => {
if (graphqlErrors) {
graphqlErrors.map(({ message, location, path }) => {
alert(`Graphql error ${message}`);
});
}
});
// 에러 관리와 엔딩포인트를 변수로 선언하여 사용한다. instance.
const link = from([
errorLink,
new HttpLink({ uri: "http://localhost:4000/graphql" }),
]);
/*
* client 객체를 생성한다. InMemoryCache()로 데이터 캐싱을 기본값 두고,
* link에는 link instance를 담는다.
*/
const client = new ApolloClient({
cache: new InMemoryCache(),
link: link,
});
export default function App() {
return (
<ApolloProvider client={client}>
<Body/>
</ApolloProvider>
);
}
gql객체 내부에 작성되어 있는 것이 GraphQL의 query 기본형이다.
Query 객체로 시작해, 그 내부에는 백엔드에서 만들어 준 해당 Query Handler를 주입하고, 필요한 데이터 key를 그 객체 안에 다시 작성하는 방식이다. 이제 useQuery를 통해 어디에서든 이 데이터 구조와 내용을 가져올 수 있다. 데이터 명시성이 좋아 보인다.
import { gql } from "@apollo/client";
export const LOAD_USERS = gql`
query {
getAllUsers {
id
firstName
email
password
}
}
`;
이 부분은 createUser라는 POST 메소드를 위한 Mutation Query를 작성한 코드다. 이 Query를 호출하면, GQL로 작성된 백엔드 서버 데이터베이스에 해당 규격에 통과된 데이터를 POST할 수 있다. PUT이나 PATCH도 동일한 Query를 작성해 사용할 수 있겠다.
import { gql } from "@apollo/client";
/*
* 특징적이지만, JS 프로젝트라고 해도, 이렇게 타입을 작성해야 한다.
* mutaition createUser() 라는 객체 내부에, 다시 creatUser라는
* 백엔드 핸들러 객체를 중첩하여 작성해야만 구동된다.
*/
export const CREATE_USER_MUTATION = gql`
mutation createUser(
$firstName: String!
$lastName: String!
$email: String!
$password: String
) {
createUser(
firstName: $firstName
lastName: $lastName
email: $email
password: $password
) {
id
}
}
`;
apollo의 useQuery()를 사용해, 데이터를 조회하는 페이지 컴포넌트의 코드.
import { useEffect, useState } from "react";
import { useQuery, gql } from "@apollo/client";
import { LOAD_USERS } from "../GraphQL/Queries";
export default function Body() {
/*
* useQuery()는 인자로 LOAD_USERS라는 Query를 받는다.
* custom hooks처럼 필요한 item을 가져와 사용하면 된다.
* setUsers에 data의 getAllUsers 엔티티를 넣어주고 있다.
*/
const { error, loading, data } = useQuery(LOAD_USERS);
const [users, setUsers] = useState([]);
useEffect(() => {
if (data) {
setUsers(data.getAllUsers);
}
}, [data]);
return (
<div>
{/*
{users.map((val) => {
return <h1> {val.firstName}</h1>;
})} */}
</div>
);
}
Mutation 쪽의 코드는 약간 복잡한데, 기본적으로는 State에 event value를 삽입하여, 데이터 요청하는 구조는 익숙한 그 자체이다. 다만 CREATE_USER_MUTATION이라는 Query를 담보로 하는 createUser 핸들러를 직접 이용해, event handler를 사용해야 한다.
import { useState } from "react";
import { useMutation } from "@apollo/client";
import { CREATE_USER_MUTATION } from "../GraphQL/Mutations";
export default function Create() {
// state 4개와 input jsx 4개는 생략한다.
const [firstName, setFirstName] = useState("");
...
/*
* createUser GQL 서버 핸들러와 CREATE_USER_MUTATION 클라이언트
* Query를 기본으로 createUser 메소드를 활성시킨다.
*/
const [createUser] = useMutation(CREATE_USER_MUTATION);
const addUser = () => {
createUser({
/*
* 일종의 컨벤션으로 지켜야하는 부분이다. State로 담겨진 데이터를
* variables 객체로 감싸야 한다.
*/
variables: {
firstName: firstName,
lastName: lastName,
email: email,
password: password,
},
});
};
return (
<div>
<input
type="text"
placeholder="First Name"
onChange={(e) => {
setFirstName(e.target.value);
}}
/>
...
<button onClick={addUser}>Create!</button>
</div>
);
}