zod 에서 관리하고 처리한다
시작하기 앞서 옵셔넬 체이닝이 나쁜 이유 에 대한 게시물을 참고하기 바란다.

명확하게 에러와 로딩 상태를 했음에도 불구하고 여전히 타입스크립트는 우리에게 UserInfoSection 컴포넌트의 data는 undefined를 반환한다고 나와 있다
이 문제는 리액트쿼리에서 의도한바로 실제로 React.Suspense 와 ErrorBoundary 여부와 상관없이 useQuery 의 enabled 속성에 따라 data 를 가져오는 것이 가능하기 때문이다
리액트쿼리의 enabled 속성을 수정해도 여전히 리액트쿼리는 타입에 대한 반영을 명확하게 인지 못한다
useSuspenseQuery 를 제공합니다 참고해주세요가장 쉽게 해결하는 방법은 로딩일경우와 에러일경우 null을 반환하도록 하는 조건문이 들어가는 것이다
위 문서를 참고하여 아래와 같이 에러를 관리하여 data 타입에서 undefined 를 없애보자
function UserInfoSection() {
const { data, isLoading, isError } = useQuery<{ name: string }>({
queryKey: ["userInfo"],
queryFn: () =>
fetch("http://localhost:5173/api/user").then(async (response) => {
return await response.json();
}),
});
+ // 로딩일경우와 에러일경우 null을 반환한다.
+ if(isLoading || isError) return null;
return (
<div>
{data.name}
</div>
);
}
이제 타입스크립트는 data.name 이 undefined 일 가능성이 사라졌다고 알려준다
그러나 실무에서는 여전히 우리는 아래와 같은 문제점과 맞닥뜨린다
useQuery 와 리턴 조건문 사이에 특정 hook이 사용되고 있다면 이슈가 발생한다
const { data, isError, isLoading } = useQuery(...);
if (!data || isError || isLoading) return null;
// 이 로직은 동작하지 않고 에러가 나타남🤕
// hook 은 return 아래에 위치 할 수 없습니다.
// 우리는 undefined 가 아닌 data를 원합니다!
const [some, setSome] = useState(data);
우리는 isLoading과 isError를 각각의 ErrorBoundary 와 Suspnse 에서 처리되도록 만들었는데 타입하나때문에 겨우 타입스크립트 에러 하나 잡겠다고 저 로직을 추가한다는 건 말이 안되는 일이다
이때 zod 를 접했고 이를 이용해 type 과 value 에 대한 상태를 zod에게 일임하는 방법에 대해서 생각했다.
useGetUserInfo hook 을 만들어 의존성을 독립시키고 zod 를 이용하여 data 를 parse 하자
function useGetUserInfo() {
const { data } = useQuery<{ name: string }>({
queryKey: ["userInfo"],
queryFn: () =>
fetch("http://localhost:5173/api/user").then(async (response) => {
return await response.json();
}),
suspense: true,
useErrorBoundary: true
});
// safeParse와 다르게 parse는 error를 throw 한다
const parsedData = z.object({ name: z.string() }).parse(data);
return { data: parsedData };
}
function UserInfoSection() {
- const { data, isLoading, isError } = useQuery<{ name: string }>({
- queryKey: ["userInfo"],
- queryFn: () =>
- fetch("http://localhost:5173/api/user").then(async (response) => {
- return await response.json();
- }),
- });
- // 로딩일경우와 에러일경우 null을 반환한다.
- if(isLoading || isError) return null;
+ const { data } = useGetUserInfo();
return (
<div>
{data.name}
</div>
);
}
이제 드디어 UserInfoSection 컴포넌트는 아래의 목적을 달성 했다
useGetuserInfo hook에 일임 하게 되었다useGetuserInfo hook 에 일임하여 역할 분리가 가능해졌다UserInfoSection 컴포넌트는 자신의 이름대로 자신의 역할을 가진 기능을 담당하여 로직을 짤 수 있다