invalidateQueries를 통해 캐싱 무효화를 해주었음에도 불구하고 화면이 바로 바뀌지 않는 문제가 발생했다.
처음 코드
const TodoContainer = () => { const { data, isPending } = getTodoApi(); if (isPending) { return <div>loading...</div>; } return ( <main className="flex h-screen w-full flex-col justify-center"> <div className="mx-auto flex w-[600px] flex-col gap-5"> <div className="flex justify-end"> <ServerInputModal /> </div> {data ? ( <> {data.map(({ content, completed, id }) => ( <TodoItem checked={completed} todo={content} todoId={id} key={`${content}-${id}`} /> ))} </> ) : ( <AlertBox /> )} </div> </main> ); };
잘못 알고 있었던 내용
<ServerInputModal /> 에서 정상 base url을 입력했을 때는 data가 불러와지므로 TodoComponent가 반환되고 잘못된 url을 입력하면 에러 컴포넌트로 바뀔 것을 예상했다.
<ServerInputModal />에서 invalidateQueries를 통해 캐싱 무효화를 해주었기 때문에 다시 데이터를 받아오기 위해 쿼리가 재실행되고
재요청에 실패한 경우 데이터가 없기 때문에 data가 없을 때 반환되는 AlertBox가 렌더링 된다고 생각했다.
코드 결과 및 의문
그런데 <ServerInputModal /> 에서 정상 base url을 입력했을 때는 get 요청이 새로 되어 data의 내용을 기반으로 한 TodoComponent가 반환됐지만
<ServerInputModal />에서 잘못된 url을 입력하면 에러 컴포넌트로 바뀌지 않았다.
2가지 의문
-
isError 상태일 때 데이터가 갱신되지 않는다. : data가 undefined 상태일텐데 왜
<AlertBox />가 반환되지 않는가? -
왜 pending 상태를 건너뛰고 error를 반환할까?
원인과 문제 해결
일단 컴포넌트가 렌더링되지 않았던 이유는 isError 상태를 반영해주지 않아서였다. 요청이 실패하면 isError가 true로 설정되지만 이를 UI에서 처리하지 않으면 에러 상황이 사용자에게 전달되지 않는다고 한다.
다음과 isError 상태를 반영해주니 정상적으로 작동되었다.
const TodoContainer = () => { const { data, isError, isPending } = getTodoApi(); if (isPending) { return <div>loading...</div>; } return ( <main className="flex h-screen w-full flex-col justify-center"> <div className="mx-auto flex w-[600px] flex-col gap-5"> <div className="flex justify-end"> <ServerInputModal /> </div> {!isError && data && ( <> {data.map(({ content, completed, id }) => ( <TodoItem checked={completed} todo={content} todoId={id} key={`${content}-${id}`} /> ))} </> )} {isError && <AlertBox />} </div> </main> ); }; export default TodoContainer;
아까 언급한 2가지 문제에 대해서도 자세히 알아보자
- isError 상태일 때 데이터가 갱신되지 않는다.
: data가 undefined 상태일텐데 왜<AlertBox />가 반환되지 않는가?- pending 상태를 건너뛰고 error를 반환한다.
data가 undefined 상태일텐데 <AlertBox /> 가 반환되지 않는 이유
이전 캐시된 데이터의 존재
캐시된 데이터가 존재하고, 잘못된 url로 인해 새로운 데이터를 가져오는 요청이 실패하면, 리액트 쿼리는 이전에 성공적으로 가져온 캐시된 데이터를 계속 사용한다. 이때 data는 무효화되었지만, 캐시된 데이터가 남아 있으므로 화면이 유지되는 것이다.
invalidateQueries를 사용하면 해당 쿼리의 데이터가 "stale"로 표시되어 더 이상 최신 상태로 간주되지 않지만, 즉시 데이터가 삭제되거나 사라지는 것이 아니다!
pending 상태를 건너뛰고 error를 반환하는 이유
쿼리를 무효화하고 데이터 요청을 시도하면 다시 pending 상태가 되는 줄 알았는데 아니었다!!
애플리케이션이 쿼리를 처음 실행할 때만 isPending이 true로 설정된다. 페이지를 새로고침하거나 처음으로 해당 쿼리를 사용하는 컴포넌트가 렌더링될 때 발생한다.
invalidateQueries를 사용해 쿼리를 무효화하고 재요청을 하는 경우는 데이터를 갱신하는 중이기 때문에 isPending이 아니라 isFetching을 사용해야 한다!
isFetching 상태를 사용해주니 loading 화면이 정상적으로 작동되었다.
const { data, isError, isPending, isFetching } = getTodoApi(); if (isPending) { return <div>loading...</div>; } if (isFetching) { return <div>loading...</div>; }