fullstackopen.com/en/part8/login_and_updating_the_cache 읽고 정리한 글.
Fullstack part8 |
Open online course on Javascript based modern web development by University of Helsinki and Houston Inc..
fullstackopen.com
목차
🔸 유저 로그인
- 로그아웃 시, apollo client의 캐시를 지우는 resetStore
🔸 헤더에 토큰 추가
- httpLink, apollo-link-context로 아폴로가 서버와 연결되는 방식에 접근하여 헤더를 수정
🔸 캐시 업데이트, revisited
- useMutation의 update 옵션을 통해 최적화. readQuery, writeQuery
유저 로그인
- useState로 token state를 생성 및 관리하고, useMutation으로 login mutation 생성한다.
- 로그인: login mutation의 결과가 들어오면 유저 토큰을 각각 token state와 local storage에 저장한다.
- 로그아웃: token state를 null로 업데이트 하고, local storage 값 삭제할 뿐만 아니라 Apllo client의 캐시도 리셋한다.
const App = () => {
const [token, setToken] = useState(null)
// ...
if (!token) {
return (
<div>
<Notify errorMessage={errorMessage} />
<h2>Login</h2>
<LoginForm
setToken={setToken}
setError={notify}
/>
</div>
)
}
return (
// ...
)
}
//...
export const LOGIN = gql`
mutation login($username: String!, $password: String!) {
login(username: $username, password: $password) {
value
}
}
`
const [ login, result ] = useMutation(LOGIN, {
onError: (error) => {
setError(error.graphQLErrors[0].message)
}
})
useEffect(() => {
if ( result.data ) {
const token = result.data.login.value
setToken(token)
localStorage.setItem('phonenumbers-user-token', token)
}
}, [result.data]) // eslint-disable-line
resetStore
- Apollo client 객체의 메소드이며, 앞서 말한 Apollo client의 캐시를 리셋한다.
- 여기에서는 useApolloClient Hook으로 client 객체를 가져왔다.
const App = () => {
const [token, setToken] = useState(null)
const [errorMessage, setErrorMessage] = useState(null)
const result = useQuery(ALL_PERSONS)
const client = useApolloClient()
if (result.loading) {
return <div>loading...</div>
}
const logout = () => {
setToken(null)
localStorage.clear()
client.resetStore() // <-
}
}
헤더에 토큰 추가
- 서버에 요청할 때, 헤더에 토큰을 담아보내야 한다.
- 이를 위해 ApolloClient 객체의 link 파라미터를 수정한다. link는 아폴로가 서버에 연결되는 방식을 정의한다.
- apollo-link-context 라이브러리를 설치한다.
- httpLink 연결을 수정하여, 서버로 요청할 때 authorization 헤더가 local storage에 저장된 토큰을 담아 보내도록 한다.
(www.apollographql.com/docs/react/api/link/introduction/)
import { setContext } from 'apollo-link-context'
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('phonenumbers-user-token')
return {
headers: {
...headers,
authorization: token ? `bearer ${token}` : null,
}
}
})
const httpLink = new HttpLink({ uri: 'http://localhost:4000' })
const client = new ApolloClient({
cache: new InMemoryCache(),
link: authLink.concat(httpLink)
})
+ )프론트에서 새 전화번호 등록할 때, phone 값을 입력하지 않으면 유효성 검사를 통과하지 못하고 에러 발생
phone 값을 삼항 연산자를 이용해 제어한다.
캐시 업데이트, revisited
새 유저를 전화번호부에 추가할 때, Apollo client 캐시를 업데이트 해야 한다. useMutation의 두 번째 파라미터로 refetchQueries 옵션을 주면 업데이트할 쿼리를 선택할 수 있다.
- 단점은 업데이트가 있을 때마다 rerun 한다는 점이다.
const PersonForm = ({ setError }) => {
// ...
const [ createPerson ] = useMutation(CREATE_PERSON, {
refetchQueries: [ {query: ALL_PERSONS} ],
onError: (error) => {
setError(error.graphQLErrors[0].message)
}
})
최적화
캐시 업데이트를 직접 핸들링 하면 된다. useMutation의 두 번째 파라미터인 옵션에서 update 필드에 아폴로가 mutation 이후에 호출할 콜백 함수를 정의해주면 된다. 캐시와 mutation에 의해 리턴되는 데이터가 파라미터로 들어오면서 이 콜백 함수를 참조한다.
const PersonForm = ({ setError }) => {
// ...
const [ createPerson ] = useMutation(CREATE_PERSON, {
onError: (error) => {
setError(error.graphQLErrors[0].message)
},
update: (store, response) => {
const dataInStore = store.readQuery({ query: ALL_PERSONS })
store.writeQuery({
query: ALL_PERSONS,
data: {
...dataInStore,
allPersons: [ ...dataInStore.allPersons, response.data.addPerson ]
}
})
}
})
// ..
}
- readQuery 함수로 ALL_PERSONS 쿼리의 캐시된 state를 읽고, writeQuery 함수로 캐시를 업데이트 한다. 새로 추가된 전화번호가 캐시된 데이터에 추가된다.
- 캐시가 특정된 쿼리를 충족시키는 데이터를 전부 담고 있지 않으면 readQuery가 에러를 던지는 것에 주의해야한다. try-catch 블록으로 잡도록 한다.
- 어떤 경우에는 update 콜백을 써야만 캐시를 항상 최신으로 유지할 수도 있다.
- 필요한 경우에는 전체 어플리케이션의 캐시나 단일 쿼리를 막아둘 수도 있다. fetchPolicy를 no-cache로 설정하면 캐시 사용을 제어할 수 있다. (www.apollographql.com/docs/react/data/queries/#configuring-fetch-logic)
캐시에 오래된 데이터가 있으면 버그를 찾기 어려울 수도 있으므로 부지런히 관리하도록 한다.
There are only two hard things in Computer Science: cache invalidation and naming things.
'GraphQL' 카테고리의 다른 글
Apollo GraphQL Error: "Cannot read property 'startsWith' of undefined" (0) | 2021.03.17 |
---|---|
Fullstack Part 8 #5 Fragments and subscriptions (0) | 2021.03.12 |
Fullstack Part 8 #3 DB (0) | 2021.03.10 |
Fullstack part 8 #2 React and GraphQL (0) | 2021.03.09 |
Fullstack Part 8 #1 GraphQL-server (0) | 2021.03.08 |
댓글