๐ฑ ์ฑ ๊ฐ๋ฐ ๋์ค ์๋ฌ๊ฐ ๋ฐ์ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ง ์๋๋ค๋ฉด ๋จธ๋ฆฌ๋ฟ๋ง ์๋๋ผ ์ ์ ์๊ฒ ๋ณด์ฌ์ค ํ๋ฉด๋ ์ํ์๊ฒ ๋ณํ ๊ฑฐ ๊ฐ๋ค.
๐ค ๋ฆฌ์กํธ์์๋ ์๋ฌ์ฒ๋ฆฌ๋ฅผ ์ด๋ป๊ฒ ํด์ค ์ ์์๊น?
๋ฆฌ์กํธ์ Error boundary (legacy)
๊ฐ๋ฐ ๋ฌธ์์ ์ํ๋ฉด ์ผ๋ฐ์ ์ผ๋ก ๋ฆฌ์กํธ๋ ๋ ๋๋ง ๋์ค์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด UI๋ฅผ ํ๋ฉด์์ ์ ๊ฑฐํ๋ค.
์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์๋ ํด๋น UI๋ฅผ Error boundary๋ก ๊ฐ์ธ์ค ์ ์๋ค.
์ฆ, Error boundary๋ ์๋ฌ๊ฐ ๋์ ์ ๊ฑฐ๋ UI๋์ ์ ํ๋ฉด์ fallback UI๋ฅผ ๋ณด์ฌ์ฃผ๋ ํน์ํ ์ปดํฌ๋ํธ๋ค.
Error boundary๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ static getDerivedStateFromError๋ฅผ ์ ๊ณตํด์ผ ํ๋ค.
static getDerivedStateFromError๋ฅผ ์ ๊ณตํด์ผ ์๋ฌ์ ๋ฐ๋ผ state์ ์ ๋ฐ์ดํธํ๊ณ ์ ์ ํํ ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ค ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ถ๊ฐ์ ์ผ๋ก componentDidCatch๋ฅผ ๊ตฌํํ๋ฉด ๋ถ์ ์๋น์ค์ ์ฐ์ผ ์๋ฌ ๋ก๊น ๋ก์ง์ ์ถ๊ฐํ ์๋ ์๋ค.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// ๋ค์ render ์์ fallback UI๊ฐ ๋ณด์ด๋๋ก state ์
๋ฐ์ดํธ
return { hasError: true };
}
componentDidCatch(error, info) {
// 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) {
// ์๋ฌด ์ปค์คํ
fallback UI๋ฅผ ๋ ๋ํ ์ ์๋ค.
return this.props.fallback;
}
return this.props.children;
}
}
// ์ปดํฌ๋ํธ ํธ๋ฆฌ ์ผ๋ถ๋ฅผ ๊ฐ์ธ์ฃผ๋ฉด ๋๋ค.
<ErrorBoundary fallback={<p>Something went wrong</p>}>
<Profile />
</ErrorBoundary>
Profile ๋ ์์ ์ปดํฌ๋ํธ์์ ์๋ฌ๋ฅผ ๋์ง๋ค๋ฉด ErrorBoundary์์๋ ๊ทธ ์๋ฌ๋ฅผ catch ํ๊ณ ,
์ ๊ณตํ fallback UI์ ํจ๊ป ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์๋ฌ ๊ณต์ง ์๋น์คํํ production error report๋ฅผ ๋ณด๋ธ๋ค.
๐ ํ์ฌ๋ ํจ์ ์ปดํฌ๋ํธ๋ก error boundary๋ฅผ ์์ฑํ ์๋ ์๋ค.
ํ์ง๋ง ์ง์ error boundary class๋ฅผ ์์ฑํ์ง ์๊ณ react-error-boundary ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ ์ ์๋ค.
ํจ์ํ ์ปดํฌ๋ํธ์์์ Error boundary
๋ฆฌ์กํธ์ Error boundary๊ฐ ํ ์ ์๋ ๋ช ๊ฐ์ง๋ฅผ ์ดํด๋ณด์๋ฉด
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ด์ ์๋ฌ ์บ์น (try-catch ๋ธ๋ก์ผ๋ก ๊ฐ์ธ์ค์ผ ํจ) โ
- ๋น๋๊ธฐ ์ฝ๋ (API, setTimeout, requestAnimationFrame ๋ฑ๋ฑ) ๋ด์์ ์บ์น โ
- ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง ๋ด์ ์๋ฌ ์บ์น โ
- Error boundary ์์ฒด ๋ด์ ์๋ฌ ์บ์น โ
- ํจ์ ์ปดํฌ๋ํธ์ ์ฌ์ฉ โ
- ๋ด๋ถ์ ํ ์ฌ์ฉ โ
๊ฐ๋ฐ ๋ฌธ์์์ ๋์๋ฏ์ด ์ ํต์ ์ธ Error Boundary ์ปดํฌ๋ํธ์ wrapper๋ฅผ ์ฐ์ธ react-error-boundary๋ฅผ ํ์ฉํด์ ์์ ์ด์๋ค์ ๊ทน๋ณตํ ์ ์๋ค.
์ฌ์ฉ๋ฒ
์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ๋๋ ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ <ErrorBoundary>๋ก ๊ฐ์ธ์ค ์ ์๋ค.
๊ธฐ๋ณธ์ ์ธ ๊ตฌ์ฑ์ ์ดํด๋ณด์.
๋ค์์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด fallback UI๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์๋ค.
import React from 'react';
import { ErrorBoundary } from "react-error-boundary";
const App = () => {
return <ErrorBoundary fallback={<div>Something went wrong</div>}>
/* rest of your component */
</ErrorBoundary>
}
๋ค์๊ณผ ๊ฐ์ด fallback ์ปดํฌ๋ํธ๋ก ์์ธํ ์๋ฌ ์ฌํญ์ ๋ฉ์์ง๋ก ๋ํ๋ด๊ณ ์ถ์ ์ ์๋ค.
import React from 'react';
import { ErrorBoundary } from "react-error-boundary";
function fallbackRender({ error, resetErrorBoundary }) {
// resetErrorBoundary()๋ฅผ ํธ์ถํ์ฌ ์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ฅผ resetํ๊ณ ๋๋๋ง์ ์ฌ์๋ ํ๋ค.
return (
<div role="alert">
<p>Something went wrong:</p>
<pre style={{ color: "red" }}>{error.message}</pre>
</div>
);
}
const App = () => {
return <ErrorBoundary
fallbackRender={fallbackRender}
onReset={(details) => {
// ์ฑ์ state์ ๋ฆฌ์
ํด์ ์๋ฌ๊ฐ ๋ค์ ๋ฐ์ํ๋ ๊ฑธ ๋ง๊ธฐ
}}
>
/* rest of your component */
</ErrorBoundary>
}
๋๋ fallback์ด๋ fallbackRender ๋์ ๋ค์๊ณผ ๊ฐ์ด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์ถ์ ์ ์๋ค.
import React from 'react';
import { ErrorBoundary } from "react-error-boundary";
const Fallback = ({ error, resetErrorBoundary }) => {
// Call resetErrorBoundary() to reset the error boundary and retry the render.
return (
<div role="alert">
<p>Something went wrong:</p>
<pre style={{ color: "red" }}>{error.message}</pre>
</div>
);
}
const App = () => {
return <ErrorBoundary
FallbackComponent={Fallback}
onReset={(details) => {
// Reset the state of your app so the error doesn't happen again
}}
>
/* rest of your component */
</ErrorBoundary>
}
๋ง์ฝ ์๋ฌ ๋ก๊ทธ๋ฅผ ํ๊ณ ์ถ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐํด ์ค ์ ์๋ค.
import React from 'react';
import { ErrorBoundary } from "react-error-boundary";
const logError = (error: Error, info: { componentStack: string }) => {
// Do something with the error, e.g. log to an external API
};
const Fallback = ({ error, resetErrorBoundary }) => {
// Call resetErrorBoundary() to reset the error boundary and retry the render.
return (
<div role="alert">
<p>Something went wrong:</p>
<pre style={{ color: "red" }}>{error.message}</pre>
</div>
);
}
// You can use fallback / fallbackRender / FallbackComponent anything
const App = () => {
return <ErrorBoundary
FallbackComponent={Fallback}
onError={logError}
onReset={(details) => {
// Reset the state of your app so the error doesn't happen again
}}
>
/* rest of your component */
</ErrorBoundary>
}
๋๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋น๋๊ธฐ ์ฝ๋์ ์๋ฌ๋ฅผ ์บ์นํ ์ ์๋ค.
import { useErrorBoundary } from "react-error-boundary";
function Example() {
const { showBoundary } = useErrorBoundary();
const getGreeting = async(name) => {
try {
const response = await fetchGreeting(name);
// rest of your code
} catch(error){
// Show error boundary
showBoundary(error);
}
}
useEffect(() => {
getGreeting()
});
return <Whatever UI you want to render/>
}
๋ค๋ง ErrorBoundary๋ ํด๋ผ์ด์ธํธ ์ชฝ ์ปดํฌ๋ํธ๋ค.
๋ฐ๋ผ์ nextjs์์ ์ฌ์ฉ ์ 'use client'์ ํ์ผ ์ต์๋จ์ ์ธ๊ธํด ์ฃผ๊ฑฐ๋,
serializeable (๋ฐ์ดํธ ์คํธ๋ฆผ์ผ๋ก ํ๋กญ์ค ํํ๋ฅผ ์์ ๋กญ๊ฒ ์ ํ์ํฌ ์ ์๋ ํน์ฑ, js์์๋ JSON.stringify()์ JSON.parse()๋ก ์ ํํ ์ ์๋ค) props๋ง ์ ๋ฌํ ์ ์๋ค.
๐ References
https://medium.com/@vnkelkar11/using-error-boundary-in-react-a29ded725eee
Mastering Error Boundaries in React: A Guide to Effective Error Handling
What is an Error Boundary?
medium.com
https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
Component – React
The library for web and native user interfaces
react.dev
'๐ฉ๐ปโ๐ป dev' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ ์คใ ฃSLASH 23 - ํผ๋ : ์์์ง๋ ํ์ด์ง ํ ๋ฐฉ์ ๊ด๋ฆฌํ๊ธฐ (TOSH | SLASH 23 - Funnel Pattern : Organizing an overflow of pages at once) (2) | 2024.07.05 |
---|---|
๋ชจ๋ ์์คํ (0) | 2024.06.26 |
โ๏ธ <Suspense> ํ์ฉ (0) | 2024.06.16 |
โ๏ธ <Suspense>๋? (0) | 2024.06.15 |
์ ์ธ์ ์ธ ์ฝ๋ ์์ฑํ๊ธฐ (2) | 2024.06.13 |