프론트엔드 에러 처리 - (1) Error Bounday

6mn12j·2022년 9월 13일
1

앞단!

목록 보기
2/2

개발을 진행하다보면 에러를 마주하게 된다. 나의 경우에는 프로젝트를 진행하면서 비동기적으로 API 를 받아오고 데이터를 처리하면서 비동기API에 대한 에러 처리에 대한 고민을 하게 됐다.

나의 상황은 ReactQuery를 이용하여 server state를 관리하고 있었고 비동기 통신 라이브러리로는 Axios를 이용하고 있었다.

ReactQuery의 useQuery 의 onError 옵션 마다 에러를 처리하지 않고, 전역적인 에러 처리에대해서 의문점이 생겼다.

이러한 고민을 함께 스터디 하고 있는 사람들에게 얘기 했을때 이야기 나왔던 ErrorBoundarySuspense 를 이용하여 처리할 수 있을거라고 얘기를 해주었고 일단 그 에대한 개념에 대해서 찾아봤다.

Error Boundary (에러 경계)

  • 선언적 으로 에러 처리 하기위한 컴포넌트

Error Boundary(에러 경계)란 하위 컴포넌트의 에러를 잡아내서 우리의 부서진 앱 대신 지정된 폴백 UI를 보여주는 리액트의 컴포넌트.

React16 이전 버전에서 핸들링 되지 않던 에러들이 발생하여 원인을 찾는데 많은 어려움을 겪었고, 선언적 으로 에러 핸들링을 위한 대안으로 React16에서 도입 됐다.

에러가 발생하면 전체 리액트 컴포넌트를 unmounting 시킨다. 별도의 구분이 필요하다면 각 컴포넌트마다 별도의 에러경계 컴포넌트를 래핑 하여 나머지 구성요소가 유지되게 해줘야 한다.

  • catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI
  • As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree.

ErrorBoundary 컴포넌트

  • static getDerivedStateFromError(error)
    • error : 발생한 오류
  • componentDidCatch(error, info)
    • error : 발생한 오류.
    • info : 어떤 컴포넌트가 오류를 발생 시켰는지에 대한 객체.

class 컴포넌트에 getDerivedStateFromError() 메서드 또는 componentDidCatch() 메서드가 정의 되어 있는경우(혹은 둘 다)에 ErrorBoundary컴포넌트로 판단 된다.

  • 에러 바운더리는 컴포넌트에서의 catch { }

그렇다면 왜 try/catch 가 아니라 ErrorBoundary 를 사용할까?

  • React의 특징 중 하나인 선언적인 특성을 유지하기 위해서.
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

//사용
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

두 메서드는 하위의 컴포넌트에서 오류가 발생되면 호출된다.
getDerivedStateFromError 는 오류가 발생한 후 다음 렌더에서 풀백 UI를 렌더링하고, componentDidCatch 는 오류 정보를 기록한다.

에러 경계의 한계

  • 이벤트 핸들러
  • 비동기적 코드
  • 서버 사이드 렌더링
  • 에러 경계 자체에서 발생하는 에러

이벤트 핸들러는 컴포넌트의 렌더링 단계와는 연관이 없다. 따라서 에러바운더리가 아닌, try/catch 구문을 이용하여 처리한다.

const MyComponent = () => {
	const [error,setError] = useState(null);
	
	const handleClick = () => {
			try {
	      // Do something that could throw
			} catch (error) {
					 setState({ error });
		  }
	}
	if (error !== null)
	return <h1>Caught an error </h1>

	return <button onClick={handleClick}>Click Me</button>

}

비동기처리는 API 응답을 기다렸다가 오류가 발생했을때 컴포넌트 단계에서 에러를 throw 시켜주면 ErrorBoundary를 이용하여 에러를 잡아낼 수 있다.

컴포넌트 추적

Create React App 를 이용하여 프로젝트를 만들었다면 별도의 필요없이 에러가 발생한 파일이름, 라인수를 console 을통해 알 수 있다.

Create React App앱을이용 하지 않았다면 별도의 플러그인, 바벨 설정이 필요하다.

class 말고 functional은?

공식문서에서는 생명주기의 getDerivedStateFromError componentDidCatch 메서드를 사용한다고 나와 있는걸 보니 class 말고 functional 에서는 사용하지 못하는 건가? 라는 생각으로 검색을 해보니

참고자료

https://reactjs.org/docs/error-boundaries.html
https://meticulous.ai/blog/react-error-boundaries-complete-guide/
https://usage.tistory.com/140

profile
TIL 쩨끼럽 남기는 중 💻

0개의 댓글