๋ถ์กฑํ ๊ฐ๋ฐ์๋ ๊ธ์ ๋์ผ๋ก๋ง ์ฝ์ด์๋ ์ค์ํ ์ง์์ ๋จธ๋ฆฌ์ ๋จ๊ธธ ์ ์๊ธฐ์
react-query ๊ณต์ ๋ฌธ์์ ๋ด์ฉ ์ค ์ค์ํด ๋ณด์ด๋ ๊ฒ๋ค์ ๋ฒ์ญํ๋ฉฐ ์ ๋ฆฌํ๊ฒ ์ต๋๋ค.
์์ญ ๋ง์์!! ํ์ง๋ง ์ฌ๋ฌ๋ถ๋ค์ด ์ฝ๊ธฐ์ ์ ํํ ๋ฒ์ญ๋ณด๋จ ์ฝ๊ธฐ ํธํ ๋ฒ์ญ์ด ์ข์ง ์์๊น์??
๐ ๋ฉ์ธ ํ์ด์ง
๋ฆฌ์กํธ๋ฅผ ์ํ ๊ฐ๋ ฅํ ๋ฐ์ดํฐ ๋๊ธฐํ
React์ React Native์์ ์ ์ญ ์ํ ๋ณ๊ฒฝ ์์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์บ์ฑํ๊ณ ์
๋ฐ์ดํธ ํ์ธ์!
- ์ ์ธ์ ์ด๋ฉด์ ์๋ํ ๋ ํน์ง
- ์์ผ๋ก ์์ฑํ๋ ๋ฐ์ดํฐ ํ์นญ ๋ก์ง์ ์ด์ ๊ทธ๋ง
- react query์๊ฒ ์ด๋์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์ผ๋ง๋ ์ต์ ์ ๋ฐ์ดํฐ๊ฐ ํ์ํ์ง๋ง ๋งํ์ธ์
- ๋ณ๋ค๋ฅธ ์ค์ ์์ด๋ ๋๋จธ์ง ๋๋ถ๋ถ์ ์ฐ๋ฆฌ๊ฐ ์ฒ๋ฆฌํด์ค๊ฒ์
- ๊ฐ๋จํ๊ณ ์น์ํ ํน์ง
- ๋น์ ์ด promise๋ง ๋ค๋ฃฐ ์ ์๋ค๋ฉด ๋น์ ์ ์ด๋ฏธ react-query๋ฅผ ๋ง์คํฐํ๋ค๊ณ ํ ์ ์์ต๋๋ค
- ์ฐ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋ ๊ธ๋ก๋ฒ ์ํ ๊ด๋ฆฌ๋ ๋ฆฌ๋์ ๊ฐ์ด ๋ณต์กํ ์ค์ ์ด ํ์ ์์ด์
- ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ (๊ทธ๋ฆฌ๊ณ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ ) ํจ์๋ง ๋์ ธ์ฃผ์ธ์
- ๋ค์ํ ์กฐ์์ด ๊ฐ๋ฅํด ๊ฐ๋ ฅํ ํน์ง
- ์ฟผ๋ฆฌ์ ๋ชจ๋ observer ๊ฐ์ฒด๋ง๋ค ํน๋ณํ ์ค์ ์ ์ถ๊ฐํ ์ ์์ด์
- ๊ฐ๋ ฅํ ๊ฐ๋ฐ์ ๋๊ตฌ ๋ฑ์ด ์๊ธฐ์ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ์ ํจ๊ป๋ผ๋ฉด ๋ฐ์ดํฐ ์กฐ์์ ์์ ์ฃฝ ๋จน๊ธฐ๋ผ๊ตฌ์!
- ๊ทธ๋ ๋ค๊ณ ๋๋ฌด ๊ฑฑ์ ์ ๋ง์ธ์. ์ด๋ฐ ๊ฒ๋ค์ ๋ค ์ฐ๋ฆฌ๊ฐ ์ด๋ฏธ ๊ธฐ๋ณธ ์ธํ
์ ์๋ฃํด๋์์ผ๋๊น์!
๊ณต์ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ ๊ฐ์ข๋ ์์ด์! ๋งํฌ
ํ์์ฌ ๋ชฉ๋ก
์ ์ ์ฝ๋์ ๋๋ฌธ ํฌ๊ท ์ผ์ด์ค
- ๋ณต์กํ ๊ฑฐ ์ด๊ฑฐ ์ ๊ฑฐ ๋ค ์ ์ ํ์๊ฐ ์์ด์!
- ์ผ๋จ ํ๋ฒ ์จ๋ณด์ธ์
- ์ผ๋ง๋ ์ฝ๋๊ฐ ์ ์ด์ง ์ ์๋์ง ๊น์ง ๋๋ผ์ค ๊ฒ๋๋ค.
๋ฆฌ์กํธ ์ฟผ๋ฆฌ ํ๋๋ก ๋ชจ๋ ๊ธฐ๋ฅ์ด ๋ฐ๋ผ์ต๋๋ค
- (๋ค์ํ ๊ธฐ๋ฅ๋ค..)
- ๋ณ๋ ฌ ์ฟผ๋ฆฌ ๊ธฐ๋ฅ
- SSR ์ง์
โ
Docs ํญ (์์ํ๊ธฐ - Getting Started)
๊ฐ๊ด
๊ฐ๋ฐ ๋๊ธฐ
- ๋ฆฌ์กํธ๋ ์ฌ์ค ์ ์์ผ๋ก ์ง์๋๋ ๋ฐ์ดํฐ fetching ๋ฐฉ์์ด ์์๋ค
- ๊ทธ๋์ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์๊ธฐ ๋ง์ ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐํ๊ฑฐ๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ๋ง ํ๋ค.
- ๊ทธ๋ฐ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋๋ถ๋ถ ํ๋ฅญํ๊ธด ํ์ง๋ง, ๋ถ์กฑํ ์ ๋ ๋ง์๋ค.
- ์ฐ์ ํด๋ผ์ด์ธํธ์ ์ํ๋ ์๋ฒ ์ํ์ ๊ทผ๋ณธ์ ์ผ๋ก ๊ตฌ๋ณ๋๋ค๋ ์ ์ด ๊ฐ์ฅ ํฐ ๋ฌธ์ ์๋ค.
- ์ด๋ป๊ฒ ์ด๋ป๊ฒ ์๋ฒ์ ์ํ ๊ด๋ฆฌ๋ฅผ ์ปจํธ๋กค ํ ์ ์๊ฒ ๋์๋ค ํ๋๋ผ๋ ๋ ํฐ ๋ฌธ์ ๋ค์ด ๋ง์ด ์์ ๊ฒ์ด๋ค.
- ๋ง์ฝ ์ด๋ฐ ๋ฌธ์ ๋ค์ด ๋ณ๋ก ๋๋จํ๊ฒ ๋๊ปด์ง์ง ์๋๋ค๋ฉด, ๋น์ ์ ๋ถ๋ช
ํ ๋๊ตฌ๋๊ฐ ์ธ์ ํ๋ ํน์์งฑ ๊ฐ๋ฐ์์ผ ๊ฒ์ด๋ค.
- ๊ทธ๋ฌ๋ ๋๋ถ๋ถ์ ์ฌ๋๋ค์๊ฒ ์ด๋ ๋๋จํ ์ด๋ ค์ด ๋ฌธ์ ์ด๋ค.
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ ์๋ฒ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ๋๋จํ ํ๋ฅญํ ์ต๊ณ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๋ง์ ๋๊ณ , ์์ ์ฝ๋ ๋จผ์ ๋ณด์ฌ์ค๊ฒ!
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
function Example() {
const { isLoading, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>๐ {data.subscribers_count}</strong>{' '}
<strong>โจ {data.stargazers_count}</strong>{' '}
<strong>๐ด {data.forks_count}</strong>
</div>
)
}
์ค์น
yarn add react-query
- ์ต์ ์๊ตฌ ์กฐ๊ฑด react v 16.8+
๋น ๋ฅธ ์คํ (quick start)
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from 'react-query'
import { getTodos, postTodo } from '../my-api'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
function Todos() {
const queryClient = useQueryClient()
const query = useQuery('todos', getTodos)
const mutation = useMutation(postTodo, {
onSuccess: () => {
queryClient.invalidateQueries('todos')
},
})
return (
<div>
<ul>
{query.data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
<button
onClick={() => {
mutation.mutate({
id: Date.now(),
title: 'Do Laundry',
})
}}
>
Add Todo
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
๊ฐ๋ฐ ๋๊ตฌ
- ์ฐ๋ฆฌ๊ฐ ์ง์ง ์ข์ ๊ฐ๋ฐ ๋๊ตฌ ๋ง๋ค์ด ๋จ๋ค๊ตฌ!
- ๊ทผ๋ฐ ์์ง react native ์ฉ์ ์์ดใ
(22๋
4์ 25์ผ ๊ธฐ์ค)
import { ReactQueryDevtools } from 'react-query/devtools'
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ ๊ฐ๋ฐ๋๊ตฌ๋
process.env.NODE_ENV === 'development'
์ผ๋๋ง ๋ฒ๋ค๋ง ๋๋๊น ๋ฐ๋ก ๊ฑฑ์ ๋ง๋ผ๊ณ ~
- 'Floating Mode'๋ผ๊ณ ํด์ ํด๋ผ์ด์ธํธ ํ์ด์ง์ ๋์๋๊ณ ์ธ ์๋ ์์ด~
import { ReactQueryDevtools } from 'react-query/devtools'
function App() {
return (
<QueryClientProvider client={queryClient}>
{}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
- 'Embedded Mode'๋ก ํ๋ฉด ํ์ด์ง์ ์ ์ ์ปดํฌ๋ํธ๋ก ๊ฐ๋ฐ๋๊ตฌ๋ฅผ ๋ฃ์ ์๋ ์์ด
import { ReactQueryDevtoolsPanel } from 'react-query/devtools'
function App() {
return (
<QueryClientProvider client={queryClient}>
{}
<ReactQueryDevtoolsPanel style={styles} className={className} />
</QueryClientProvider>
)
}
๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๊ณผ์ ๋น๊ต (SWR, Apollo ๋ฑ)
- ์์ธํ ๊ฑด ์ง์ ๋ค์ด๊ฐ ๋ณด์ธ์~
- ๋งํฌ
ํ์
์คํฌ๋ฆฝํธ
- ์ฐ๋ฆฌ๊บผ ํ์
์คํฌ๋ฆฝํธ ์ง์ํฉ๋๋ค
- ๋ค๋ง ์ต์ํ 3.8 ๋ฒ์ ๋ณด๋ค ๋์์ผ ํฉ๋๋ค
- ๊ทธ๋ฆฌ๊ณ ๋ฆฌํด ๊ฐ์ ๋ํด์๋ ์ ์ ์ฉํ๋ ค๋ฉด 4.1 ๋ณด๋ค ๋์์ผ ํฉ๋๋ค
- ํ์
๋ณ๊ฒฝ์ patch๋ก ์ทจ๊ธ๋์ด ๊ณ์ ์๋์ผ๋ก ์
๋ฐ์ดํธ ๋ฉ๋๋ค. (Semver ๊ธฐ์ค)
- ๊ทธ๋์ ์ฐ๋ฆฌ๋ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ ํจํค์ง๋ฅผ ๊ณ ์ ๋ ๋ฒ์ ์ผ๋ก ์ฌ์ฉํ์๊ธฐ๋ฅผ ์ถ์ฒ๋๋ ค์
- ์ปค์คํ
ํ
๋ง๋ค ๋๋ ์๋์ฒ๋ผ..
function useGroups() {
return useQuery<Group[], Error>('groups', fetchGroups)
}
GraphQL
- ์ฌ์ค ์ฐ๋ฆฌ๋ ์ด๋ ํ ๋น๋๊ธฐ ๋ฐ์ดํฐ Fetching ํด๋ผ์ด์ธํธ์๋ ๊ฐ์ด ์ธ ์ ์์ด์!
- ๊ทธ๋ฐ๋ฐ ์ฃผ์ํ ์ ์ด ํ๋ ์์ด์
- ์ฐ๋ฆฌ๋ normalized caching์ ์ง์ํ์ง ์์์.
- ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์ ํ์๊ฐ ์๋ ๊ธฐ๋ฅ์ด์ง๋ง ๋ง์ฝ ๋น์ ์๊ฒ๋ ์ด ๊ธฐ๋ฅ์ด ๊ผญ ํ์ํ๋ค๋ฉด ์ฃผ์ํ์ธ์
React Native
- 3rd party Flipper plugin์ ์จ๋ณด์ธ์ ๋งํฌ
- ์จ๋ผ์ธ ์ํ ๊ด๋ฆฌ
import NetInfo from '@react-native-community/netinfo'
import { onlineManager } from 'react-query'
onlineManager.setEventListener(setOnline => {
return NetInfo.addEventListener(state => {
setOnline(state.isConnected)
})
})
- ์ฑ ํฌ์ปค์ค ๋ Refetch
import { focusManager } from 'react-query'
import useAppState from 'react-native-appstate-hook'
function onAppStateChange(status: AppStateStatus) {
if (Platform.OS !== 'web') {
focusManager.setFocused(status === 'active')
}
}
useAppState({
onChange: onAppStateChange,
})
- ์คํฌ๋ฆฐ ํฌ์ปค์ค ๋ ์๋ก๊ณ ์นจ
import React from 'react'
import { useFocusEffect } from '@react-navigation/native'
export function useRefreshOnFocus<T>(refetch: () => Promise<T>) {
const firstTimeRef = React.useRef(true)
useFocusEffect(
React.useCallback(() => {
if (firstTimeRef.current) {
firstTimeRef.current = false;
return;
}
refetch()
}, [refetch])
)
}