이벤트 핸들러 시점 에러 처리
버튼 클릭 에러 처리와 로딩 상태 관리
목차
- 에러 처리는 왜 필요한가?
- 에러 피드백 UX 설계
- 에러 처리 원칙 세우기
- Client Side 렌더링 에러 처리
- Server Side 렌더링 에러 처리
- 이벤트 핸들러 시점 에러 처리 ← 현재 문서
- 전역/공통/개별 에러 처리
문제 정의
버튼 클릭과 같은 사용자 상호작용 중에 API 오류 등의 에러가 발생할 수 있습니다.
이때, 사용자에게 상황에 맞는 적절한 안내를 제공해야 합니다.
해결 전략
이벤트 핸들러 내부의 에러를 잡을 수 있는 유일한 방법은 try-catch 구문입니다.
기술적인 방법은 이미 정해져 있으므로, 기획 의도에 맞춰 어떤 UI로 피드백을 줄 것인지만 선택하면 됩니다.
- 모달: 중요한 에러나 사용자 확인이 필수적인 경우
- 토스트: 흐름을 방해하지 않는 가벼운 알림이 필요한 경우
- 인풋 메시지: 특정 입력 필드의 오류를 알릴 경우
구현 방법
기본 구조
function Page() {
const onClick = async () => {
try {
const response = await postProductApi(formData);
} catch (error) {
if (error instanceof UnauthorizedError) {
openModal({
title: '권한 없음',
content: '상품 등록 권한이 없습니다. 관리자에게 문의하세요.'
});
} else {
openModal({
title: '오류 발생',
content: '잠시 후 다시 시도해 주세요. 문제가 지속되면 고객센터에 문의하세요.'
});
}
}
};
return (
<button onClick={onClick}>상품 등록</button>
);
}하지만, 500 에러처럼, API마다 공통적으로 발생할 수 있는 오류가 존재합니다.
그래서 저런 코드를 이벤트 핸들러 마다 작성해야 하는 중복 코드 문제가 발생합니다.
이것은 Step 7. 전역/공통/개별 에러 처리에서 다룹니다.
관련 팁: 버튼 로딩 상태 처리
이벤트 핸들러에서 API를 호출할 때, 에러 처리와 함께 고려해야 할 것이 버튼의 로딩 상태입니다.
API 호출 중에 버튼이 활성 상태로 남아 있으면, 사용자가 중복 클릭할 수 있습니다. 그래서 useMutation의 isPending을 버튼의 loading props에 전달합니다.
function Page() {
const { mutateAsync, isPending } = useMutation(/* ... */);
const onClick = async () => {
try {
await mutateAsync(formData);
} catch (error) {
// 에러 처리
}
};
return (
<Button loading={isPending} onClick={onClick}>상품 등록</Button>
);
}그런데, API 성공 후 페이지 이동이나 모달 닫힘이 이어지는 경우에는 isPending만으로 부족합니다.
isPending은 API 응답을 받는 순간 false로 바뀌지만, 페이지 이동이나 모달이 닫히기까지는 짧은 시간이 걸립니다. 그 사이에 로딩이 풀리면서 버튼이 다시 활성화되는 현상이 발생합니다.
이를 해결하기 위해, 컴포넌트가 언마운트될 때까지 로딩을 유지하도록 isSuccess를 함께 전달합니다.
function Modal() {
const { mutateAsync, isPending, isSuccess } = useMutation(/* ... */);
const onConfirm = async () => {
try {
await mutateAsync(formData);
closeModal();
} catch (error) {
// 에러 처리
}
};
return (
<Button loading={isPending || isSuccess} onClick={onConfirm}>확인</Button>
);
}isPending: API 호출 중 → 로딩isSuccess: API 성공 후 ~ 컴포넌트 언마운트 전 → 로딩 유지
다음 단계
모든 곳에서 에러를 잡았지만, 코드가 중복되고 있습니다. 이걸 어떻게 깔끔하게 정리할까요?