🔥 뮤테이션 응답을 통한 데이터 갱신

253자
3분

강의 목차

서버의 객체를 갱신하는 뮤테이션을 다룰 때, 새로운 객체가 뮤테이션 응답으로 자동 반환되는 경우가 많습니다. 이미 가지고 있는 데이터를 위해 불필요한 네트워크 요청을 하지 않고, 뮤테이션 함수가 반환한 객체를 활용해 기존 쿼리를 즉시 갱신할 수 있습니다. 이를 위해 Query Client의 setQueryData 메서드를 사용합니다.

typescript
const queryClient = useQueryClient()
 
const mutation = useMutation({
  mutationFn: editTodo,
  onSuccess: (data) => {
    queryClient.setQueryData(['todo', { id: 5 }], data)
  },
})
 
mutation.mutate({
  id: 5,
  name: '빨래하기',
})
 
// 아래 쿼리는 성공적인 뮤테이션의 응답으로 갱신됩니다
const { status, data, error } = useQuery({
  queryKey: ['todo', { id: 5 }],
  queryFn: fetchTodoById,
})
 
typescript
const queryClient = useQueryClient()
 
const mutation = useMutation({
  mutationFn: editTodo,
  onSuccess: (data) => {
    queryClient.setQueryData(['todo', { id: 5 }], data)
  },
})
 
mutation.mutate({
  id: 5,
  name: '빨래하기',
})
 
// 아래 쿼리는 성공적인 뮤테이션의 응답으로 갱신됩니다
const { status, data, error } = useQuery({
  queryKey: ['todo', { id: 5 }],
  queryFn: fetchTodoById,
})
 

재사용 가능한 뮤테이션에 onSuccess 로직을 연결하고 싶다면, 다음과 같이 커스텀 훅을 만들 수 있습니다:

typescript
const useMutateTodo = () => {
  const queryClient = useQueryClient()
 
  return useMutation({
    mutationFn: editTodo,
    // `mutate` 함수가 받는 변수 객체가 두 번째 인자임에 주목하세요
    onSuccess: (data, variables) => {
      queryClient.setQueryData(['todo', { id: variables.id }], data)
    },
  })
}
 
typescript
const useMutateTodo = () => {
  const queryClient = useQueryClient()
 
  return useMutation({
    mutationFn: editTodo,
    // `mutate` 함수가 받는 변수 객체가 두 번째 인자임에 주목하세요
    onSuccess: (data, variables) => {
      queryClient.setQueryData(['todo', { id: variables.id }], data)
    },
  })
}
 

불변성

setQueryData를 통한 갱신은 반드시 불변 방식으로 수행해야 합니다. 캐시에서 가져온 데이터를 직접 수정하여 캐시에 쓰려고 하지 마세요. 처음에는 동작할 수 있지만, 나중에 미묘한 버그를 유발할 수 있습니다.

typescript
queryClient.setQueryData(['posts', { id }], (oldData) => {
  if (oldData) {
    // ❌ 이렇게 하지 마세요
    oldData.title = '새 게시물 제목'
  }
  return oldData
})
 
queryClient.setQueryData(
  ['posts', { id }],
  // ✅ 이렇게 하세요
  (oldData) =>
    oldData
      ? {
          ...oldData,
          title: '새 게시물 제목',
        }
      : oldData,
)
 
typescript
queryClient.setQueryData(['posts', { id }], (oldData) => {
  if (oldData) {
    // ❌ 이렇게 하지 마세요
    oldData.title = '새 게시물 제목'
  }
  return oldData
})
 
queryClient.setQueryData(
  ['posts', { id }],
  // ✅ 이렇게 하세요
  (oldData) =>
    oldData
      ? {
          ...oldData,
          title: '새 게시물 제목',
        }
      : oldData,
)
 

이 방법을 통해 데이터의 일관성을 유지하고 예측 가능한 애플리케이션 상태를 보장할 수 있습니다. 불변성을 지키면 버그를 줄이고 코드의 가독성과 유지보수성을 높일 수 있습니다.

YouTube 영상

채널 보기
입력을 전처리하는 Functor - Contravariant와 contramap 이해하기 | 프로그래머를 위한 카테고리 이론
펑터 합성 | 프로그래머를 위한 카테고리 이론
Product와 Coproduct가 Bifunctor인 이유 | 프로그래머를 위한 카테고리 이론
바이펑터란? | 프로그래머를 위한 카테고리 이론
NestJS 파이프가 뭔가요? 컨트롤러를 보호하는 방법 | NestJS 가이드
NestJS 전역 에러 처리 | NestJS 가이드
변환 파이프로 컨트롤러 코드 깔끔하게 만들기 | NestJS 가이드
C++ 속의 펑터 | 프로그래머를 위한 카테고리 이론