안녕하세요 ? YB 김가현입니다
4주차 공유과제로는 Error Boundary로 에러 핸들링하기에 대해 알아보려고 합니다 !
그간 에러 처리에 대해 깊게 고민해본 경험이 없었는데요,
- try-catch로 API 통신 에러 잡아내기
- react-query onError
보통 이 두가지 방법 중 하나를 사용했던 것 같습니다.
4주차 과제를 하면서 try-catch 블록을 사용해 catch에서 발생한 에러를 단순히 throw 하여 해당 함수를 가져다 쓰는 컴포넌트(혹은 페이지) 에서 각 케이스를 처리하는 방식으로 구현하였는데요 ..
이게 맞는 방식인지 궁금증이 생겨 검색해보며 프론트엔드에서 에러 핸들링 하는 법을 다룬 다양한 아티클을 읽어보았는데, Error Boundary 라는 것이 있더라고요 !
어떤 녀석인지 한 번 알아보도록 하겠습니다 👀
에러 핸들링의 중요성
에러 핸들링이 왜 중요한 이유는 안정적인 서비스를 만들고 사용자 경험을 높이기 위함이 아닐까 생각합니다.
정말 다양한 이유로 에러가 발생할 수 있기에 이를 적절히 처리하여 정제된 화면을 사용자에게 보여주는 것이 곧 서비스의 신뢰도와도 연결되기 때문입니다.
날것 그대로의 에러를 보여주기 보다는 … 잘 다듬고 깎고 보살피고 처리해서 깔끔한 UI와 명확한 안내 메시지를 제공하는게 좋겠죠 ?
이러한 에러 핸들링 방법에는 여러가지가 있습니다.
- try-catch문
- promise catch문
- react-query onError
- axios interceptor
- error boundary
그럼 지금부터 error boundary에 대해 알아보도록 하겠습니다.
ErrorBoundary
에러 경계(Error Boundaries) – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
ErrorBoundary 는 하위 컴포넌트에서 발생하는 자바스크립트 에러를 잡아서 fallback UI를 보여주는 React 컴포넌트입니다. 하지만 하위 컴포넌트에서 발생하는 모든 에러를 잡아주는 것은 아니에요 !
공식문서에서는 아래 네 가지의 경우는 ErrorBoundary가 포착할 수 없다고 설명하고 있습니다.
그 이유가 궁금하신 분은 ,,, 아래 글을 읽어보시는 것을 추천드립니다.
각 케이스 별로 왜 ErrorBoundary가 해당 에러를 포착할 수 없는지를 잘 설명해주고 있어요.
ErrorBoundary 가 포착할 수 없는 에러와 그 이론적 원리 분석
서언 ErrorBoundary 는 하위 컴포넌트에서 발생하는 자바스크립트 에러를 잡아서 fallback UI를 보여주는 React 컴포넌트 입니다. 하지만 하위에 존재하는 컴포넌트에서 에러가 발생한다고 하여, 모든
happysisyphe.tistory.com
Error Boundary를 사용하려면 getDerivedStateFromError() 또는 componentDidCatch() 생명주기 메서드를 포함한 클래스 컴포넌트를 정의해야 합니다. 아래는 예시 코드입니다.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 다음 렌더링에서 fallback UI가 보이도록 상태를 업데이트한다.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
// fallback UI를 커스텀하여 렌더링 할 수 있다.
return <h1>문제가 발생했습니다.</h1>;
}
return this.props.children;
}
}
// 컴포넌트에서 사용 예시
class App extends React.Component {
render() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
}
위 예시에서 ErrorBoundary는 자식 컴포넌트 트리에서 발생하는 자바스크립트 오류를 잡아내고, 해당 오류를 콘솔에 기록한 후 ‘문제가 발생했습니다’ 라는 메시지를 렌더링합니다. 생명주기 메서드인 static getDerivedStateFromError() 와 componentDidCatch()를 사용하면 클래스 컴포넌트 자체가 에러 바운더리가 되는데, static getDerivedStateFromError()를 사용하여 에러가 발생했을 때 hasError의 값을 true로 변경하면 폴백 UI를 렌더링할 수 있습니다. componentDidCatch() 를 통해 에러 정보를 기록할 수도 있습니다.
만약 MyComponent에서 오류가 발생하면, ErrorBoundary가 이를 잡아내어 콘솔에 기록하고, MyComponent 대신 오류 메시지를 렌더링하겠죠 ?
여기까지 공식문서의 가이드 내용입니다.
왜 클래스형 컴포넌트인가 ? 라고 한다면 아직 함수형 컴포넌트에서는 이러한 생명주기와 똑같은 기능이 아직 없어 ErrorBoundary 구현에는 클래스형 컴포넌트를 사용하고 있다고 해요.
그래서 … react-error-boundary 라는 라이브러리를 활용하여 Error Boundary를 설정하는 방식을 선택하곤 합니다 🫵
react-error-boundary
react-error-boundary 라이브러리는 ErrorBoundary라는 컴포넌트를 제공합니다. 이 컴포넌트에는 fallback이라는 prop이 있는데, 오류가 감지되었을 때 표시할 함수 또는 React 요소를 전달 받습니다. 아래와 같이 사용할 수 있습니다.
yarn add react-error-boundary
import { ErrorBoundary } from 'react-error-boundary'
const FallbackComponent = () => {
return <div>에러 발생 .. 어쩌구저쩌구 ........</div>
}
export const App = () => {
return (
<ErrorBoundary fallback={</FallbackComponent>}>
<Component />
</ErrorBoundary>
)
}
fallback 외에도 몇 가지 prop이 더 있는데요,
type ErrorBoundarySharedProps = PropsWithChildren<{
onError?: (error: Error, info: ErrorInfo) => void;
onReset?: (
details:
| { reason: "imperative-api"; args: any[] }
| { reason: "keys"; prev: any[] | undefined; next: any[] | undefined }
) => void;
resetKeys?: any[];
}>;
onReset prop : 오류가 발생하기 전에 발생한 부작용을 정리하는 데 사용
resetKeys prop : 컴포넌트 상태를 언제 재설정할지 제어하는 데 사용
onError prop : 오류가 감지될 때마다 호출 → 오류를 logging 하는 데 사용 가능
이렇게 정리할 수 있을 것 같습니다.
react-error-boundary의 useErrorBoundary 훅
이 훅은 에러 바운더리를 쉽게 표시하고 해제할 수 있게 해주는 커스텀 훅입니다. 이 훅을 이용해 함수형 컴포넌트의 내부에서 에러를 핸들링 할 수 있는데, 비동기 처리를 해야 할 때 유용하게 사용될 수 있습니다.
import React from 'react';
import { useErrorBoundary } from "react-error-boundary";
const App = () => {
const { handleError } = useErrorBoundary();
const fetchData = async () => {
try {
const result = await fetch("https://abc.def");
} catch (error) {
handleError(error);
}
};
return (
<div>
<button onClick={fetchData}>click !</button>
</div>
);
};
export default App;
'4주차' 카테고리의 다른 글
한 번 설정해두면 개발이 편해지는,,, Axios Interceptor (0) | 2024.11.12 |
---|---|
[React] 타입 단언은 대체 (0) | 2024.11.12 |
개발 어떤 방식으로 하세요? _ CDD : 컴포넌트 주도개발 (0) | 2024.11.12 |
React Router에서 Outlet과 Layout을 활용하는 방법 (0) | 2024.11.12 |
Protected Route로 라우트 보호하기 (0) | 2024.11.12 |