react에서 제공하는 ErrorBoundary가 존재한다
ErrorBoundary
생명주기 메서드 중 getDerivedStateFromError와 ComponentDidCatch를 정의하면 ErrorBoundary로 사용할 수 있다. ComponentDidCatch를 사용하여 에러 정보를 기록할 수 있다.
사용 예시
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트 합니다.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 폴백 UI를 커스텀하여 렌더링할 수 있습니다.
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
ErrorBoundary의 한계
위와 같은 사항을 포착하지 못한다.
해결 방법
그렇다. 위 ErrorBoundary의 한계에서 2번에 해당하는 내용이다. 비동기적 코드는 react-query, swr등의 비동기 통신 라이브러리를 사용해야 한다. 혹은, 직접 에러를 컴포넌트에서 동기적으로 던져주는 방법도 있다.
에러를 throw하는 방법을 공부해보기 위해, 직접 동기적으로 에러를 throw하는 로직을 작성해보자.
1. error를 동기적으로 직접 throw하자
function Children() {
const [todos, setTodos] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
(async () => {
try {
const res = await axios.get(
"https://jsonplaceholder.typicode.com/todos21231"
);
setTodos(res.data);
} catch (e) {
setError(e);
}
})();
}, []);
if (error) {
throw error;
}
return click;
}
데이터를 fetch하는 곳에서 throw를 하는 것이 아니라, 바깥에서 에러를 던지는 것이다. 데이터 fetch하는 함수는 비동기적으로 실행되기 때문에, ErrorBoundary의 try-catch문 바깥에 있어서 포착하지 못한다. 동기적으로 실행되는 곳에서 throw를 해주면 이 throw문은 ErrorBoundary에서 포착하여 에러 화면을 보여줄 수 있다.
2. useError로 error를 throw하는 로직을 추상화하자
그리고, 이러한 error에대한 로직을 커스텀 훅으로 만들었다.
export default function useError() {
const [error, setError] = useState<Error | unknown>();
useEffect(() => {
if (!error) return;
if (error instanceof Error) throw error;
else throw new Error("알 수 없는 에러가 발생했습니다.");
}, [error]);
return setError;
}
사용 방법은 다음과 같다.
function Children() {
const [todos, setTodos] = useState([]);
const setError = useError()
useEffect(() => {
(async () => {
try {
const res = await axios.get(
"https://jsonplaceholder.typicode.com/todos21231"
);
setTodos(res.data);
} catch (e) {
setError(e);
}
})();
}, []);
return click;
}
사용하는 측에서는 setError로 에러를 설정해주면, 에러를 어떻게 throw하는지 알지 않아도 된다. 그리고, 사용하는 측에서 error상태값 정보는 몰라도 되기에(이미 가지고 있기 때문) 넘겨주지 않는다.
useQuery를 직접 만들어보자 글에서 이어진다.