ErrorBoundary만으로는 부족하다: React Query 에러 재시도하기
onReset과 QueryErrorResetBoundary 연결로 재시도 구현하기
2026년 1월 10일
ErrorBoundary만으로는 부족하다: React Query 에러 재시도하기
저는 평소에 섹션별로 ErrorBoundary, Suspense를 이용해서 로딩과 에러처리를 하고 있었어요.
그런데 이렇게 섹션별로 에러처리를 하다 보니, 전체 화면에서 일부가 보이지 않는다고 해서 모든 부분을 새로고침해야 한다는 게 입력하던 것이 날아간다든지, 화면을 다시 렌더링하는 걸 기다려야 한다는 점에서 불편하게 느껴질 수 있다는 생각을 하게되었어요.

그래서 해당 섹션에 에러가 보였을 때, 그 부분만 다시 요청을 보낼 수 있는 다시 시도 버튼이 있으면 좋겠다는 생각이 들었습니다.
resetErrorBoundary만으로 안 되는 이유
react-error-boundary 라이브러리에는 resetErrorBoundary 함수가 존재해요.
버튼을 누르면 resetErrorBoundary가 호출되고, ErrorBoundary가 초기화되면서 자식 컴포넌트가 다시 마운트돼요. 이 방법으로 해결할 수 있을 거라 생각했지만, 실제로 API 요청이 다시 가지 않는 걸 확인할 수 있었어요.
ErrorBoundary 상태 vs React Query 캐시
| 위치 | 역할 | 초기화 방법 |
|---|---|---|
| ErrorBoundary | UI 상태 관리 | resetErrorBoundary() |
| React Query 캐시 | 데이터 상태 관리 | QueryErrorResetBoundary의 reset() |
resetErrorBoundary는 ErrorBoundary의 내부 상태만 초기화해요. 에러가 났다는 UI 상태를 지우고, 자식 컴포넌트를 다시 렌더링해요.
React Query는 자체 캐시에 "이 쿼리는 에러 상태야"라고 기록해둬요. 컴포넌트가 다시 마운트되어도, 캐시를 먼저 확인하고 "아, 이거 에러났던 거네" 하면서 서버 요청 없이 바로 에러를 던지게 돼요.
사용자가 '다시 시도' 클릭
- resetErrorBoundary() 호출
- ErrorBoundary 상태 초기화
- 컴포넌트 다시 마운트
- React Query: 이미 에러로 기록 되어있네?
- 서버 요청 안 함
- 즉시 에러 발생
- 다시 Error UI 표시
이렇게 되면, 그 섹션은 새로고침이 되었는데 데이터는 여전히 에러 상태로 남아있게 돼요.
QueryErrorResetBoundary로 캐시까지 초기화하기
React Query는 이 문제를 위해 QueryErrorResetBoundary를 제공해요.
QueryErrorResetBoundary의 reset과 ErrorBoundary의 onReset으로 이 문제를 해결할 수 있었어요.
ErrorBoundary는 리셋되기 직전에 onReset에 연결된 함수를 실행해요. 여기에 QueryErrorResetBoundary의 reset을 연결하면, 쿼리 캐시의 에러 상태를 먼저 초기화시키게 돼요.
사용자가 '다시 시도' 클릭
- resetErrorBoundary() 호출
- onReset 실행 → QueryErrorResetBoundary의 reset() 호출
- React Query 캐시의 에러 상태 초기화
- ErrorBoundary 상태 초기화
- 컴포넌트 다시 마운트
- React Query: "캐시가 깨끗 → 서버에 요청"
- 서버 요청 (Refetch)
- 성공하면 정상 UI, 실패하면 다시 에러 UI
이렇게 했을 때, 진짜 다시 시도를 클릭했을 때 새 요청을 보낼 수 있었습니다.
효율적인 재시도 전략 설계하기
문득 "무조건 요청을 다시 보내는 게 항상 정답일까?" 하는 고민이 들었어요. 불필요한 재시도는 사용자에게 희망 고문이 될 수도 있고, 서버에 무의미한 부하만 줄 수 있기 때문입니다. 그래서 사용자 경험과 서버 부하를 모두 고려해 '언제', '어떻게' 재시도를 허용할지 기준을 세워보았습니다.
React Query의 retry 옵션
가장 먼저 든 생각은 "재시도 버튼 없이 React Query의 retry 옵션만 잘 써도 되지 않을까?"였습니다.
retry: 2로 설정하면, 사용자가 "다시 시도" 버튼을 누르기 전에 이미 2번의 자동 재시도가 끝난 상태예요. 이렇게 생각하면 자동으로 여러 번 다시 시도를 하기에 '굳이 사용자가 에러 화면을 보고, 다시 시도를 눌러야 할까?' 생각이 들 수도 있어요.
그런데 저는 엘리베이터에서, 또는 사람이 많은 곳에서 네트워크가 잘 안 잡히는 경험을 한 적이 있어요. 이런 상황에서 앱 진입 → 요청 실패 → 자동 재시도 1회 실패 → 자동 재시도 2회 실패 플로우로 에러 화면이 뜨겠지만, 엘리베이터에서 내린 후 다시 시도해서 복구되면 이전에 보고 있던 스크롤 위치나 입력 상태가 남아있을 수 있어요. 어떤 상황에서는 사용자에게 더 좋은 UX를 제공할 수 있다고 판단했어요.
그래서 retry와 다시 시도 버튼을 함께 사용하기로 했어요. retry 옵션으로 에러 화면의 등장 빈도를 줄이고, 재시도 버튼은 그럼에도 에러가 떴을 때 사용자에게 행동의 주도권을 주는 역할을 해요.
재시도 불가능한 에러 분기
모든 에러가 재시도로 해결되는 건 아니에요. 404나 403 같은 에러는 재시도해봤자 같은 결과를 return할 수 있어요. 또한 재시도 가능 여부는 비즈니스 로직이라고 볼 수도 있을 것 같았어요. 어떤 에러가 재시도 가능한지는 섹션마다, 상황마다 다를 수 있어요.
그래서 아래와 같이 다시 시도가 가능한 경우에만 onRetry에 resetErrorBoundary를 넘겨주는 방법도 있을 것 같아요.
마치며
사용자 관점에서 생각하다 보니 에러처리를 하면서도 다양한 상황을 고려해볼 수 있었던 것 같아 좋았고, tanstack-query를 프로젝트에서 아직 잘 활용하지 못했던 것 같아 이런 다양한 부분을 더 공부해보면 좋을 것 같다는 생각이 들었습니다.