useMutation 옵션 순서 중요한거 아시나요?

민경찬·2025년 5월 31일
3

프론트엔드

목록 보기
2/2
post-thumbnail

안녕하세요. 백엔드 개발자 민경찬입니다.
오늘은 Tanstack/query 라이브러리에서 제공하는 useMutation 사용 주의 사항에 대해서 소개해볼까합니다.


💡 useMutation이란?

Tanstack/query 라이브러리에서 제공하는 기술로, 서버에 데이터를 생성, 업데이트, 삭제 등의 비동기 요청을 수행할 때 사용하는 훅입니다.

const { mutate } = useMutation({
  mutationFn: async (someVal: string) => {},
  onMutate: (someData) => someData,
  onError: (_error, _variables, context) => {},
  onSettled: (_data, _error, _variables, context) => {},
});

useMutation 은 정말 놀라운 Typescript 호환성을 보여줍니다.
mutationFnonMutate 가 어떤 값을 리턴하냐에 따라 onErroronSettled 의 파라미터 타입이 자동으로 추적됩니다.

정말 훌륭한 DX입니다.

😧 프로퍼티에도 순서가 있다?

하지만 Typescript를 극한으로 활용한 만큼, 일부 부작용이 있습니다.

예를 들어볼게요.

아래 처럼 onMutate{ data: string } 타입을 리턴한다면 context 는 자동으로 그 타입으로 추적됩니다.

const { mutate } = useMutation({
  mutationFn: async (someVal: string) => {},
  onMutate: (someData) => ({ data: "some data" }), // { data: string }
  onError: (_error, _variables, context) => {
	  context?.data; // data가 잡힘
  },
  onSettled: (_data, _error, _variables, context) => {},
});

그러나!

onMutateonError 보다 아래에 쓰게 된다면 어떻게 될까요?

const { mutate } = useMutation({
  mutationFn: async (someVal: string) => {},
  onError: (_error, _variables, context) => {
	  context?.data; // Error: Property 'data' does not exist on type '{}'.
  },
  onMutate: (someData) => ({ data: "some data" }), // { data: string }
  onSettled: (_data, _error, _variables, context) => {},
});

에러를 마주하게 됩니다. 이때 context 는 더 이상 { data: string } 타입이 아닌, unknown 이기 때문입니다.

이는 Typescript 구현 자체가 이런 방식으로 되어있기에 어쩔 수가 없습니다.

관련 내용: https://github.com/microsoft/TypeScript/issues/53018#issuecomment-1518558545

😵 이런 상황에서도 높은 DX를 유지하는 방법

한 번 잘못 마주하면 정말 오랜 시간을 낭비하게 될 겁니다.

타입이 unknown 으로 잡히는데 아 Typescript 특성 때문에 타입이 uknown으로 추적되니 onMutate를 위로 올려야겠구나! 라고 생각할 수 있을까요.

저는 힘들 것 같습니다.

하지만 이런 문제는 비단 useMutation 만의 문제가 아닙니다. useInfiniteQuery 에서도 같은 문제가 있죠.

@Tanstack/query 에서는 이런 문제를 진작 인지하고 있었고 이를 방지하기 위하여 ES-Lint 플러그인을 이용하고 있습니다.

ES-Lint 의 기능을 활용하여 속성의 순서가 어긋나면 빨간줄을 띄워주는 것이죠!

https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query

🤔 그러나 useMutation은 없네요?

useInfiniteQuery 는 ES-Lint 규칙이 존재하지만 useMutation 은 존재하지 않습니다.

@Tanstack/query 이슈에도 올라와있어요.

https://github.com/TanStack/query/issues/8988

그래서 저는 기여를 도전해보기로 했어요.

👍 첫 번째 PR

이미 property order를 확인하는 ES-Lint 플러그인이 라이브러리 내부에 구현되어 있었기에 따라 구현하는 것은 어렵지 않았어요.

테스트 코드도 금방 작성할 수 있었습니다.

https://github.com/TanStack/query/pull/9186

그러나, infinite-query-property-order 와 로직이 너무 겹친다는 피드백을 받았습니다.

👍 두 번째 PR

이 두 개의 로직을 하나로 합쳐 유틸 함수로 만들었고 빠르게 다시 PR을 올려봤습니다.

https://github.com/TanStack/query/pull/9191

올린지 얼마 되지 않고 머지가 되더라구요.


결론!

ES-Lint 플러그인이 원하는대로 작동하는지에 관련한 테스트도 신기하더라구요.
제가 알던 Jest 방식의 테스트와 달라 인상이 깊었습니다.

@Tanstack/query 가 어떻게든 DX를 챙기려는 것도 매우 인상이 깊었어요.
ES-Lint 플러그인 잘 세팅해서 개발해야겠다는 생각도 들더라구요.

여러분들도 ES-Lint 세팅하고 개발하세요. :)

도움주신 인제님께 다시 한 번 감사 인사드립니다!

1개의 댓글

comment-user-thumbnail
2025년 6월 27일

👏👏👏👏👏

답글 달기