
useMutation으로 데이터 추가하기useMutation은 새로운 데이터를 추가할 때 사용하는 훅으로, 여기서 mutation이란, 사이드 이펙트를 가진 함수를 의미한다.
데이터베이스에 새로운 값을 추가, 수정, 삭제하는 행위는 사이드 이펙트에 해당하며, 이렇게 사이드 이펙트가 발생하는 경우에 useMutation()이라는 훅을 사용하는 것이다.
useMutationo()과 useQuery()는 차이가 존재한다.
useQuery()의 쿼리 함수는 컴포넌트가 마운트되면서 자동으로 실행되지만, useMutation()은 실제로 mutation 하는 함수를 직접 실행해주어야 한다.
mutate() 함수를 통해 mutationFn으로 등록했던 함수를 실행할 수 있고, 그래야만 백엔드 데이터를 실제로 수정하게 된다.
참고로, mutate()를 하면 백엔드의 데이터는 변경이 되지만, 현재 캐시에 저장된 데이터는 refetch를 하지 않는 이상 기존의 데이터가 그대로 저장되어 있다.
따라서, refetch를 해줘야 변경된 데이터를 화면에 제대로 반영할 수 있다.
useMutation()을 이용해 포스트를 업로드하는 기능을 만들어보자.
우선은 다음과 같이 포스트 업로드를 요청하는 API 함수를 만들어보았다.
export async function uploadPost(newPost) {
const response = await fetch(`${BASE_URL}/posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newPost),
});
if (!response.ok) {
throw new Error('Failed to upload the post.');
}
return await response.json();
}
그리고, 다음과 같이, 간단한 form을 만들어주었다.
const [content, setContent] = useState('');
// ...
const handleInputChange = (e) => {
setContent(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
const newPost = { username: 'codeit', content };
// ...
};
return (
<>
<div>
<form onSubmit={handleSubmit}>
<textarea
name='content'
value={content}
onChange={handleInputChange}
/>
<button
disabled={!content}
type='submit'
>
업로드
</button>
</form>
</div>
<div>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.user.name}: {post.content}
</li>
))}
</ul>
</div>
</>
);
마지막으로, 다음과 같이 useMutation()을 작성하고, 업로드 버튼을 눌렀을 때 mutate() 함수를 실행하도록 하면 된다.
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
});
const handleSubmit = (e) => {
e.preventDefault();
const newPost = { username: 'codeit', content };
uploadPostMutation.mutate(newPost);
setContent('');
};
[전체 코드]
import { useState } from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { getPosts, uploadPost } from './api';
function HomePage() {
const [content, setContent] = useState('');
const {
data: postsData,
isPending,
isError,
} = useQuery({
queryKey: ['posts'],
queryFn: getPosts,
retry: 0,
});
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
});
const handleInputChange = (e) => {
setContent(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
const newPost = { username: 'codeit', content };
uploadPostMutation.mutate(newPost);
setContent('');
};
if (isPending) return '로딩 중입니다...';
if (isError) return '에러가 발생했습니다.';
const posts = postsData?.results ?? [];
return (
<>
<div>
<form onSubmit={handleSubmit}>
<textarea
name="content"
value={content}
onChange={handleInputChange}
/>
<button disabled={!content} type="submit">
업로드
</button>
</form>
</div>
<div>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.user.name}: {post.content}
</li>
))}
</ul>
</div>
</>
);
}
export default HomePage;
이렇게 코드를 작성하고 포스트를 업로드 해보면, 새로 등록한 포스트가 화면에 보이지 않는 것을 확인할 수 있는데,
이는 앞서 언급했듯이, mutate()를 한다고 해서 캐시에 있는 데이터가 변경되는 것이 아니기 때문이다.
데이터 변경을 확인하고자 한다면 새로고침을 통해 refetch를 해주어야 한다는 점을 잊지 말자.
그렇다면 업로드를 한 이후에 refetch를 자동으로 해주는 방법은 없을까?
이에 대해서는 다음 포스팅에서 다뤄볼 예정이다.