2020년 9월 15일 기준으로 apollo-client가 v3.0을 맞이했습니다.
그러면 무엇을 해야할까요.
레거시에서 마이그레이팅해야합니다...
분명 20년 5월에 apollo 공식 문서 get started에서 apollo-boost
튜토리얼이 있던게 어제 같은데
레거시가 되어버린 https://github.com/Kunune/mo-gak-ko-front-public 코드를 업데이트해야 합니다.
Local Resolver
가 deprecated 되었습니다.@apollo/react-hooks
와 react-apollo
가 코어 패키지인 @apollo/client
로 병합됩니다.기존의 코드는 이렇습니다.
수정할 부분
을 ❗
으로 마킹해봤습니다.
📚 src/apollo/client.tsx
👇
import ApolloClient from "apollo-boost"; ❗
import { defaults, resolvers } from "./localState"; ❗
export default new ApolloClient({
uri: ❗
process.env.NODE_ENV === "production" ❗
? process.env.REACT_APP_PROD_API_URL ❗
: process.env.REACT_APP_DEV_API_URL, ❗
clientState: { ❗
defaults, ❗
resolvers, ❗
}, ❗
request: (operation) => { ❗
const token = localStorage.getItem("token"); ❗
operation.setContext({ ❗
headers: { ❗
authorization: token ? `Bearer ${token}` : "", ❗
}, ❗
}); ❗
}, ❗
}); ❗
--------------------------------------------------------------------
📚 src/apollo/localState.ts
👇
import { Resolvers } from "apollo-boost"; ❗
export const defaults = { ❗
isLoggedIn: Boolean(localStorage.getItem("token")) || false, ❗
}; ❗
export const resolvers: Resolvers = { ❗
Mutation: { ❗
logUserIn: (_, { token }, { cache }) => { ❗
localStorage.setItem("token", token); ❗
cache.writeData({ ❗
data: { ❗
isLoggedIn: true, ❗
}, ❗
}); ❗
return null; ❗
}, ❗
logUserOut: (_, __, { cache }) => { ❗
localStorage.removeItem("token"); ❗
cache.writeData({ ❗
data: { ❗
isLoggedIn: false, ❗
}, ❗
}); ❗
window.location.href = "/"; ❗
return null; ❗
}, ❗
}, ❗
}; ❗
이런! 그냥 새로 작성하는 것이 나을 정도입니다!
📌 local state를 cache에 저장하는 법
📌 모든 request에 header > authorization을 보내는 법
📚 src/apollo/client.tsx
👇
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink, concat, } from "@apollo/client";
import { isLoggedIn } from "./cache";
const httpLink = new HttpLink({
uri:
process.env.NODE_ENV === "production"
? process.env.REACT_APP_PROD_API_URL
: process.env.REACT_APP_DEV_API_URL,
}); 👈 GraphQL API 주소를 정의하는 Link
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext({
headers: {
authorization: localStorage.getItem("token") || null,
},
});
return forward(operation);
}); 👈 인증 미들웨어로 httpLink와 같이 동작하는 함수
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
isLoggedIn: {
read() {
return isLoggedIn();
},
},
},
},
},
}); 👈 문서에서 이렇게 cache에 local state를 저장하라고 한다.
export default new ApolloClient({
link: concat(authMiddleware, httpLink), 👈 인증 미들웨어, API URL 병합
cache,
});
---------------------------------------------------------
📚 src/apollo/cache.ts
👇
import { makeVar } from "@apollo/client";
export const isLoggedIn = makeVar(
Boolean(localStorage.getItem("token")) || false
); 👈 makeVar로 값을 지정해두면 그 변수는 localState가 된다.
export const logIn = (token: string) => {
localStorage.setItem("token", token);
isLoggedIn(true);
}; 👈 makeVar로 만들어진 변수는 고차 함수, Closure같은 개념으로 작동한다.
export const logOut = () => {
localStorage.removeItem("token");
isLoggedIn(false);
window.location.reload();
};
React와 연결하는 ApolloProvider
는 패키지 명만 바뀝니다.
import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider } from "@apollo/react-hooks"; 👈
import App from "./components/App";
import client from "./apollo/client";
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
);
import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider } from "@apollo/client"; 👈
import App from "./components/App";
import client from "./apollo/client";
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
);
이 앱은 로그인 버튼을 누르면 github auth
로 넘어가고 callback url
로 이동한 컴포넌트에서 토큰 처리 후 메인 페이지로 이동합니다.
로그아웃
도 동일한 방법으로 수정.
📚 해당 기능의 컴포넌트 중 코드 일부분
👇
import { useMutation } from "@apollo/react-hooks";
import { LOG_USER_IN } from "../queries/globalQueries";
function Auth() {
const [logUserIn] = useMutation(LOG_USER_IN);
useEffect(() => {
async function getToken() {
const { token } = await fetch(
process.env.NODE_ENV === "production"
? `${process.env.REACT_APP_PROD_API_URL}/auth/github?code=${code}`
: `${process.env.REACT_APP_DEV_API_URL}/auth/github?code=${code}`,
{
method: "POST",
}
).then((res) => res.json());
logUserIn({ variables: { token } }); ❗
}
getToken();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}
import { logIn } from "../apollo/cache"; 👈 InMemoryCache에 넣어둔 그것
function Auth() {
...
useEffect(() => {
async function getToken() {
const { token } = await fetch(
process.env.NODE_ENV === "production"
? `${process.env.REACT_APP_PROD_API_URL}/auth/github?code=${code}`
: `${process.env.REACT_APP_DEV_API_URL}/auth/github?code=${code}`,
{
method: "POST",
}
).then((res) => res.json());
logIn(token); 👈 인자로 string을 받던 그 함수 맞습니다
}
getToken();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return ...
}