노마드코더에서 우버클론코딩을 하면서 GraphQL, Apollo를 배우고 있다.
Typescript와 함께 쓰면서 백엔드와 타입을 일치시켜서 보호하는 법도 배워서 매우 유용하다 생각해서 정리를 해보려고 한다!
Apollo만을 정리하기 때문에 GraphQL서버는 이미 구현되어있다고 가정을 한다.
- set up
yarn add @apollo/client graphql
2. apollo client 파일 생성(이름은 마음대로)
import { ApolloClient, createHttpLink, InMemoryCache, makeVar } from '@apollo/client';
import { LOCALSTORAGE_TOKEN } from './constants';
import { setContext } from '@apollo/client/link/context';
const token = localStorage.getItem('token');
export const tokenVar = makeVar(token); //null
const http = createHttpLink({
uri: 'graphql server',
});
const auth = setContext((_, { headers }) => {
return {
headers: {
...headers,
'token': authTokenVar() || '',
},
};
});
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
token: {
read() {
return tokenVar();
},
},
},
},
},
}),
});
- Apollo client를 생성할 때는 uri, cache가 기본으로 요구된다. 해당 코드에서는 link를 사용했는데 그 이유는 header에 token을 넣고 통신하기 위해서다(auth)
- 1. uri를 정의한 http와 header를 이용해 context를 설정한 auth를 concat해서 link를 정의한다.
- 2. cache를 생성한다. (일단 typePolicies는 다음에 보자)
- 3. makeVar()로 responsive variable을 생성한다. responsive variable은 local state를 읽고 수정하기 위한 기능이다.
- *** 여기서 local state는 graphql schema에는 없지만 apollo에서 다룰 값을 의미한다. 로그인 상태나 다크모드 등이 있다.
- 4. cache에서 typePolicies를 이용해 resVar(3)을 read함수로 정의해서 반환하도록 한다.
- NOTE!! 사실 makeVar만 사용해도 local state를 사용할 수 있다. 하지만 typePolicies를 같이 사용하는 이유는 graphql server에서 받아오는 데이터와 함께 사용할 수 있기 때문인 듯하다. (apollo blog에서도 해당 방법을 이용하는 것이 local state를 관리하기에 더 편한 이유라고 설명하고 있다.)
2. apollo code generation 적용하기
code:gen을 적용하는 이유는 GraphQL서버의 Schema의 타입을 apollo에서 지키도록 하기 위함이다. 처음에는 번거로운 일이 아닌가했는데 한번 적용하고 나니 훨씬 편리하고 실수가 적어졌다.
2-1. 먼저 apollo를 설치한다.
yarn add global apollo
2-2. apollo.config.js
module.exports = {
client: {
includes: ["./src/**/*.tsx"],
tagName: "gql",
service: {
name: "something",
url: "graphql server",
},
},
};
3. package script에 적용
"apollo": "rm -rf src/__api__ && apollo client:codegen src/__api__ --target=typescript --outputFlat",
"start": "yarn apollo:codegen & react-scripts start",
script를 실행하면 사용된 Query나 Mutation의 type을 정의한 파일을 생성한다.
3. Query 사용예시 : 사용자의 정보를 받아오는 커스텀 훅 useClient()
import { gql, useQuery } from '@apollo/client';
import { clientQuery } from '../__api__/meQuery';
const CLIENT_QUERY = gql`
query clientQuery {
client {
id
email
}
}
`;
export const useClient = () => {
return useQuery<clientQuery>(CLIENT_QUERY);
// {data, loading, error} = useClient();
};
4. Mutation 사용예시 :
import { gql, useMutation } from '@apollo/client';
//created by codegen
import { signupMu, signupMuVariables } from '../__api__/signupMu';
import { UserRole } from '../__api__/globalTypes';
const SIGNUP_MUTATION = gql`
mutation signupMu($input: SignupInput!) {
signUp(input: $input) {
ok
error
}
}
`;
// output : data{signUp{error, ok}}
export const Signup = () => {
const onCompleted = (data: signupMu) => {
//logic ...
};
const [signupMu, { data: isSignup, loading }] = useMutation<signupMu, signupMuVariables>(SIGNUP_MUTATION, {
onCompleted,
});
const onSubmit = () => {
if (!loading) {
signupMu({
variables: {
input: {
email,
password,
},
},
});
}
};
return ( ... )
}
5. Cache update 사용예시
먼저 Apollo extension으로 캐시를 확인한다.
import { gql, useApolloClient, useMutation } from '@apollo/client';
export const ConfirmEmail = () => {
const client = useApolloClient();
const onCompleted = (data: Type) => {
client.writeFragment({
id: `User:${user?.me.id}`,
fragment: gql`
fragment Something on User {
verified
}`,
data: {
verified: true,
},
});
}
- id는 수정할 캐시를 식별하도록 하는데 위의 이미지에서 분홍색 박스에 해당한다.
- fragment에 수정할 속성(key), data에는 실제로 수정할 값을 넣는다.