Error Boundary ํ†บ์•„๋ณด๊ธฐ ๐Ÿ”Ž

์šฐํ˜ยท2024๋…„ 8์›” 19์ผ
15

React

๋ชฉ๋ก ๋ณด๊ธฐ
4/10

Error Boundary๋ž€?

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ Œ๋”๋ง ๋„์ค‘ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ๋ฆฌ์•กํŠธ๋Š” ํ™”๋ฉด์—์„œ ํ•ด๋‹น UI๋ฅผ ์ œ๊ฑฐํ•˜๋Š”๋ฐ, ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด UI ์ผ๋ถ€๋ฅผ Error Bounary๋กœ ๊ฐ์‹ธ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€์™€ ๊ฐ™์€ fallback UI๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.
โ†ช ๋ฆฌ์•กํŠธ ํŒŒ์ด๋ฒ„ ํŠธ๋ฆฌ์— ๋Œ€ํ•œ ์„ ์–ธ์  try...catch์ด๋‹ค.

๋งŒ์•ฝ Error Boundary๊ฐ€ ์—†๋‹ค๋ฉด ์—๋Ÿฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋กœ ์ „ํŒŒ๋˜์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค์šด๋  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์•กํŠธ ํŒ€์€ ์—๋Ÿฌ์— ๊ด€๋ จ๋œ UI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์•„์˜ˆ ์•„๋ฌด๊ฒƒ๋„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋” ๋‚˜์˜๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ Error Boundary๋ฅผ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•˜์˜€๋‹ค.


์ฃผ์š” ํŠน์ง•

  1. ์—๋Ÿฌ ํฌ์ฐฉ: Error Boundary๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ํฌ์ฐฉํ•˜์—ฌ, ํ•ด๋‹น ์—๋Ÿฌ๋กœ ์ธํ•ด ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค์šด๋˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

  2. ๋Œ€์ฒด UI ์ œ๊ณต: ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, Error Boundary๋Š” fallback UI๋ฅผ ๋ Œ๋”๋งํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ์•Œ๋ฆฌ๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

  3. ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ: componentDidCatch(error, info), static getDerivedStateFromError(error) ๋‘ ๊ฐ€์ง€ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

    • static getDerivedStateFromError(error): UI๊ฐ€ ๊ทธ๋ ค์ง€๊ธฐ ์ „์— ํ˜ธ์ถœ๋˜๋ฉฐ ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œ์ผœ fallback UI๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.

    • componentDidCatch(error, info): UI๊ฐ€ ๊ทธ๋ ค์ง„ ํ›„ ํ˜ธ์ถœ๋˜๋ฉฐ ์—๋Ÿฌ๋ฅผ ๊ธฐ๋กํ•˜๊ฑฐ๋‚˜ ํ›„์† ์ž‘์—…(์—๋Ÿฌ ๋ฆฌํฌํŒ… ์„œ๋น„์Šค ๋กœ๊ทธ ์ „์†ก ๋“ฑ)์„ ํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๋‹ค์Œ ๋ Œ๋”๋ง์— fallback UI ํ‘œ์‹œ
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // ์—๋Ÿฌ๋ฅผ ๊ธฐ๋กํ•˜๊ฑฐ๋‚˜ ํ›„์† ์ž‘์—…(์—๋Ÿฌ ๋ฆฌํฌํŒ… ์„œ๋น„์Šค ๋กœ๊ทธ ์ „์†ก ๋“ฑ) ์ง„ํ–‰
    logErrorToMyService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // ์ปค์Šคํ…€ fallback UI๋ฅผ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋‹ค.
      return this.props.fallback;
    }

    return this.props.children;
  }
}

๐Ÿ’ก ํ˜„์žฌ Error Boundary๋ฅผ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—†๋‹ค.
ํ•˜์ง€๋งŒ Error Boundary๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  react-error-boundary ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
โ†ช ๋ฆฌ์•กํŠธ ๊ณต์‹ ๋ฌธ์„œ(v18.3.1)


๋‚ด๋ถ€ ๋™์ž‘์›๋ฆฌ

  1. ์—๋Ÿฌ ๋ฐœ์ƒ: ์ปดํฌ๋„ŒํŠธ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ฆฌ์•กํŠธ๋Š” ํ•ด๋‹น ์—๋Ÿฌ๋ฅผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์—๋Ÿฌ๋ฅผ ๋ฒ„๋ธ”๋ง(bubbling)ํžˆ์—ฌ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์‹œ๋„ํ•œ๋‹ค.

  2. ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary ์ฐพ๊ธฐ: ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary๋ฅผ ์ฐพ๋Š”๋‹ค. ์ด Error Boundary๋Š” ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  Fallback UI๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

  3. ์ปค์„œ ์ด๋™: ๋ฆฌ์•กํŠธ๋Š” ํ˜„์žฌ ์ž‘์—…ํ•˜๊ณ  ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์˜ current ์ปค์„œ๋ฅผ Error Boundary๋กœ ์ด๋™ํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ’ก current ์ปค์„œ: ๋ฆฌ์•กํŠธ๊ฐ€ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ถ”์ ํ•˜๊ณ  ์žˆ๋Š” ํฌ์ธํ„ฐ์ด๋‹ค.

  1. ShouldCapture ํ”Œ๋ž˜๊ทธ: ์–ธ์™€์ด๋”ฉ ๊ณผ์ •์—์„œ ShouldCapture๋ผ๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ DidCapture๋กœ ๋ฐ”๋€๋‹ค. ์ด๊ฒƒ์€ Error Boundary๊ฐ€ ์—๋Ÿฌ๋ฅผ ์บก์ณํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

  2. Error Boundary์—์„œ ๋ฆฌ๋ Œ๋”๋ง ์‹œ๋„: Error Boundary์— ๋„๋‹ฌํ•œ ํ›„, ๋ฆฌ์•กํŠธ๋Š” ํ•ด๋‹น ๊ฒฝ๊ณ„์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ Error Boundary๋Š” ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋Œ€์ฒด UI๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.

  3. ์™„๋ฃŒ ์ž‘์—…: ์ด๋Ÿฌํ•œ ์–ธ์™€์ด๋”ฉ ๊ณผ์ •์€ completeUnitOfWork()๋ผ๋Š” ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ์—์„œ ์ด๋ฃจ์–ด์ง„๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๊ฐ ํŒŒ์ด๋ฒ„(๋ฆฌ์•กํŠธ์˜ ๊ฐ€์ƒ DOM ๋‹จ์œ„)์— ๋Œ€ํ•œ ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๐Ÿ’ก ์–ธ์™€์ด๋”ฉ ๊ณผ์ •: ๋ฆฌ์•กํŠธ์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary(์—๋Ÿฌ ๊ฒฝ๊ณ„)๋กœ ์ด๋™ํ•˜์—ฌ ํ•ด๋‹น ๊ฒฝ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์„ ์˜๋ฏธํ•œ๋‹ค.

๐Ÿ“ ์ •๋ฆฌํ•˜๊ธฐ

  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary๋ฅผ ์ฐพ๊ณ , ํ•ด๋‹น ๊ฒฝ๊ณ„๋กœ ์ปค์„œ๋กœ ์ด๋™์‹œํ‚จ๋‹ค.

  • ShouldCaptureํ”Œ๋ž˜๊ทธ๊ฐ€ DidCapture๋กœ ๋ฐ”๋€Œ๋ฉด์„œ Error Boundary๊ฐ€ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

  • ์ด ๋ชจ๋“  ๊ณผ์ •์€ completeUnitOfWork() ๋ฉ”์„œ๋“œ์—์„œ ๊ด€๋ฆฌ๋˜์–ด, ์—๋Ÿฌ ๊ด€๋ฆฌ๊ฐ€ ์ฒด๊ณ„์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๋„๋ก ํ•œ๋‹ค.


ErrorBoundary๊ฐ€ ํฌ์ฐฉํ•˜์ง€ ๋ชปํ•˜๋Š” ์—๋Ÿฌ๋“ค

  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
  • ๋น„๋™๊ธฐ ์ฝ”๋“œ(setTimeout, fetch ๋“ฑ)
  • ์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง
  • Error Boundary ์ž์ฒด์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, ๋น„๋™๊ธฐ ์ฝ”๋“œ

import * as React from "react";
import { createRoot } from 'react-dom/client'

function MainComponents(){
    return (
    <ErrorBoundary>
      <TestComponents />
    </ErrorBoundary>
  );
}

function TestComponents() {
  return (
   <button type="button" onClick={() => {
      throw new Error("์—๋Ÿฌ ๋ฐœ์ƒ!")
    }}>
    ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฒ„ํŠผ
   </button>
  );
}

const root = createRoot(document.querySelector('#root'))
root.render(<MainComponents/>)

์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์„œ ์—๋Ÿฌ๋ฅผ throwํ•˜์ง€๋งŒ ๋ฆฌ์•กํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Error Boundary๋Š” ์—๋Ÿฌ๋ฅผ ํฌ์ฐฉํ•˜์ง€ ๋ชปํ•œ๋‹ค.

  • ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฒ„ํŠผ์„ ์—ฌ๋Ÿฌ๋ฒˆ ํด๋ฆญํ•ด๋„ ์•„๋ฌด๋Ÿฐ ๋ฐ˜์‘์ด ์—†๋‹ค

๋งŒ์•ฝ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋‚˜ ๋น„๋™๊ธฐ ์ฝ”๋“œ์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ Error Boundary์—์„œ ํฌ์ฐฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ†ตํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œ์ผœ ์—๋Ÿฌ๋ฅผ throwํ•˜๋ฉด ๋œ๋‹ค.

import * as React from "react";
import { useState } from "react";
import { createRoot } from 'react-dom/client'

function MainComponents(){
    return (
    <ErrorBoundary>
      <TestComponents />
    </ErrorBoundary>
  );
}

function TestComponents() {
  const [isError, setIsError] = useState(false);

  if(isError){
    throw new Error("์—๋Ÿฌ ๋ฐœ์ƒ!")
  }

  return (
   <button type="button" onClick={() => setIsError(true)}>
    ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฒ„ํŠผ
   </button>
  );
}
  • ๋ฆฌ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ์—๋Ÿฌ๋ฅผ throwํ•˜๊ธฐ ๋•Œ๋ฌธ์— fallback UI๊ฐ€ ๋ Œ๋”๋ง๋œ๋‹ค.

์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง

Error Boundary๋Š” getDerivedStateFromError() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ๋™์ž‘ํ•˜๋Š”๋ฐ, ์ด ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋Š” ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์—์„œ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ๋ Œ๋”๋ง์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” Error Boundary๊ฐ€ ํฌ์ฐฉํ•  ์ˆ˜ ์—†๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ์„œ๋ฒ„ ๋ Œ๋”๋ง ์ค‘์— ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋Š” ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋ฉฐ, Error Boundary๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ๋ Œ๋”๋ง ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ณ  ์žˆ๋‹ค.

Error Boundary ์ž์ฒด์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜

Error Boundary๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ž์‹ ์ด ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ํฌ์ฐฉํ•˜๋Š” ๊ฒƒ์ด ์ฃผ๋œ ๋ชฉ์ ์ด๋ฉฐ, Error Boundary ์ž์ฒด์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋Š” Error Boundary์˜ ๋ณดํ˜ธ๋ฅผ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.


Error Boundary์˜ ์žฅ์ 

  1. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX) ๊ฐœ์„ 
    Error Boundary๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋กœ ์ „ํŒŒ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜์—ฌ, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์„ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ๋‚˜์€ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•œ๋‹ค.

  2. ์˜ค๋ฅ˜ ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง
    componentDidCatch() ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•˜๊ฑฐ๋‚˜ ์™ธ๋ถ€ ๋ชจ๋‹ˆํ„ฐ๋ง ์„œ๋น„์Šค์— ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ณ  ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

  3. ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ
    ๊ฐ Error Boundary๋Š” ํŠน์ • ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋งŒ ๊ฐ์‹ธ๋„๋ก ์„ค์ •ํ•˜์—ฌ ํ•„์š”ํ•œ ๋ถ€๋ถ„์—์„œ๋งŒ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ๋ชจ๋“ˆ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์ด ํ–ฅ์ƒ๋œ๋‹ค.

  4. ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜(DX) ํ–ฅ์ƒ
    ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ fallback UI๋‚˜ ์‹คํ–‰ํ•ด์•ผ ํ•  ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด, ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ง€ ๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ณ , ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ํ•˜์—ฌ ๋ฌด์—‡์„ ํ•ด์•ผํ•˜๋Š”์ง€ ๋ช…ํ™•ํžˆ ํ‘œํ˜„ํ•˜์—ฌ ๋ณต์žกํ•œ ํ๋ฆ„์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.


Tanstack Query(React Query)์—์„œ ErrorBoundary ์‚ฌ์šฉํ•˜๊ธฐ

์œ„์—์„œ ๋น„๋™๊ธฐ ์ฝ”๋“œ์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ Error Boundary์—์„œ ํฌ์ฐฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ†ตํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•œ๋‹ค๊ณ  ์„ค๋ช…ํ–ˆ๋Š”๋ฐ, Tanstack Query(React Query)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด throwOnError ์˜ต์…˜์„ ์„ค์ •ํ•˜์—ฌ ๋ณ„๋„์˜ ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์—†์ด Error Boundary์—์„œ ์—๋Ÿฌ๋ฅผ ํฌ์ฐฉํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก throwOnError ๋ž€ ๋ฌด์—‡์ผ๊นŒ?
Tanstack Query์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ์˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
์ด ๊ฐ’์„ true๋กœ ์„ค์ •ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ Œ๋”๋ง ๋‹จ๊ณ„์—์„œ ๋ฐœ์ƒํ•˜์—ฌ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary๋กœ ์ „ํŒŒ๋œ๋‹ค.

const fetchTodo = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  throw new Error("์—๋Ÿฌ ๋ฐœ์ƒ");
};

export default function Home() {
  return (
    <>
      <ErrorBoundary fallback={<div>์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค</div>}>
        <TestComponent throwOnErrorOption={true} /> // throwOnError: true
      </ErrorBoundary>
      <ErrorBoundary fallback={<div>์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค</div>}>
        <TestComponent throwOnErrorOption={false} /> // throwOnError: false
      </ErrorBoundary>
    </>
  );
}

function TestComponent({ throwOnErrorOption }: { throwOnErrorOption: boolean }) {
  const { data, refetch } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodo,
    throwOnError: throwOnErrorOption,
    enabled: false, // fetch button์„ ๋ˆŒ๋Ÿฌ์•ผ๋งŒ fetch๊ฐ€ ์‹คํ–‰๋จ
  });

  return (
    <div>
      <h1>Error Boundary</h1>
      <h2>throwOnError: {`${throwOnErrorOption}`}</h2>
      <button type="button" onClick={() => refetch()}>
        fetch button
      </button>
      {data && <div>{data.title}</div>}
    </div>
  );
}
  • throwOnError๊ฐ€ true๋กœ ์„ค์ •๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ErrorBoundary๋กœ ์—๋Ÿฌ๊ฐ€ ์ „ํŒŒ๋œ๋‹ค!

๐Ÿ™ƒ ๋„์›€์ด ๋˜์—ˆ๋˜ ์ž๋ฃŒ๋“ค

error boundary๋กœ ๋ Œ๋”๋ง ์˜ค๋ฅ˜ ํฌ์ฐฉํ•˜๊ธฐ - React ๊ณต์‹ ๋ฌธ์„œ(v18.3.1)
Error Boundaries - React ๊ณต์‹ ๋ฌธ์„œ(v18.2.0)
ErrorBoundary ์„ค๋ช…ํšŒ
How does ErrorBoundary work internally in React?

0๊ฐœ์˜ ๋Œ“๊ธ€