๐คทโ๏ธ ๋ณธ๋ก ์ ์์
react-query๋ React์์ ๋ง์ด ์ฌ์ฉ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ ๋ค์ํ ํ ๊ณผ ์ ํธ๋ฆฌํฐ๋ฅผ ์ ๊ณตํด ์ฃผ๋ฉฐ ๊ฐ๋ฐ์์ ํธ๋ฆฌ์ฑ๊ณผ ์์ฐ์ฑ์ ๋์ฌ์ค๋๋ค.
๋ณธ๋ก ์ ์์ ์๋์ ๊ธ๋ค์ ์ฝ๊ณ ์ค์๋ ๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค.
useMutation๋ํ ๊ธ์ ์ดํดํ๋๋ฐ ๋ง์ ๋์์ด ๋ฉ๋๋ค.
react-query ์ฌ์ฉ๋ฒ์ ๋ํด ์์๋ณด๊ธฐ
https://cometruedream.tistory.com/130
react-query ๋ฐ์ดํฐ ํ๋ฆฌํ์นญ ์ฌ์ฉ๋ฒ์ ๋ํด ์์๋ณด๊ธฐ
https://cometruedream.tistory.com/251
refetch ์ต์ ๊ณผ ์ฌ์ฉ๋ฒ ์ด ์ ๋ฆฌ
https://cometruedream.tistory.com/252
๐คทโ๏ธ useMutation ๊ฐ๋ ?
useMutation์ React Query์์ ์ ๊ณตํ๋ ํ ์ผ๋ก ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ์์ (HTTP ๋ฉ์๋: POST, PUT, PATCH, DELETE)์ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
useMutation์ ์๋ฒ ์ํ์ ํด๋ผ์ด์ธํธ ์ํ๋ฅผ ๋๊ธฐํํ๋๋ฐ ๋งค์ฐ ํจ์จ์ ์ด๋ฉฐ, ์ ์ฉํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
useMutation ์ ์ํ ๊ธฐ๋ฅ๋ค์ ํ์ฉํด์ ๋ฐ์ดํฐ์ ์ํ๊ด๋ฆฌ๋ฅผ ๋ณด๋ค ํจ์จ์ ์ผ๋ก ํ ์ ์์ต๋๋ค.
๋ฆฌ์กํธ ์ธ๊ธฐ ์๋ ์ด์ ?
๐คทโ๏ธ useMutation ์ฌ์ฉ๋ฒ?
Mutation์ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
- useMutation ํ ์ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๋น๋๊ธฐ ํจ์์ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค.
- ์์
import { useMutation } from 'react-query';
import axios from 'axios';
const createPost = async (newPost) => {
const response = await axios.post('/posts', newPost);
return response.data;
};
const MyComponent = () => {
const { mutate } = useMutation(createPost);
const handleCreatePost = () => {
mutate({ title: 'New Post', content: 'Hello World' });
};
return (
<div>
<button onClick={handleCreatePost}>Create Post</button>
{mutation.isLoading && <p>Creating post...</p>}
{mutation.isError && <p>Error creating post</p>}
{mutation.isSuccess && <p>Post created successfully!</p>}
</div>
);
};
์์ ์์ ์ฒ๋ผ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ useMutation ์ฌ์ฉ๋ฒ์ด๋ฉฐ HTTP ๋ฉ์๋ POST ์์ฒญ์ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
useMutation ํ ์ ๊ฐ์ฅ ์ค์ํ 4๊ฐ์ง์ ์ฝ๋ฐฑ ์ต์
- useMutation ํ ์ ‘onSuccess’, ‘onError’, ‘onSettled’์ ๊ฐ์ ๋ค์ํ ์ฝ๋ฐฑ ์ต์ ์ ์ ๊ณตํฉ๋๋ค.
- ์์
const mutation = useMutation(createPost, {
onSuccess: (data) => {
console.log('์๋ฒ์ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ:', data);
},
onError: (error) => {
console.error('์๋ฒ์ ํต์ ์ค๋ฅ:', error);
},
onSettled: () => {
console.log('์๋ฒ์ ํต์ ์ด ์คํจํ๋ ์ํ๋ ํธ์ถ');
},
});
์์ ์์ ์ฒ๋ผ useMutation ํ ์ 4๊ฐ์ง ์ฌ์ฉ๋ฐฉ๋ฒ์ ๋๋ค.
onMutate, onSuccess, onError, onSettled ์ต์ ์์๋ณด๊ธฐ
- onMutate
- useMutation ํ ์ด ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ์ง์ ์ ํธ์ถ๋ฉ๋๋ค.
- ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ์ ์ ์ฒ๋ฆฌํด์ผ ๋๋ ๋ก์ง์ onMutate์์ ์ฒ๋ฆฌํฉ๋๋ค. ์: ์๋ฒ์ ํต์ ์ง์ ๋ก๋ฉ ์ํ๋ฅผ ํ์ํ ๋ ๋ง์ด ์ฌ์ฉ๋ฉ๋๋ค.
- onSuccess
- ์๋ฒ์ ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๊ณ ์๋ฒ๊ฐ ์๋ต๊ฐ์ ๋๋ ค์คฌ์ ๋ ํธ์ถ๋ฉ๋๋ค.
- ์๋ฒ์ ์์ฒญ์ด ์ฑ๊ณตํ๊ณ ์ฒ๋ฆฌํด์ผ๋๋ ๋ก์ง์ onSuccess์์ ์ฒ๋ฆฌํฉ๋๋ค. ์: ์ฑ๊ณต ๋ฉ์์ง, ๋ค๋ฅธ ํ๋ฉด์ผ๋ก ์ด๋์ํฌ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
- onError
- ์๋ฒ์ ์์ฒญ์ด ์คํจํ์ ๋ ํธ์ถ๋ฉ๋๋ค.
- ์์ฒญ์ด ์คํจํ์ ๋ ์ฒ๋ฆฌํด์ผ ๋๋ ๋ก์ง์ onError์์ ์ฒ๋ฆฌํฉ๋๋ค. ์: ์๋ฒ์ ํต์ ์คํจ ์ ์๋ฌ๋ฉ์์ง, ์ฌ์ฉ์์ ๊ฐ์ด๋๋ฅผ ์์ฒญํ ์ ์์ต๋๋ค.
- onSettled
- ์๋ฒ์ ์์ฒญ์ด ๋๋ ํ ์ฑ๊ณตํ๋ ์คํจํ๋ ํญ์ ํธ์ถ๋ฉ๋๋ค. ํ์ ์๋ ๊ฒฝ์ฐ์๋ ํธ์ถ์ ์ ํด๋ ๋ฉ๋๋ค.
- ์์ฒญ์ด ๋๋๊ณ ํญ์ ์ฒ๋ฆฌํด์ผ ๋๋ ๋ก์ง์ onSettled์์ ์ฒ๋ฆฌํฉ๋๋ค. ์: ์๋ฒ์ ํต์ ์ด ์๋ฃ ํ ๋ก๋ฉ๋ฐ๋ฅผ ํด์ฒดํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์์ 4๊ฐ์ง ์ต์ ์ ์ดํดํ ์ ์๋ ์์
import { useMutation } from '@tanstack/react-query';
const sendText = async (message) => {
// ์๋ฒ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ํจ์
};
const useSendText = () => {
const { mutate: send } = useMutation({
mutationFn: sendText,
onMutate: () => {
// ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ธฐ ์ง์ ์ ํธ์ถ
console.log('๋ฌธ์๋ฅผ ๋ณด๋ด๋ ์ค...');
},
onSuccess: () => {
// ๋ฉ์์ง๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๋ณด๋ธ ํ ํธ์ถ
console.log('๋ฌธ์๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๋ณด๋์ด์!');
},
onError: (error) => {
// ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ํธ์ถ
console.log('๋ฌธ์ ๋ณด๋ด๊ธฐ์ ์คํจํ์ด์. ๋ค์ ์๋ํด ์ฃผ์ธ์.');
},
onSettled: () => {
// ๋ฉ์์ง ๋ณด๋ด๊ธฐ๊ฐ ๋๋๋ฉด ํญ์ ํธ์ถ (์ฑ๊ณต์ด๋ ์คํจ๋ )
console.log('๋ฌธ์ ๋ณด๋ด๊ธฐ ์์
์ด ๋๋ฌ์ด์.');
},
});
return send;
};
useMutation ์ค์ ์์
import { useMutation, useQueryClient } from 'react-query';
import axios from 'axios';
const deletePost = async (postId) => {
await axios.delete(`/posts/${postId}`);
};
const MyComponent = () => {
const queryClient = useQueryClient();
const { mutate: send } = useMutation(deletePost, {
onSuccess: () => {
queryClient.invalidateQueries('posts');
},
});
const handleDelete = (postId) => {
send(postId);
};
return (
<div>
<button onClick={() => handleDelete(1)}>Delete Post</button>
{mutation.isLoading && <p>Deleting post...</p>}
{mutation.isError && <p>Error deleting post</p>}
{mutation.isSuccess && <p>Post deleted successfully!</p>}
</div>
);
};
์์ ์์ ์ฒ๋ผ ์ค์ ๋ง์ด ์ฌ์ฉ๋๋ useMutation ๋ก์ง์ ๋๋ค.
์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ํต์ ํ onSuccess ํธ์ถํ๊ณ ๋ณ๊ฒฝํ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ๋ react-query ์ ๊ณตํ๋ invalidateQueries ๋ฉ์๋๋ฅผ ์คํํ์ต๋๋ค.
queryClient.invalidateQueries ๋ฌด์์ธ๊ฐ?
queryClient.invalidateQueries ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ์ฟผ๋ฆฌ์ status๋ฅผ ์ฆ์ stale๋ก ๋ณ๊ฒฝํ์ฌ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋จ์์ refetch๊ฐ ๋ฐ์ํ์ฌ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค.
useMutation ํ ์ค์์ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ํต์ ํ์ ๋ง์ด ์ฌ์ฉ๋๋ ๋ฉ์๋์ ๋๋ค.
๐คทโ๏ธ ๋ง์ง๋ง์ผ๋ก
useMutation์ ๊ฐ๋ ๊ณผ ํ์ฉํ๋๋ฐ ๊ฐ์ฅ ์ค์ํ 4 ์๊ธฐ์ ์ฝ๋ฐฑํจ์๋ฅผ ์์๋ณด์์ต๋๋ค.
๊ฐ ์ฝ๋ฐฑ ํจ์๋ ์๋ฒ ์์ฒญ์ ๋ค๋ฅธ ๋จ๊ณ์์ ํธ์ถ๋๋ฉฐ ๊ฐ๊ฐ์ ์ฝ๋ฐฑ ํจ์๋ฅผ ํตํด์ ํจ์จ์ ์ผ๋ก ์๋น์ค ๋ก์ง์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
useMutation ํ ์ค๋ ์๋ฒ์ ํด๋ผ์ด์ธํธ์ ์ํ๋ฅผ ๋ค๋ฃจ๋๋ฐ ๋งค์ฐ ํจ์จ์ ์ด๋ฉฐ ๋ค์ํ ์ต์ ๋ค์ ํ์ฉํด์ ์ฌ์ฉ์์๊ฒ ์ข์ UI/UX ๊ฒฝํ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
์ฌ๋ฐ๋ ์ค๋์ ๋ ๋ณ ์ด์ธ ๋ณด๊ณ ๊ฐ์ธ์!
https://fnfentermagazine.com/%ec%8a%a4%ec%bd%94%ed%94%84%ec%9d%98-%ea%b0%9c%eb%85%90/
https://cometruedream.tistory.com/247
https://cometruedream.tistory.com/243
https://cometruedream.tistory.com/242
https://cometruedream.tistory.com/241
https://cometruedream.tistory.com/244
'React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฆฌ์กํธ ์ ์ด vs ๋น์ ์ด ์ปดํฌ๋ํธ ์์๋ณด๊ธฐ (0) | 2024.08.02 |
---|---|
๋ฆฌ์กํธ useLayoutEffect ๊ฐ๋ ๋ฐ ์ฌ์ฉ๋ฒ (36) | 2024.07.31 |
[React-query] ๋ฐ์ดํฐ ํ๋ฆฌํ์นญ ์ฌ์ฉ๋ฒ ์ ๋ฆฌ (1) | 2024.06.19 |
[React] Tailwind ํ์ค, ๋์ค, ์ธ์ค ํจ๊ณผ className ์ปค์คํ (1) | 2024.06.03 |
Next JS์์ tailwind ์ ์ฉ ์๋ ๋ ํด๊ฒฐ๋ฐฉ๋ฒ (1) | 2024.05.17 |