2026.07.21
부모(index.tsx)
└─ const { data, refetch } = useQuery(...)
└─ <TableSection refetch={refetch} />
TableSection
└─ <Dialog refetch={refetch} />
Dialog
└─ mutation 성공 → refetch() 호출
1. 책임 분리 위반
mutation이 성공했을 때 "캐시를 갱신한다"는 건 mutation의 side effect다. 근데 그 책임을 컴포넌트가 들고 있었다. mutation hook 안에서 처리해야 할 일을 밖으로 꺼낸 것.
2. prop drilling
refetch가 index → TableSection → Dialog 3단계를 거쳐 전달됨. 중간 컴포넌트는 쓰지도 않으면서 받아서 넘기기만 함.
3. 단일 인스턴스만 갱신
refetch()는 그 컴포넌트에서 쓰고 있는 query 인스턴스 하나만 갱신한다. 만약 같은 queryKey를 다른 페이지나 컴포넌트에서도 쓰고 있으면 그쪽은 갱신이 안 된다.
4. 컴포넌트 간 결합도 증가
Dialog가 "누가 나를 렌더링했는지"를 알아야 했다. refetch가 어디서 오는지 알아야 하니까. 재사용이 어려워짐.
mutation hook
└─ onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [...] })
}
mutation hook을 어디서 호출하든 자동으로 캐시 무효화. 컴포넌트는 refetch를 알 필요가 없다.