Apollo Client - ParkEunwoo/seoul-smart-app GitHub Wiki
- Apollo Client๋?
- Apollo Boost๋ก ์ฝ๊ฒ ์์ํ๊ธฐ
- graphql query ์์ฑํ๊ธฐ
- ๋ฐ์ดํฐ ํ๋ฉด์ ๋์ฐ๊ธฐ
- Apollo Cache
apollo client๋ javascript app์ ์ํ๊ด๋ฆฌ๋ฅผ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ๊ธฐ์กด redux๋ณด๋ค ํจ์ฌ ๊ฐํธํ๊ณ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ฝ๊ณ ๊ฐํธํ๋ค. redux์์๋ ๊ฐ์ ธ์ฌ๋๋ง๋ค ํด๋น ์๋ฒ์ url์ด ํ์์ง๋ง ์๋ฒ๊ฐ graphql๋ก ๋ง๋ค์ด์ก๋ค๋ฉด ํ๋ฒ์ url์์ฑ์ผ๋ก graphql์ ์ฝ๊ฒ query๋ฅผ ์์ฑํ ์ ์๋ค.
apollo client๋ cache, http๋ฑ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ํด์ผํ๋ ๊ฒ๋ค์ด ์กด์ฌํ๋ค. ํ์ง๋ง apollo boost๋ฅผ ์ด์ฉํด ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
yarn add apollo-boost
apolloClient.js ํ์ผ์ ๋ง๋ค์ด์ ๊ธฐ๋ณธ ์ค์ ์ ํด์ค๋ค.
import ApolloClient from 'apollo-boost';
export const client = new ApolloClient({
uri: 'http://seoul-smart-api.herokuapp.com',
});
App.js ํ์ผ์์ ์ํ๊ด๋ฆฌ๋ฅผ ์ํ provider๋ก ๊ฐ์ธ์ค๋ค. provider๋ฅผ ์ฝ๊ฒ ๋ง๋ค๊ธฐ ์ํด apollo/react-hook์ ์ฌ์ฉํ๋ค.
yarn add @apollo/react-hooks
...
import { ApolloProvider } from '@apollo/react-hooks';
import client from './apolloClient.js';
export default function App(props) {
...
return (
<ApolloProvider client={client} >
<View />
</ApolloProvider>
);
}
์ด๋ฌ๋ฉด App์ด ํฌํจํ๋ ์ปดํฌ๋ํธ๋ค์์ apollo๋ฅผ ํตํด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ค์ ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผํ ๊น?
yarn add graphql-tag
graphql์ query๋ฅผ ์์ฑํ ์ ์๊ฒ ํด์ฃผ๋ ๋ชจ๋์ด๋ค. query๋ค์ ํ ๊ณณ์ ๋ชจ์๋๊ธฐ ์ํด์ query ํด๋๋ฅผ ์์ฑํ๋ค. ํด๋์์ index.js ํ์ผ์ ๋ง๋ค์ด์ค๋ค. ์์๋ก ์ฅ์๋ชฉ๋ก์ ๋ถ๋ฌ์ค๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํด๋ณด๊ฒ ๋ค.
import gql from 'graphql-tag';
const GET_PLACES = gql`
query getPlaces($page:Int) {
getPlaces(page: $page) {
id
name
}
}
`;
export {
GET_PLACES
};
์์๊ฐ์ด gql
๋ค์ ~์๋์ ์๋ ` ์์ query๋ฌธ์ ์์ฑํด์ค๋ค. ๋ง์ฝ ์ฟผ๋ฆฌ๋ฌธ์ input๊ฐ์ด ๋ค์ด๊ฐ๋ ๊ฒฝ์ฐ ์์ ํด๋น query๋ฌธ์ ๋ค์ ํจ์๋ก ๋ง๋ค์ด์ฃผ๊ณ input๋ฐ์ ๊ฐ์ ์คํค๋ง๋ฅผ ์ ํด์ค๋ค. ์ด๋ ๋ณ์๋ก ์ฌ์ฉ๋ ์ด๋ฆ ์์ $๋ฅผ ๋ถ์ฌ์ค๋ค.
mutation๋ ์ด์ด์ ์์ฑํด๋ณด์.
import gql from 'graphql-tag';
...
const SIGN_UP = gql`
mutation createUser($token: String!, $name: String!) {
createUser(token: $token, name: $name) {
id
name
achievement
activityLog
}
}
`
export {
GET_PLACES,
SIGN_UP
}
์์ฑํ query๋ฌธ์ ์ค์ ๋ก component์์ ์ฌ์ฉํ๊ธฐ ์ํด์ react hook์ ์ด์ฉํ๋ค. ๊ผญ ๊ทธ๋์ผํ๋ ๊ฑด ์๋์ง๋ง ์ฐ๋ฆฌ๋ hook์ ์ด์ฉํ๋ค ์ด์ ์๋ฒ์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋ ์ปดํฌ๋ํธ์์ ์์
์ ์์ํ๋ค. ์ฅ์ ๋ชฉ๋ก์ ๋ณด์ฌ์ค์ผํ๋ ์ปดํฌ๋ํธ์ด๊ณ ๋ชฉ๋ก์์ ๋์ธ ์์ดํ
์ปดํฌ๋ํธ๊ฐ ์๋ค๊ณ ๊ฐ์ ํ์.
...
import { useQuery } from '@apollo/react-hooks';
import PlaceItem from './PlaceItem';
import { GET_PLACES } from './../query';
export default function PlaceList(){
const { loading, error, data } = useQuery(
GET_PLACES,
{ variables: { page: 1 } }
);
if(loading) return <View>Loading...</View>;
if(error) return <View>Error...</View>;
const placeList = data.getPlaces.map(({id, name}) => <PlaceItem key={id} name={name} />)
return (
<View>
{placeList}
</View>
);
}
useQuery๋ฅผ ์ด์ฉํด loading, error, data๋ฅผ ๊ฐ์ ธ์จ๋ค. data์ ํด๋น resolver์ด๋ฆ์ผ๋ก ์ ์ฅ๋ ์ ๋ณด๊ฐ ๋ฐํ๋๋๋ฐ ์ฅ์๋ ๋ฐฐ์ด์ด๊ธฐ ๋๋ฌธ์ mapํจ์๋ฅผ ์ด์ฉํด ๊ฐ๊ฐ์ ๋ฆฌ์คํธ์ ํ์ํ๋ค.
mutation๋ hook์ ์ด์ฉํด์ ์ฌ์ฉํ ์ ์๋ค. useMutation
์ useQuery
์ ๋ค๋ฅด๊ฒ mutation query๋ฅผ ๋ณด๋ผ ์ ์๋ ํจ์๋ฅผ ๋ฐํํ๋ค.
...
import { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import { SIGN_UP } from './../query';
export default function SignUp(){
const [signUp, { loading, error, data }] = useMutation(SIGN_UP);
const [token, setToken] = useState();
const [name, setName] = useState();
return (
<View>
<TextInput value={token} onChangeText={(text) => setToken(text)}></TextInput>
<TextInput value={name} onChangeText={(text) => setName(text)}></TextInput>
<TouchableOpacity onClick={() => singUp({ variables: { token, name } })} >
<Text>๋ฒํผ</Text>
</TouchableOpacity>
</View>
);
}
react hook์ ์ด์ฉํด state๋ฅผ ์์ฑํ๊ณ input์ฐฝ์์ ์ ๋ ฅ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ state์ ๋ฃ๊ณ useMutaion์ผ๋ก ๋ง๋ signUp ํจ์์ ๋ฃ์ผ๋ฉด ํด๋น ๊ฒฐ๊ณผ๊ฐ loading, error, data์ ๋ค์ด์ค๊ฒ ๋๋ค.
apollo์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ redux๋ฅผ ๋์ฒดํด client์ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค๋ ์ ์ด๋ค. apollo๋ cache ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ด์ฉํด ํ๋ฒ ์๋ฒ์์ ๋ฐ์์จ ๋ฐ์ดํฐ๋ ์ ์ฅํด๋๊ณ ๋ค์ ์์ฒญ์ ํ ๋๋ cache๋ฅผ ํ๋ฒ ํ์ธํ ํ์ ์๋ฒ์ ํต์ ํ๊ธฐ ๋๋ฌธ์ ์์ฒญ์ ์ค์ผ ์ ์๋ค. ๋ก์ปฌ์์๋ cache ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ด์ฉํ ์ ์๋๋ฐ ์ด ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ๋ค.
apolloClient.js
์ ๊ฐ์ ๊ณณ์ clientState.js
ํ์ผ์ ๋ง๋ค์ด์ค๋ค. ์ด ํ์ผ์์๋ default, schema, resolver๋ฅผ ์์ฑํด ์ค ๊ฒ์ด๋ค.
apolloClient.js
import ApolloClient from "apollo-boost";
import clientState from "./clientState";
const client = new ApolloClient({
uri: "http://seoul-smart-api.herokuapp.com",
clientState
});
export default client;
graphql ์๋ฒ๋ฅผ ๋ง๋ค๋์ฒ๋ผ ๋์ผํ๊ฒ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค. ์ฑ ๋ด์์ ํธ์งํ๋ ํ๋๋ค์ ์ํ๋ฅผ ๊ด๋ฆฌํด๋ณด๋๋ก ํ๊ฒ ๋ค.
clientState.js
import { EDIT_FRAGMENT } from "./fragments";
const defaults = {
edits: [{
__typename: 'Edit',
editing: false,
id: 'new',
name: 'ํ๋์ด๋ฆ',
total: 3,
date: '2019-09-22',
startTime: '02:00',
endTime: '03:00',
placeId: '์ฅ์id',
room: '์ง๋',
content: '๋ด์ฉ',
type: 'mentoring'
}]
};
const typeDefs = [`
schema {
query: Query,
mutation: Mutation,
}
type Query {
edits: [Edit]!
edits(id: String!): Edit!
}
type Mutation {
modifyEdit(id: String!): Edit!
}
type Edit {
editing: Boolean!
id: String
name: String
total: Int
date: String
startTime: String
endTime: String
placeId: String
room: String
content: String
type: String
}
`];
const resolvers = {
Query: {
edits: (_, variables, { cache }) => {
const id = cache.config.dataIdFromObject({ __typename: "Edit" });
return cache.readFragment({ frament: EDIT_FRAGMENT, id });
}
},
Mutation: {
modifyEdit: (_), variables, { cache }) => {
const editQuery = cache.readQuery({query: GET_EDITS});
const {name, total, date} = variables;
const modifiedEdit = {
__typename: 'Edit',
name, total, date,
...
}
cache.writeData({
data: {
edits: [modifedEdit, ...edits]
}
});
return modifedEdit;
}
}
};
export default {
defaults,
typeDefs,
resolvers
};
resolver๋ถ๋ถ๋ง ์ฐจ์ด๊ฐ ๋ง์ด ๋๋๋ฐ ๊ธฐ์กด ์๋ฒ๋ mongodb์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์๋ค๋ฉด ์ฌ๊ธด cache์์ ๊ฐ์ ธ์์ผํ๋ค. ๊ฐ์ ธ์ค๋ ๋ฐฉ์์ cache.dataIdFromObject๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด๋ค. ์์ query๋ฌธ์ ๋ฃ์ด์ผ ํ๋๋ฐ ์์ฃผ ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ fragment๋ก ๋ง๋ค์ด์ ์ ์ฅ์ ํด๋๋ค.
fragment.js
import gql from "graphql-tag";
export default EDIT_FRAGMENT = gql`
fragment EditPars on Edit {
editing
id
name
total
date
startTime
endTime
placeId
room
content
type
}
`;
์ด๋ ๊ฒ clientState๊ฐ ์์ฑ๋์๋ค. ์ด์ ์ํ๋ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค. ํด๋น ์ปดํฌ๋ํธ ํด๋์ queries.js
ํ์ผ์ ๋ง๋ค์ด์ค๋ค.
queries.js
import gql from "graphql-tag";
export const GET_EDIT = gql`
{
edit @client {
editing
title
content
}
}
`;
์ฟผ๋ฆฌ๋ฌธ ์์ @client๋ฅผ ๋ถ์ฌ์ฃผ๋ ๊ฒ์ ์ ์ธํ๊ณ ๊ธฐ์กด๊ณผ ๊ฐ๋ค.