React ErrorBoundary๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ถฉ๋์ ๋ฐฉ์งํ๋ฉฐ, ๊ฐ๋ ฅํ ๋ณต๊ตฌ ์ ๋ต์ผ๋ก ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
React ErrorBoundary: ์ค๋ฅ ๊ฒฉ๋ฆฌ ๋ฐ ๋ณต๊ตฌ ์ ๋ต
๋์ ์ธ ํ๋ก ํธ์๋ ๊ฐ๋ฐ ์ธ๊ณ์์, ํนํ React์ ๊ฐ์ ๋ณต์กํ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ํ๋ ์์ํฌ๋ก ์์
ํ ๋ ์์์น ๋ชปํ ์ค๋ฅ๋ ๋ถ๊ฐํผํฉ๋๋ค. ์ด๋ฌํ ์ค๋ฅ๋ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌ๋์ง ์์ผ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ถฉ๋๊ณผ ์ค๋ง์ค๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. React์ ErrorBoundary ์ปดํฌ๋ํธ๋ ์ด๋ฌํ ์ค๋ฅ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ๊ฒฉ๋ฆฌํ๋ฉฐ, ๋ณต๊ตฌ ์ ๋ต์ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ์๋ฃจ์
์ ์ ๊ณตํฉ๋๋ค. ์ด ์ข
ํฉ ๊ฐ์ด๋์์๋ ErrorBoundary์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ํ์ํ๊ณ , ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ตฌํํ์ฌ ์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํด ๋ ํ๋ ฅ์ ์ด๊ณ ์ฌ์ฉ์ ์นํ์ ์ธ React ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
Error Boundary์ ํ์์ฑ ์ดํดํ๊ธฐ
๊ตฌํ์ ๋ค์ด๊ฐ๊ธฐ ์ ์, Error Boundary๊ฐ ์ ํ์์ ์ธ์ง ์ดํดํด ๋ด ์๋ค. React์์ ๋ ๋๋ง ์ค, ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋ ๋๋ ์์ ์ปดํฌ๋ํธ์ ์์ฑ์์์ ๋ฐ์ํ๋ ์ค๋ฅ๋ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ถฉ๋์ํฌ ์ ์์ต๋๋ค. ์ด๋ ์ฒ๋ฆฌ๋์ง ์์ ์ค๋ฅ๊ฐ ์ปดํฌ๋ํธ ํธ๋ฆฌ๋ฅผ ํ๊ณ ์ฌ๋ผ๊ฐ ์ข ์ข ๋น ํ๋ฉด์ด๋ ๋์์ด ๋์ง ์๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ผ๋ณธ์ ์๋ ์ฌ์ฉ์๊ฐ ์ค์ํ ๊ธ์ต ๊ฑฐ๋๋ฅผ ์๋ฃํ๋ ค๋ค๊ฐ, ๊ด๋ จ ์์ด ๋ณด์ด๋ ์ปดํฌ๋ํธ์ ์ฌ์ํ ์ค๋ฅ ๋๋ฌธ์ ๋น ํ๋ฉด์ ๋ง์ฃผํ๋ ์ํฉ์ ์์ํด ๋ณด์ญ์์ค. ์ด๋ ์ ์ ์ ์ธ ์ค๋ฅ ๊ด๋ฆฌ์ ์ค์์ฑ์ ์ ๋ณด์ฌ์ค๋๋ค.
Error Boundary๋ ์์ ์ปดํฌ๋ํธ ํธ๋ฆฌ ์ด๋์์๋ JavaScript ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ๊ณ , ํด๋น ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ๋ฉฐ, ์ปดํฌ๋ํธ ํธ๋ฆฌ๋ฅผ ์ถฉ๋์ํค๋ ๋์ ๋์ฒด UI(fallback UI)๋ฅผ ํ์ํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฒฐํจ์ด ์๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฒฉ๋ฆฌํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ๋ถ๋ถ์์ ๋ฐ์ํ ์ค๋ฅ๊ฐ ๋ค๋ฅธ ๋ถ๋ถ์ ์ํฅ์ ๋ฏธ์น๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ, ์ ์ธ๊ณ์ ์ผ๋ก ๋ ์์ ์ ์ด๊ณ ์ ๋ขฐํ ์ ์๋ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
React ErrorBoundary๋ ๋ฌด์์ธ๊ฐ?
ErrorBoundary๋ ์์ ์ปดํฌ๋ํธ ํธ๋ฆฌ ์ด๋์์๋ JavaScript ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ๊ณ , ํด๋น ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ๋ฉฐ, ๋์ฒด UI๋ฅผ ํ์ํ๋ React ์ปดํฌ๋ํธ์
๋๋ค. ์ด๋ ๋ค์ ๋ ๊ฐ์ง ์๋ช
์ฃผ๊ธฐ ๋ฉ์๋ ์ค ํ๋ ๋๋ ๋ ๋ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค ์ปดํฌ๋ํธ์
๋๋ค:
static getDerivedStateFromError(error): ์ด ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋๋ ํ์ ์ปดํฌ๋ํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ์ ํธ์ถ๋ฉ๋๋ค. ๋ฐ์ํ ์ค๋ฅ๋ฅผ ์ธ์๋ก ๋ฐ์ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ ๊ฐ์ ๋ฐํํด์ผ ํฉ๋๋ค.componentDidCatch(error, info): ์ด ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋๋ ํ์ ์ปดํฌ๋ํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ์ ํธ์ถ๋ฉ๋๋ค. ๋ฐ์ํ ์ค๋ฅ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ ์ปดํฌ๋ํธ์ ๋ํ ์ ๋ณด๊ฐ ๋ด๊ธด info ๊ฐ์ฒด, ๋ ๊ฐ์ง ์ธ์๋ฅผ ๋ฐ์ต๋๋ค. ์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ ์ ๋ณด๋ฅผ ๊ธฐ๋กํ๊ฑฐ๋ ๋ค๋ฅธ ๋ถ์ ํจ๊ณผ(side effects)๋ฅผ ์ํํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ErrorBoundary ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
๊ธฐ๋ณธ ์๋ฆฌ๋ฅผ ์ค๋ช
ํ๊ธฐ ์ํด ๊ฐ๋จํ ErrorBoundary ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
์ฝ๋ ์์
๋ค์์ ๊ฐ๋จํ ErrorBoundary ์ปดํฌ๋ํธ์ ์ฝ๋์
๋๋ค:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
};
}
static getDerivedStateFromError(error) {
// ๋ค์ ๋ ๋๋ง์์ ๋์ฒด UI๋ฅผ ํ์ํ๋๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// "componentStack" ์์:
// in ComponentThatThrows (created by App)
// in App
console.error("Caught an error:", error);
console.error("Error info:", info.componentStack);
this.setState({ error: error, errorInfo: info });
// ์ค๋ฅ ๋ณด๊ณ ์๋น์ค์ ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ ์๋ ์์ต๋๋ค
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// ์ด๋ค ์ปค์คํ
๋์ฒด UI๋ ๋ ๋๋งํ ์ ์์ต๋๋ค
return (
๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
์ค๋ฅ: {this.state.error && this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
);
}
return this.props.children;
}
}
export default ErrorBoundary;
์ค๋ช
- ์์ฑ์(Constructor): ์์ฑ์๋ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ
hasError๊ฐfalse๋ก ์ค์ ๋๋๋ก ์ด๊ธฐํํฉ๋๋ค. ๋ํ ๋๋ฒ๊น ๋ชฉ์ ์ผ๋ก error์ errorInfo๋ฅผ ์ ์ฅํฉ๋๋ค. getDerivedStateFromError(error): ์ด ์ ์ ๋ฉ์๋๋ ์์ ์ปดํฌ๋ํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ํธ์ถ๋ฉ๋๋ค. ์ํ๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ ๋ํ๋ ๋๋ค.componentDidCatch(error, info): ์ด ๋ฉ์๋๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ์ ํธ์ถ๋ฉ๋๋ค. ์ค๋ฅ์ ์ปดํฌ๋ํธ ์คํ์ ๋ํ ์ ๋ณด๊ฐ ๋ด๊ธดinfo๊ฐ์ฒด๋ฅผ ๋ฐ์ต๋๋ค. ์ฌ๊ธฐ์๋ ์ฝ์์ ์ค๋ฅ๋ฅผ ๊ธฐ๋กํฉ๋๋ค (Sentry, Bugsnag ๋๋ ์์ฒด ์๋ฃจ์ ๊ณผ ๊ฐ์ ์ ํธํ๋ ๋ก๊น ๋ฉ์ปค๋์ฆ์ผ๋ก ๊ต์ฒดํ์ธ์). ๋ํ ์ํ์ error์ errorInfo๋ฅผ ์ค์ ํฉ๋๋ค.render(): render ๋ฉ์๋๋hasError์ํ๋ฅผ ํ์ธํฉ๋๋ค.true์ด๋ฉด ๋์ฒด UI๋ฅผ ๋ ๋๋งํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ์ปดํฌ๋ํธ์ ์์๋ค์ ๋ ๋๋งํฉ๋๋ค. ๋์ฒด UI๋ ์ ์ตํ๊ณ ์ฌ์ฉ์ ์นํ์ ์ด์ด์ผ ํฉ๋๋ค. ๊ฐ๋ฐ์์๊ฒ๋ ์ ์ฉํ์ง๋ง, ์ค๋ฅ ์ธ๋ถ ์ ๋ณด์ ์ปดํฌ๋ํธ ์คํ์ ๋ณด์์์ ์ด์ ๋ก ํ๋ก๋์ ํ๊ฒฝ์์๋ ์กฐ๊ฑด๋ถ๋ก ๋ ๋๋งํ๊ฑฐ๋ ์ ๊ฑฐํด์ผ ํฉ๋๋ค.
ErrorBoundary ์ปดํฌ๋ํธ ์ฌ์ฉํ๊ธฐ
ErrorBoundary ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด, ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ทธ ์์ ๊ฐ์ธ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
์ฝ๋ ์์
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
return (
{/* ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ ์ปดํฌ๋ํธ๋ค */}
);
}
function App() {
return (
);
}
export default App;
์ค๋ช
์ด ์์ ์์ MyComponent๋ ErrorBoundary๋ก ๊ฐ์ธ์ ธ ์์ต๋๋ค. MyComponent ๋๋ ๊ทธ ์์ ์ปดํฌ๋ํธ ๋ด์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ErrorBoundary๊ฐ ์ด๋ฅผ ํฌ์ฐฉํ์ฌ ๋์ฒด UI๋ฅผ ๋ ๋๋งํฉ๋๋ค.
๊ณ ๊ธ ErrorBoundary ์ ๋ต
๊ธฐ๋ณธ ErrorBoundary๊ฐ ๊ธฐ๋ณธ์ ์ธ ์ค๋ฅ ์ฒ๋ฆฌ ์์ค์ ์ ๊ณตํ์ง๋ง, ์ค๋ฅ ๊ด๋ฆฌ๋ฅผ ํฅ์์ํค๊ธฐ ์ํด ๊ตฌํํ ์ ์๋ ๋ช ๊ฐ์ง ๊ณ ๊ธ ์ ๋ต์ด ์์ต๋๋ค.
1. ์ธ๋ถํ๋ Error Boundary
์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ๋จ์ผ ErrorBoundary๋ก ๊ฐ์ธ๋ ๋์ , ์ธ๋ถํ๋ Error Boundary๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ณ ๋ คํด ๋ณด์ธ์. ์ด๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ๊ฑฐ๋ ์คํจ ์ ์ํฅ์ด ์ ํ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์
์ ํน์ ๋ถ๋ถ ์ฃผ์์ ErrorBoundary ์ปดํฌ๋ํธ๋ฅผ ๋ฐฐ์นํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๊ฐ๋ณ ์์ ฏ์ด๋ ์ธ๋ถ ๋ฐ์ดํฐ ์์ค์ ์์กดํ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ์ ์์ต๋๋ค.
์์
function ProductList() {
return (
{/* ์ ํ ๋ชฉ๋ก */}
);
}
function RecommendationWidget() {
return (
{/* ์ถ์ฒ ์์ง */}
);
}
function App() {
return (
);
}
์ด ์์ ์์ RecommendationWidget์ ์์ฒด ErrorBoundary๋ฅผ ๊ฐ์ง๋๋ค. ์ถ์ฒ ์์ง์ด ์คํจํ๋๋ผ๋ ProductList์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ผ๋ฉฐ, ์ฌ์ฉ์๋ ๊ณ์ํด์ ์ ํ์ ํ์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ธ๋ถํ๋ ์ ๊ทผ ๋ฐฉ์์ ์ค๋ฅ๋ฅผ ๊ฒฉ๋ฆฌํ๊ณ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด๋ก ์ ํ๋๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ ์ ๋ฐ์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํฉ๋๋ค.
2. ์ค๋ฅ ๋ก๊น ๋ฐ ๋ณด๊ณ
์ค๋ฅ ๋ก๊น
์ ๋๋ฒ๊น
๊ณผ ๋ฐ๋ณต๋๋ ๋ฌธ์ ๋ฅผ ์๋ณํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. componentDidCatch ์๋ช
์ฃผ๊ธฐ ๋ฉ์๋๋ Sentry, Bugsnag, ๋๋ Rollbar์ ๊ฐ์ ์ค๋ฅ ๋ก๊น
์๋น์ค์ ํตํฉํ๊ธฐ์ ์ด์์ ์ธ ์ฅ์์
๋๋ค. ์ด๋ฌํ ์๋น์ค๋ ์คํ ํธ๋ ์ด์ค, ์ฌ์ฉ์ ์ปจํ
์คํธ, ํ๊ฒฝ ์ ๋ณด๋ฅผ ํฌํจํ ์์ธํ ์ค๋ฅ ๋ณด๊ณ ์๋ฅผ ์ ๊ณตํ์ฌ ๋ฌธ์ ๋ฅผ ์ ์ํ๊ฒ ์ง๋จํ๊ณ ํด๊ฒฐํ ์ ์๊ฒ ํด์ค๋๋ค. GDPR๊ณผ ๊ฐ์ ๊ฐ์ธ์ ๋ณด ๋ณดํธ ๊ท์ ์ ์ค์ํ๊ธฐ ์ํด ์ค๋ฅ ๋ก๊ทธ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ๋ฏผ๊ฐํ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์ต๋ช
ํํ๊ฑฐ๋ ์ญ์ ํ๋ ๊ฒ์ ๊ณ ๋ คํ์ธ์.
์์
import * as Sentry from "@sentry/react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(error) {
// ๋ค์ ๋ ๋๋ง์์ ๋์ฒด UI๋ฅผ ํ์ํ๋๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// ์ค๋ฅ๋ฅผ Sentry์ ๊ธฐ๋กํฉ๋๋ค
Sentry.captureException(error, { extra: info });
// ์ค๋ฅ ๋ณด๊ณ ์๋น์ค์ ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ ์๋ ์์ต๋๋ค
console.error("Caught an error:", error);
}
render() {
if (this.state.hasError) {
// ์ด๋ค ์ปค์คํ
๋์ฒด UI๋ ๋ ๋๋งํ ์ ์์ต๋๋ค
return (
๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
);
}
return this.props.children;
}
}
export default ErrorBoundary;
์ด ์์ ์์ componentDidCatch ๋ฉ์๋๋ Sentry.captureException์ ์ฌ์ฉํ์ฌ Sentry์ ์ค๋ฅ๋ฅผ ๋ณด๊ณ ํฉ๋๋ค. Sentry๋ฅผ ๊ตฌ์ฑํ์ฌ ํ์ ์๋ฆผ์ ๋ณด๋ด๋๋ก ์ค์ ํ๋ฉด ์ฌ๊ฐํ ์ค๋ฅ์ ์ ์ํ๊ฒ ๋์ํ ์ ์์ต๋๋ค.
3. ์ปค์คํ ๋์ฒด UI
ErrorBoundary๊ฐ ํ์ํ๋ ๋์ฒด UI๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋๋ ์ฌ์ฉ์ ์นํ์ ์ธ ๊ฒฝํ์ ์ ๊ณตํ ๊ธฐํ์
๋๋ค. ์ผ๋ฐ์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ๋์ , ์ฌ์ฉ์๋ฅผ ํด๊ฒฐ์ฑ
์ผ๋ก ์๋ดํ๋ ๋ ์ ์ตํ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ๊ฒ์ ๊ณ ๋ คํด ๋ณด์ธ์. ์ฌ๊ธฐ์๋ ํ์ด์ง๋ฅผ ์๋ก ๊ณ ์น๋ ๋ฐฉ๋ฒ, ๊ณ ๊ฐ ์ง์์ ๋ฌธ์ํ๋ ๋ฐฉ๋ฒ, ๋๋ ๋์ค์ ๋ค์ ์๋ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์ง์นจ์ด ํฌํจ๋ ์ ์์ต๋๋ค. ๋ฐ์ํ ์ค๋ฅ ์ ํ์ ๋ฐ๋ผ ๋์ฒด UI๋ฅผ ๋ง์ถค ์ค์ ํ ์๋ ์์ต๋๋ค.
์์
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error) {
// ๋ค์ ๋ ๋๋ง์์ ๋์ฒด UI๋ฅผ ํ์ํ๋๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, info) {
console.error("Caught an error:", error);
// ์ค๋ฅ ๋ณด๊ณ ์๋น์ค์ ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ ์๋ ์์ต๋๋ค
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// ์ด๋ค ์ปค์คํ
๋์ฒด UI๋ ๋ ๋๋งํ ์ ์์ต๋๋ค
if (this.state.error instanceof NetworkError) {
return (
๋คํธ์ํฌ ์ค๋ฅ
์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํ๊ณ ๋ค์ ์๋ํด์ฃผ์ธ์.
);
} else {
return (
๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๊ฑฐ๋ ๊ณ ๊ฐ ์ง์์ ๋ฌธ์ํด์ฃผ์ธ์.
);
}
}
return this.props.children;
}
}
export default ErrorBoundary;
์ด ์์ ์์ ๋์ฒด UI๋ ์ค๋ฅ๊ฐ NetworkError์ธ์ง ํ์ธํฉ๋๋ค. ๋ง์ฝ ๊ทธ๋ ๋ค๋ฉด, ์ฌ์ฉ์์๊ฒ ์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํ๋ผ๋ ํน์ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ผ๋ฐ์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค. ๊ตฌ์ฒด์ ์ด๊ณ ์คํ ๊ฐ๋ฅํ ์ง์นจ์ ์ ๊ณตํ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค.
4. ์ฌ์๋ ๋ฉ์ปค๋์ฆ
์ด๋ค ๊ฒฝ์ฐ์๋ ์ค๋ฅ๊ฐ ์ผ์์ ์ด๋ฉฐ ์์
์ ์ฌ์๋ํ๋ฉด ํด๊ฒฐ๋ ์ ์์ต๋๋ค. ErrorBoundary ๋ด์ ์ฌ์๋ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ์ฌ ์ผ์ ์ง์ฐ ํ ์คํจํ ์์
์ ์๋์ผ๋ก ์ฌ์๋ํ ์ ์์ต๋๋ค. ์ด๋ ๋คํธ์ํฌ ์ค๋ฅ๋ ์ผ์์ ์ธ ์๋ฒ ์ค๋จ์ ์ฒ๋ฆฌํ๋ ๋ฐ ํนํ ์ ์ฉํ ์ ์์ต๋๋ค. ๋ถ์ ํจ๊ณผ๊ฐ ์์ ์ ์๋ ์์
์ ์ฌ์๋ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ ๋๋ ์ฃผ์ํด์ผ ํฉ๋๋ค. ์ฌ์๋ํ๋ฉด ์๋ํ์ง ์์ ๊ฒฐ๊ณผ๋ก ์ด์ด์ง ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์์
import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [retryCount, setRetryCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (e) {
setError(e);
setRetryCount(prevCount => prevCount + 1);
} finally {
setIsLoading(false);
}
};
if (error && retryCount < 3) {
const retryDelay = Math.pow(2, retryCount) * 1000; // ์ง์ ๋ฐฑ์คํ
console.log(`${retryDelay / 1000}์ด ํ์ ์ฌ์๋ํฉ๋๋ค...`);
const timer = setTimeout(fetchData, retryDelay);
return () => clearTimeout(timer); // ์ธ๋ง์ดํธ ๋๋ ๋ฆฌ๋ ๋ ์ ํ์ด๋จธ ์ ๋ฆฌ
}
if (!data) {
fetchData();
}
}, [error, retryCount, data]);
if (isLoading) {
return ๋ฐ์ดํฐ ๋ก๋ฉ ์ค...
;
}
if (error) {
return ์ค๋ฅ: {error.message} - {retryCount}ํ ์ฌ์๋ํจ.
;
}
return ๋ฐ์ดํฐ: {JSON.stringify(data)}
;
}
function App() {
return (
);
}
export default App;
์ด ์์ ์์ DataFetchingComponent๋ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ์๋ํฉ๋๋ค. ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด retryCount๋ฅผ ์ฆ๊ฐ์ํค๊ณ ์ง์์ ์ผ๋ก ์ฆ๊ฐํ๋ ์ง์ฐ ์๊ฐ ํ์ ์์
์ ์ฌ์๋ํฉ๋๋ค. ErrorBoundary๋ ์ฒ๋ฆฌ๋์ง ์์ ์์ธ๋ฅผ ํฌ์ฐฉํ๊ณ ์ฌ์๋ ํ์๋ฅผ ํฌํจํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค.
5. Error Boundary์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR)
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR)์ ์ฌ์ฉํ ๋ ์ค๋ฅ ์ฒ๋ฆฌ๋ ๋์ฑ ์ค์ํด์ง๋๋ค. ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ค๋ฅ๋ ์ ์ฒด ์๋ฒ๋ฅผ ๋ค์ด์์ผ ๋ค์ดํ์๊ณผ ์ข์ง ์์ ์ฌ์ฉ์ ๊ฒฝํ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์๋ฒ์ ํด๋ผ์ด์ธํธ ์์ชฝ์์ ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ๋๋ก Error Boundary๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌ์ฑ๋์๋์ง ํ์ธํด์ผ ํฉ๋๋ค. ์ข ์ข Next.js๋ Remix์ ๊ฐ์ SSR ํ๋ ์์ํฌ์๋ React Error Boundary๋ฅผ ๋ณด์ํ๋ ์์ฒด ๋ด์ฅ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ด ์์ต๋๋ค.
6. Error Boundary ํ ์คํธํ๊ธฐ
Error Boundary๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๊ณ ์์๋ ๋์ฒด UI๋ฅผ ์ ๊ณตํ๋์ง ํ์ธํ๋ ค๋ฉด ํ ์คํธ๊ฐ ํ์์ ์ ๋๋ค. Jest๋ React Testing Library์ ๊ฐ์ ํ ์คํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ ์ํฉ์ ์๋ฎฌ๋ ์ด์ ํ๊ณ Error Boundary๊ฐ ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ์ฌ ์ ์ ํ ๋์ฒด UI๋ฅผ ๋ ๋๋งํ๋์ง ํ์ธํ์ธ์. ๋ค์ํ ์ ํ์ ์ค๋ฅ์ ์ฃ์ง ์ผ์ด์ค๋ฅผ ํ ์คํธํ์ฌ Error Boundary๊ฐ ๊ฒฌ๊ณ ํ๊ณ ๊ด๋ฒ์ํ ์๋๋ฆฌ์ค๋ฅผ ์ฒ๋ฆฌํ ์ ์๋๋ก ํด์ผ ํฉ๋๋ค.
์์
import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('์ด ์ปดํฌ๋ํธ๋ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค');
return ์ด๊ฒ์ ๋ ๋๋ง๋์ด์๋ ์ ๋ฉ๋๋ค
;
}
test('์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ๋์ฒด UI๋ฅผ ๋ ๋๋งํฉ๋๋ค', () => {
render(
);
const errorMessage = screen.getByText(/๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค/i);
expect(errorMessage).toBeInTheDocument();
});
์ด ํ
์คํธ๋ ErrorBoundary ๋ด์์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ฌธ์์ ์๋์ง ํ์ธํ์ฌ ๋์ฒด UI๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋ ๋๋ง๋์๋์ง ๊ฒ์ฆํฉ๋๋ค.
7. ์ฐ์ํ ์ฑ๋ฅ ์ ํ(Graceful Degradation)
Error Boundary๋ React ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฐ์ํ ์ฑ๋ฅ ์ ํ๋ฅผ ๊ตฌํํ๋ ํต์ฌ ์์์ ๋๋ค. ์ฐ์ํ ์ฑ๋ฅ ์ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ผ๋ถ๊ฐ ์คํจํ๋๋ผ๋ ๊ธฐ๋ฅ์ด ์ถ์๋ ์ํ๋ก ๊ณ์ ์๋ํ๋๋ก ์ค๊ณํ๋ ๊ดํ์ ๋๋ค. Error Boundary๋ฅผ ์ฌ์ฉํ๋ฉด ์คํจํ ์ปดํฌ๋ํธ๋ฅผ ๊ฒฉ๋ฆฌํ๊ณ ๋๋จธ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํฅ์ ๋ฏธ์น๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค. ๋์ฒด UI์ ๋์ ๊ธฐ๋ฅ์ ์ ๊ณตํจ์ผ๋ก์จ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ผ๋ ์ฌ์ฉ์๊ฐ ํ์ ๊ธฐ๋ฅ์ ๊ณ์ ์ก์ธ์คํ ์ ์๋๋ก ๋ณด์ฅํ ์ ์์ต๋๋ค.
ํผํด์ผ ํ ์ผ๋ฐ์ ์ธ ํจ์
ErrorBoundary๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ด์ง๋ง, ํผํด์ผ ํ ๋ช ๊ฐ์ง ์ผ๋ฐ์ ์ธ ํจ์ ์ด ์์ต๋๋ค:
- ๋น๋๊ธฐ ์ฝ๋ ๊ฐ์ธ์ง ์๊ธฐ:
ErrorBoundary๋ ๋ ๋๋ง ์ค, ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋, ์์ฑ์์์ ๋ฐ์ํ๋ ์ค๋ฅ๋ง ํฌ์ฐฉํฉ๋๋ค. ๋น๋๊ธฐ ์ฝ๋(์:setTimeout,Promises)์ ์ค๋ฅ๋try...catch๋ธ๋ก์ ์ฌ์ฉํ์ฌ ํฌ์ฐฉํ๊ณ ๋น๋๊ธฐ ํจ์ ๋ด์์ ์ ์ ํ๊ฒ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. - Error Boundary ๋จ์ฉํ๊ธฐ: ์ ํ๋ฆฌ์ผ์ด์
์ ๋์ ๋ถ๋ถ์ ๋จ์ผ
ErrorBoundary๋ก ๊ฐ์ธ๋ ๊ฒ์ ํผํ์ธ์. ์ด๋ ์ค๋ฅ์ ์์ธ์ ๊ฒฉ๋ฆฌํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค๊ณ ์ผ๋ฐ์ ์ธ ๋์ฒด UI๊ฐ ๋๋ฌด ์์ฃผ ํ์๋๊ฒ ํ ์ ์์ต๋๋ค. ์ธ๋ถํ๋ Error Boundary๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์ปดํฌ๋ํธ๋ ๊ธฐ๋ฅ์ ๊ฒฉ๋ฆฌํ์ธ์. - ์ค๋ฅ ์ ๋ณด ๋ฌด์ํ๊ธฐ: ๋จ์ํ ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ๊ณ ๋์ฒด UI๋ฅผ ํ์ํ๋ ๋ฐ ๊ทธ์น์ง ๋ง์ธ์. ์ค๋ฅ ์ ๋ณด(์ปดํฌ๋ํธ ์คํ ํฌํจ)๋ฅผ ์ค๋ฅ ๋ณด๊ณ ์๋น์ค๋ ์ฝ์์ ๋ฐ๋์ ๊ธฐ๋กํ์ธ์. ์ด๋ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ ๋ฅผ ์ง๋จํ๊ณ ์์ ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ํ๋ก๋์ ํ๊ฒฝ์์ ๋ฏผ๊ฐํ ์ ๋ณด ํ์ํ๊ธฐ: ํ๋ก๋์ ํ๊ฒฝ์์ ์์ธํ ์ค๋ฅ ์ ๋ณด(์: ์คํ ํธ๋ ์ด์ค)๋ฅผ ํ์ํ๋ ๊ฒ์ ํผํ์ธ์. ์ด๋ ์ฌ์ฉ์์๊ฒ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๋ ธ์ถํ ์ ์์ผ๋ฉฐ ๋ณด์ ์ํ์ด ๋ ์ ์์ต๋๋ค. ๋์ ์ฌ์ฉ์ ์นํ์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ๊ณ ์์ธ ์ ๋ณด๋ ์ค๋ฅ ๋ณด๊ณ ์๋น์ค์ ๊ธฐ๋กํ์ธ์.
ํจ์ํ ์ปดํฌ๋ํธ ๋ฐ ํ ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ Error Boundary
Error Boundary๋ ํด๋์ค ์ปดํฌ๋ํธ๋ก ๊ตฌํ๋์ง๋ง, ํ ์ ์ฌ์ฉํ๋ ํจ์ํ ์ปดํฌ๋ํธ ๋ด์ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ์ฌ์ ํ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ผ๋ฐ์ ์ธ ์ ๊ทผ ๋ฐฉ์์ ์ด์ ์ ์ค๋ช ํ ๊ฒ์ฒ๋ผ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ErrorBoundary ์ปดํฌ๋ํธ๋ก ๊ฐ์ธ๋ ๊ฒ์ ๋๋ค. ์ค๋ฅ ์ฒ๋ฆฌ ๋ก์ง์ ErrorBoundary ๋ด์ ์กด์ฌํ๋ฉฐ, ํจ์ํ ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ด๋ ํ ์คํ ์ค์ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ฒฉ๋ฆฌํฉ๋๋ค.
๊ตฌ์ฒด์ ์ผ๋ก, ํจ์ํ ์ปดํฌ๋ํธ์ ๋ ๋๋ง ์ค์ด๋ useEffect ํ ์ ๋ณธ๋ฌธ ๋ด์์ ๋ฐ์ํ๋ ๋ชจ๋ ์ค๋ฅ๋ ErrorBoundary์ ์ํด ํฌ์ฐฉ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ErrorBoundary๋ ํจ์ํ ์ปดํฌ๋ํธ ๋ด์ DOM ์์์ ์ฐ๊ฒฐ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ(์: onClick, onChange) ๋ด์์ ๋ฐ์ํ๋ ์ค๋ฅ๋ ํฌ์ฐฉํ์ง ์๋๋ค๋ ์ ์ ์ ์ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๊ฒฝ์ฐ, ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ์ํด ์ ํต์ ์ธ try...catch ๋ธ๋ก์ ๊ณ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์ค๋ฅ ๋ฉ์์ง์ ๊ตญ์ ํ ๋ฐ ํ์งํ
์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฐ๋ฐํ ๋๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๊ตญ์ ํํ๊ณ ํ์งํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ErrorBoundary์ ๋์ฒด UI์ ํ์๋๋ ์ค๋ฅ ๋ฉ์์ง๋ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๊ธฐ ์ํด ์ฌ์ฉ์๊ฐ ์ ํธํ๋ ์ธ์ด๋ก ๋ฒ์ญ๋์ด์ผ ํฉ๋๋ค. i18next๋ React Intl๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒ์ญ์ ๊ด๋ฆฌํ๊ณ ์ฌ์ฉ์์ ๋ก์ผ์ผ์ ๋ฐ๋ผ ์ ์ ํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋์ ์ผ๋ก ํ์ํ ์ ์์ต๋๋ค.
i18next ์ฌ์ฉ ์์
import i18next from 'i18next';
import { useTranslation } from 'react-i18next';
i18next.init({
resources: {
en: {
translation: {
'error.generic': '๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋์ค์ ๋ค์ ์๋ํด์ฃผ์ธ์.',
'error.network': '๋คํธ์ํฌ ์ค๋ฅ. ์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํด์ฃผ์ธ์.',
},
},
fr: {
translation: {
'error.generic': '์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋์ค์ ๋ค์ ์๋ํด ์ฃผ์ธ์.',
'error.network': '๋คํธ์ํฌ ์ค๋ฅ. ์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํด ์ฃผ์ธ์.',
},
},
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false, // react๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด์ค์ผ์ดํ ์ฒ๋ฆฌํ๋ฏ๋ก ํ์ํ์ง ์์
},
});
function ErrorFallback({ error }) {
const { t } = useTranslation();
let errorMessageKey = 'error.generic';
if (error instanceof NetworkError) {
errorMessageKey = 'error.network';
}
return (
{t('error.generic')}
{t(errorMessageKey)}
);
}
function ErrorBoundary({ children }) {
const [hasError, setHasError] = useState(false);
const [error, setError] = useState(null);
static getDerivedStateFromError = (error) => {
// ๋ค์ ๋ ๋๋ง์์ ๋์ฒด UI๋ฅผ ํ์ํ๋๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธ
// return { hasError: true }; // ์ด ์ฝ๋๋ ํ
๊ณผ ํจ๊ป ๊ทธ๋๋ก ์๋ํ์ง ์์
setHasError(true);
setError(error);
}
if (hasError) {
// ์ด๋ค ์ปค์คํ
๋์ฒด UI๋ ๋ ๋๋งํ ์ ์์ต๋๋ค
return ;
}
return children;
}
export default ErrorBoundary;
์ด ์์ ์์๋ i18next๋ฅผ ์ฌ์ฉํ์ฌ ์์ด์ ํ๋์ค์ด ๋ฒ์ญ์ ๊ด๋ฆฌํฉ๋๋ค. ErrorFallback ์ปดํฌ๋ํธ๋ useTranslation ํ
์ ์ฌ์ฉํ์ฌ ํ์ฌ ์ธ์ด์ ๋ฐ๋ผ ์ ์ ํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ์ด๋ฅผ ํตํด ์ฌ์ฉ์๋ ์ ํธํ๋ ์ธ์ด๋ก ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ณผ ์ ์์ด ์ ๋ฐ์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํต๋๋ค.
๊ฒฐ๋ก
React ErrorBoundary ์ปดํฌ๋ํธ๋ ๊ฒฌ๊ณ ํ๊ณ ์ฌ์ฉ์ ์นํ์ ์ธ React ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐ ์ค์ํ ๋๊ตฌ์
๋๋ค. Error Boundary๋ฅผ ๊ตฌํํจ์ผ๋ก์จ ์ค๋ฅ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์
์ถฉ๋์ ๋ฐฉ์งํ๋ฉฐ, ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ ์ ์์ต๋๋ค. Error Boundary์ ์๋ฆฌ๋ฅผ ์ดํดํ๊ณ , ์ธ๋ถํ๋ Error Boundary, ์ค๋ฅ ๋ก๊น
, ์ปค์คํ
๋์ฒด UI์ ๊ฐ์ ๊ณ ๊ธ ์ ๋ต์ ๊ตฌํํ๋ฉฐ, ์ผ๋ฐ์ ์ธ ํจ์ ์ ํผํจ์ผ๋ก์จ, ์ ์ธ๊ณ ์ฌ์ฉ์์ ์๊ตฌ๋ฅผ ์ถฉ์กฑํ๋ ๋ ํ๋ ฅ์ ์ด๊ณ ์ ๋ขฐํ ์ ์๋ React ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ์ง์ ์ผ๋ก ํฌ๊ด์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๊ธฐ ์ํด ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํ ๋ ๊ตญ์ ํ์ ํ์งํ๋ฅผ ๊ณ ๋ คํ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค. ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณต์ก์ฑ์ด ๊ณ์ ์ฆ๊ฐํจ์ ๋ฐ๋ผ, ๊ณ ํ์ง ์ํํธ์จ์ด๋ฅผ ๊ตฌ์ถํ๋ ๊ฐ๋ฐ์์๊ฒ ์ค๋ฅ ์ฒ๋ฆฌ ๊ธฐ์ ์ ๋ง์คํฐํ๋ ๊ฒ์ ์ ์ ๋ ์ค์ํด์ง ๊ฒ์
๋๋ค.