에러경계 Error Boundary : 하위 컴포넌트에서 발생하는 자바스크립트 에러를 잡아서 fallback UI 를 보여주는 공식 리액트 컴포넌트.
선언적으로 에러를 처리하는 방법. 공식 리액트가 제공한다.
1. Error Boundary가 캐치할 수 없는 코드 를 있는 코드로 변경해보기
2. 브라우저의 구조를 뜯어보아 Error Boundary의 목적을 알아보기
기본 사용 구조
App.tsx
export default function App: React.FC()(
//<QueryClientProvider client={queryClient}> 리액트쿼리 쓰려면!
<ErrorBoundary fallback={<p>Something went wrong</p>}>
<MyComponent />
</ErrorBoundary>
//</QueryClientProvider>
);
ErrorBoundary 컴포넌트 하위에 존재하는 컴포넌트에 에러throw 를 적용하기 위해, 상위에서 묶는다.
ErrorBoundary.tsx
interface ErrorBoundaryProps {
children: React.ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
}
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
// return this.props.fallback;
return <h1>error Boundary!</h1>
}
return this.props.children;
}
}
myComponent.tsx
const fetchTodos = async()=>{
try{
const res = await fetch("api 주소");
const data = awati res.json();
setTodos(data);
}catch(error){
console.error("fetchError",error);
setError(error as Error);
//throw error; //동작안함
}};
if(error){
throw error; //동작함
}
왜 비동기문 안에서는 throw 한 것을 catch를 못했을까?
브라우저 구조 - 렌더링 프로세스
event loop
callstack
v8 JS 엔진
api
구성되어있고, C++ 로 작성되었다.
비동기로직 - Event Loop 에서 처리한다 = 실행 컨텍스트를 가진다.
- fetch 서버와 통신
- event 사용자 이벤트 - onClick, onChange 등
- setTimeout
- 등
JS 엔진도 실행 컨텍스트를 가진다. 에러캐치는 여기에서 한다!
즉 다른 주체가 각각의 실행컨텍스트를 가지는데, 속한게 다르기 때문에 비동기는 캐치를 못한다.
JS 엔진 - 에러 바운더리 / Event Loop - 비동기
JS 엔진??
리액트 컴포넌트는 번들링이 되서 Js 파일이 script 태그를 만나서 Js 엔진에 전달된다. 그럼 파싱해서 실행 컨텍스트를 생성한다.
ErrorBoundary 는 리액트의 기능이기 때문에 JS 엔진에 해당하는 실행컨텍스트에서 에러를 캐치하는 것이다.
비동기 내부에서 throw를 한 것은 EventLoop 에서 에러가 발생한 것이기 때문에, JS엔진에서 catch 를 할 수 없다.
ErrorBoundary 의 목적은 리액트 렌더링 프로세스 내에서 발생하는 JavaScript 런타임 에러를 catch 하는 것!
*리액트 렌더링 프로세스 : react Element tree(render phase) -> Fider = V DOM (commit phase) -> 실제 DOM 에 반영 -> 브라우저 렌더링 프로세스
예기치 못한 에러가 발생해 throw 작업을 하지 못했더라도,
리액트 렌더링 프로세스 내부에서 발생했다면 errorBoundary를 catch 를 할 수 있다!
2. ErrorBoundary , ReactQuery 프로젝트에 적용