๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
React

[React] ErrorBoundary & Suspense, ๊ฑฐ์˜ ์™„๋ฒฝํ•œ ์‚ฌ์šฉ๋ฐฉ๋ฒ• ๊ฐ€์ด๋“œ

by LasBe 2024. 6. 4.
๋ฐ˜์‘ํ˜•

๐Ÿ“’ ErrorBoundary & Suspense, ๊ฑฐ์˜ ์™„๋ฒฝํ•œ ์‚ฌ์šฉ๋ฐฉ๋ฒ• ๊ฐ€์ด๋“œ


์ž˜ ๋งŒ๋“  Errorboundary
๋ฐฑ๊ฐœ์˜ try-catch ์•ˆ ๋ถ€๋Ÿฝ๋‹ค.

ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์„ ๊ณ„์†ํ•˜๋‹ค ๋ณด๋‹ˆ ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์„ ๋งˆ์ฃผ์น˜๊ฒŒ ๋˜์—ˆ๊ณ , ๊ทธ์— ๋”ฐ๋ฅธ ์ ์ ˆํ•œ ํ™”๋ฉด ํ‘œํ˜„์˜ ์ค‘์š”์„ฑ์„ ์ ์ฐจ ๋Š๊ปด๊ฐ”์Šต๋‹ˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋กœ๋”ฉ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ์žˆ์ฃ .

๋Š์ž„์—†์ด ์‚ฌ์šฉํ•ด ๋ณด๊ณ  ๊ฐœ์„ ํ•ด ๋ณด๋‹ˆ 1๋…„์ด ์ง€๋‚œ ์ง€๊ธˆ์—์„œ์•ผ ์–ด๋Š ์ •๋„ ๊ฐ์ด ์žกํžˆ๋Š” ๊ฑฐ ๊ฐ™์•„ ๊ทธ ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์€ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ์˜ ์ ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๋Š” ๊ธ€์ด๊ธฐ ๋•Œ๋ฌธ์— ErrorBoundary์™€ Suspense์— ๋Œ€ํ•œ ๊ฐœ๋…์€ ์•„๋ž˜ ๊ธ€์„ ์ฐธ์กฐํ•ด ์ฃผ์„ธ์š”.

ErrorBoundary์™€ Suspense๋ฅผ ์‹ค์ œ ํ”„๋กœ๋•ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ์†Œ๊ฐœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

๐Ÿ“Œ ์™œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€

์‚ฌ์‹ค “์™œ” ์‚ฌ์šฉํ•˜๋Š”์ง€๊ฐ€ ๊ฐ€์žฅ ์ค‘์š”ํ•˜๊ฒ ์ฃ .

ํ•œ ์ค„๋กœ ์š”์•ฝํ•˜์ž๋ฉด “ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , ์„ ์–ธ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ”์ž…๋‹ˆ๋‹ค.

๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž ๋ฏธ์‚ฌ์šฉ ์˜ˆ์ œ

function MyComponent() {
  // ๋ฐ์ดํ„ฐ์™€ ๋กœ๋”ฉ, ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” state๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
    const fetchData = async () => {
      setIsLoading(true); // ๋กœ๋”ฉ ์‹œ์ž‘
      try {
        const result = await getData()
        );
        setData(result); // ๋ฐ์ดํ„ฐ ์„ค์ •
        setError(null); // ์˜ค๋ฅ˜ ์ดˆ๊ธฐํ™”
      } catch (error) {
        setError(error); // ์˜ค๋ฅ˜ ์„ค์ •
      } finally {
        setIsLoading(false); // ๋กœ๋”ฉ ์ข…๋ฃŒ
      }
    };

    fetchData();
  }, []);

  // ๋กœ๋”ฉ ์ค‘์ผ ๋•Œ ๋กœ๋”ฉ ํ‘œ์‹œ, ์—๋Ÿฌ๊ฐ€ ์žˆ์„ ๋•Œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๊ณ ,
  // ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  return (
    <div>
      {isLoading ? (
        <div>Loading...</div>
      ) : error ? (
        <div>Error: {error.message}</div>
      ) : (
        <div>{data}</div>
      )}
    </div>
  );
}

export default MyComponent;

 

ํ•œ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ useEffect๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ ,
try-catch์™€ ๊ฐ๊ฐ์˜ ์ƒํƒœ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ์™€ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ชจ์Šต์€ ๋ณด๊ธฐ๋งŒ ํ•ด๋„ ์–ด์ง€๋Ÿฝ์ฃ .

 

๊ทธ ์ด์œ ๋Š” ํ•œ ์ปดํฌ๋„ŒํŠธ์— ๋„ˆ๋ฌด๋‚˜ ๋งŽ์€ ์—ญํ• ์ด ๋ถ€๋‹ด๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

์ด๋Ÿฌํ•œ ์—ญํ• ๋“ค์„ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹น์—ฐํžˆ ๊ทธ ์ปดํฌ๋„ŒํŠธ ๋‚ด ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๊ณ ,
์ด๋“ค์€ ํ•œ๋ฐ ๋’ค์„ž์—ฌ ๊ฐ€๋…์„ฑ์„ ๋ง์ณ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋Ÿฌํ•œ ์—ญํ• ๋“ค์„ ๋ถ„๋ฆฌํ•œ๋‹ค๋ฉด ์–ด๋–ค ๋ชจ์Šต์ผ๊นŒ์š”?

๐Ÿ”Ž ์—ญํ•  ๋ถ„๋ฆฌ

<ErrorBoundary FallbackComponent={<span>์—๋Ÿฌ ๋ฐœ์ƒ</span>}> // ์—๋Ÿฌ์ƒํƒœ ๋ถ„๋ฆฌ
  <Suspense fallback={<span>๋กœ๋”ฉ์ค‘...</span>}> // ๋กœ๋”ฉ์ƒํƒœ ๋ถ„๋ฆฌ
    <MyComponent />
  </Suspense>
</ErrorBoundary>
function MyComponent() {
  const { data } = useSuspenseQuery({ queryKey: ['data'], queryFn: getData })
  return <div>{data}</div>
}

export default MyComponent;

 

์šฐ์„  ๋กœ๋”ฉ์ƒํƒœ๊ฐ€ ๊ฐ์ง€๋˜์—ˆ์„ ๋• Suspense๊ฐ€ ๋กœ๋”ฉ์ƒํƒœ์— ๋Œ€ํ•œ ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•˜๊ณ ,

์—๋Ÿฌ์ƒํƒœ๊ฐ€ ๊ฐ์ง€ ๋˜์—ˆ์„ ๋• ErrorBoundary๊ฐ€ ์—๋Ÿฌ์ƒํƒœ์— ๋Œ€ํ•œ ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด MyComponent์—์„œ๋Š” ๋กœ๋”ฉ, ์—๋Ÿฌ ์ƒํƒœ์— ๋Œ€ํ•œ ๋กœ์ง์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์–ด,

์ •์‹ ์—†๋Š” ์ƒํƒœ๊ด€๋ฆฌ์™€ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์ด ์‚ฌ๋ผ์ง€๊ฒŒ ๋˜์–ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋งŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๊ฒŒ๋‹ค๊ฐ€ Suspense์™€ ErrorBoundary๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์„œ๋ฒ„ ์ƒํƒœ๊ด€๋ฆฌ ๋„๊ตฌ์ธ tanstack-query(react-query)์— ์˜ํ•ด useEffect ๊ฐ™์€ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๊นŒ์ง€ ์‚ฌ๋ผ์ง€๋‹ˆ ๋ง๋„ ์•ˆ ๋˜๊ฒŒ ๊น”๋”ํ•ด์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์„ค๊ณ„ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•˜๊ธฐ ์ „ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ ์‚ฌ์ „ ์„ค์น˜

$ npm i @tanstack/react-query react-error-boundary react-toastify
  • tanstack-query (react-query)

์š”์ฆ˜์—๋Š” ๊ฑฐ์˜ ๋Œ€์„ธ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ฃ .

์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ๋ฟ ์•„๋‹ˆ๋ผ Loading ์ƒํƒœ๋ฅผ throw ํ•ด Suspense๊ฐ€ catch ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ refetch ํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•˜๋‹ˆ ํ•„์ˆ˜์ ์œผ๋กœ ์„ค์น˜ํ•ด ์ค๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” v5 ๋ฒ„์ „์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

  • react-error-boundary

ErrorBoundary๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ•๋ ฅํ•œ ์ƒ๋ช…์ฃผ๊ธฐ ๊ธฐ๋Šฅ์„ ํ•„์ˆ˜์ ์œผ๋กœ ์ด์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šคํ˜•์œผ๋กœ ์ง์ ‘ ErrorBoundary๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ๊ท€์ฐฎ์œผ๋‹ˆ ์ž˜ ๋งŒ๋“ค์–ด์ง„ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

 

  • react-toastify

๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์—๋Ÿฌ ์•Œ๋ฆผ์„ ํ‘œํ˜„ํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ์—๋Ÿฌ ์ƒํƒœ์˜ ์ข…๋ฅ˜ ๋ถ„๋ฅ˜ํ•˜๊ธฐ

๋กœ๋”ฉ์ด์•ผ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ๋ฐ›๊ธฐ ์ „, ๋ฐ›์€ ํ›„๋กœ๋งŒ ๋‚˜๋‰˜์–ด์„œ ๋‹ฌ๋ฆฌ ์ƒ๊ฐํ•  ๊ฒƒ์ด ์—†์ง€๋งŒ ์—๋Ÿฌ ์ƒํƒœ๋Š” ๊ทธ๋ ‡์ง€ ์•Š์ฃ .

๊ทธ๋ž˜์„œ ์ €๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ถ„๋ฆฌํ•ด์„œ ๊ตฌ๋ถ„ํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž JavaScript ์—๋Ÿฌ

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปดํŒŒ์ผ ์‹œ์ ์— ์—๋Ÿฌ๋ฅผ ์žก์•„๋‚ด๋”๋ผ๋„ 100% ์™„๋ฒฝํ•˜๊ฒŒ ํ•  ์ˆœ ์—†์Šต๋‹ˆ๋‹ค.

์ฐธ์กฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๊ฐ€ ์ตœ์ƒ์œ„ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊นŒ์ง€ ์ „ํŒŒ๋˜๋ฉด ์•ฑ์ด crush ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ error.message๋กœ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค„ ํ•„์š”๊ฐ€ ์—†๊ธฐ์— default message๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž ํ•ฉ์˜๋œ ์—๋Ÿฌ (error.response.data)

http status๊ฐ€ ์—๋Ÿฌ ์ƒํƒœ์—ฌ๋„ Error ๊ฐ์ฒด์˜ error.response.data๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ํŠน์ •ํ•œ ์ƒํ™ฉ์—์„œ์˜ ์—๋Ÿฌ๋ฅผ ์„œ๋ฒ„ ์ธก๊ณผ ์ •์˜ํ•ด ์–ด๋–ค ์—๋Ÿฌ์ธ์ง€ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์„œ๋ฒ„์ธก๊ณผ ์–ด๋–ค ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ์ „์†กํ•  ๊ฒƒ์ธ์ง€, ์–ด๋–ค ์ฝ”๋“œ๊ฐ€ ์–ด๋– ํ•œ ์˜๋ฏธ์ธ์ง€๋ฅผ ํ•ฉ์˜ํ•œ ํ›„ ๊ณ„์†ํ•ด์„œ ๋‹ค๋“ฌ๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์ข‹์€ ์˜ˆ์‹œ๋กœ ์นด์นด์˜ค ์‡ผํ•‘์—์„œ ๊ณต๊ฐœํ•œ ์—๋Ÿฌ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ํ•ฉ์˜๋œ ์—๋Ÿฌ ์ฝ”๋“œ๋กœ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‘๋‹ต ์ฝ”๋“œ ๋ฐ ์—๋Ÿฌ ์ฝ”๋“œ

๐Ÿ”Ž http ์š”์ฒญ ์—๋Ÿฌ (error.response)

์„œ๋ฒ„ ์ธก์—์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๊ฐ€ ๋˜์–ด์žˆ์ง€ ์•Š์•„ error.response.data๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ๊ฐœ๋ฐœ ์‹œ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์˜ˆ์™ธ ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•ด ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ด์ƒ์ ์ด๋‚˜ ํ˜„์‹ค์ ์œผ๋กœ 100% ์™„๋ฒฝํ•œ ์ƒํ™ฉ์€ ์—†์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ๋Š” error.response.status ์˜ HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด ์ƒํ™ฉ์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

์ƒํƒœ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ์„ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

HTTP ์ƒํƒœ ์ฝ”๋“œ - HTTP | MDN

๐Ÿ”Ž ์‘๋‹ต์ด ์—†๋Š” ์—๋Ÿฌ (error)

์ด ๊ฒฝ์šฐ๋Š” ์ฃผ๋กœ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋‚˜ ์„œ๋ฒ„๊ฐ€ ๋‹ค์šด๋˜์–ด ์•„๋ฌด๋Ÿฐ ์‘๋‹ต์„ ๋ฐ›์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์„ค์ •ํ•œ ํƒ€์ž„์•„์›ƒ์ด ์ดˆ๊ณผ๋˜๊ฑฐ๋‚˜ DNS, CORS์— ์˜ํ•ด์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ error.code๋กœ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์‹ค์ƒ ์ตœํ›„์˜ ๊ด€๋ฌธ์ด๋ผ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ์š”์ฒญ์— ๋Œ€ํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” ์ด์ชฝ ๋‹จ๊ณ„์—์„œ ๋งˆ๋ฌด๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž ์—๋Ÿฌ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ

import { AxiosError } from 'axios';

type ErrorCodeType = {
  [key: string]: { code: string; message: string; requireLogin?: boolean };
};

export const ERROR_CODE: ErrorCodeType = {
  default: { code: 'ERROR', message: '์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.' },

  // axios error
  ERR_NETWORK: { code: 'ํ†ต์‹  ์—๋Ÿฌ', message: '์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. \nํ”„๋กœ๊ทธ๋žจ์„ ์žฌ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌ์ž์—๊ฒŒ ์—ฐ๋ฝํ•˜์„ธ์š”.' },
  ECONNABORTED: { code: '์š”์ฒญ ์‹œ๊ฐ„ ์ดˆ๊ณผ', message: '์š”์ฒญ ์‹œ๊ฐ„์„ ์ดˆ๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค.' },

  // http status code ๋ฐ ์ •์˜ ๋œ ์ฝ”๋“œ
  400: { code: '400', message: '์ž˜๋ชป๋œ ์š”์ฒญ.' },
  4001: { code: '4001', message: '์š”์ฒญ์— ๋Œ€ํ•œ Validation ์—๋Ÿฌ์ž…๋‹ˆ๋‹ค.' },
  401: { code: '401', message: '์ธ์ฆ ์—๋Ÿฌ.', requireLogin: true },
  4011: { code: '4011', message: '์ธ์ฆ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.', requireLogin: true },
  403: { code: '403', message: '๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.' },
} as const;

export const getErrorDataByCode = (error: AxiosError<{ code: number; message: string }>) => {
  const serverErrorCode = error?.response?.data?.code ?? '';
  const httpErrorCode = error?.response?.status ?? '';
  const axiosErrorCode = error?.code ?? '';
  if (serverErrorCode in ERROR_CODE) {
    return ERROR_CODE[serverErrorCode as keyof typeof ERROR_CODE];
  }
  if (httpErrorCode in ERROR_CODE) {
    return ERROR_CODE[httpErrorCode as keyof typeof ERROR_CODE];
  }
  if (axiosErrorCode in ERROR_CODE) {
    return ERROR_CODE[axiosErrorCode as keyof typeof ERROR_CODE];
  }
  return ERROR_CODE.default;
};

 

์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์—๋Ÿฌ๋“ค์„ ํ•œ ๊ณณ์— ์ •์˜ํ•ด ๋‘๊ณ  ์ผ๋ฐ˜ํ™”ํ•˜์—ฌ ์–ด๋””์„œ๋“  ๋™์ผํ•œ ์—๋Ÿฌ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ฐ›๋„๋ก ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ตœ๋Œ€ํ•œ Type Safety ํ•˜๊ฒŒ ์—๋Ÿฌ ์ •๋ณด์— ์ ‘๊ทผํ•˜๋ฉฐ, ๋ชจ๋“  ์—๋Ÿฌ ์ •๋ณด๋ฅผ ํ•œ ๊ณณ์— ๋ชจ์•„๋‘๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ์—๋Ÿฌ ์ •๋ณด๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋Š” ERROR_CODE๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

return ํ•ด์ฃผ๋Š” ๋ฐ์ดํ„ฐ๋Š” [ํ•ฉ์˜๋œ ์—๋Ÿฌ] > [http status] > [axios error] ์ˆœ์œผ๋กœ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋‘ก๋‹ˆ๋‹ค.

 

์ธ์ฆ ์ •๋ณด ๊ด€๋ จ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋• requireLogin ์†์„ฑ์„ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์žฌ๋กœ๊ทธ์ธํ•˜๋„๋ก ์œ ๋„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•ด ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋’ค์—์„œ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ์—๋Ÿฌ ํ‘œํ˜„ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ฅธ QueryClient ์„ธํŒ…

์—๋Ÿฌ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋งŽ์ด ์žˆ๊ฒ ์ง€๋งŒ ์ €๋Š” 2๊ฐ€์ง€๋กœ ๋‚˜๋ˆ„๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž ๋ฐ์ดํ„ฐ ํ‘œํ˜„(useQuery, useSuspenseQuery) → Fallback UI

get๊ณผ ๊ฐ™์ด ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ ํ‘œํ˜„ํ•˜๋Š” ํ™”๋ฉด์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋Š” ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์• ์ดˆ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์ง€ ๋ชปํ–ˆ์„ ๊ฒฝ์šฐ์— ๋นˆ ํ™”๋ฉด์„ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ๋ณด๋‹จ ์‚ฌ์šฉ์ž์—๊ฒŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ(useMutation) → Toast Message

์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญ๊ณผ ๊ฐ™์€ ์ƒํ˜ธ์ž‘์šฉ์„ ํ†ตํ•ด ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€๋กœ ์—๋Ÿฌ๋ฅผ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

๋ฒ„ํŠผ์„ ๋ˆŒ๋ €๋Š”๋ฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด ๋Œ€์ฒด UI๋กœ ์ „ํ™˜๋˜๋ฉด ๋‹นํ™ฉ์Šค๋Ÿฌ์šธ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž QueryClient ์„ธํŒ…

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      throwOnError: true,
    },
    mutations: {
      throwOnError: false,
      onError: (error: any) => {
        const errorData = getErrorDataByCode(error);
        toast.error(`[${errorData.code}] ${errorData.message}`);
      },
    },
  },
});

 

react-query์˜ QueryClient๋Š” ์ฟผ๋ฆฌ๋“ค์˜ defaultOptions ์„ธํŒ…์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ฃผ์š”ํ•˜๊ฒŒ ๋ด์•ผ ํ•  ์†์„ฑ์€ throwOnError์ž…๋‹ˆ๋‹ค.

 

throwOnError๋Š” ๊ธฐ๋ณธ๊ฐ’์ด false๋ฉฐ true๋กœ ์„ค์ •ํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ์ „ํŒŒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

queries(useQuery)์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” throw ํ•˜์—ฌ ErrorBoundary๋กœ ๋ฐ›์•„๋ฒ„๋ฆฌ๊ณ ,
mutations(useMutation)์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” ์ „ํŒŒ๋˜์ง€ ์•Š๋„๋ก ์„ค์ • ํ›„ ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€๋งŒ ๋„์›Œ์ค„ ๊ฒ๋‹ˆ๋‹ค.

๐Ÿ“Œ ๋กœ๋”ฉ ๋ฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ Component

๋ณธ๊ฒฉ์ ์ธ ์ปดํฌ๋„ŒํŠธ ์„ค๋ช…์— ์•ž์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์กฐ์™€ ๋ฐฐ์น˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž ๊ตฌ์กฐ

const Boundray = ({ children }: { children: React.ReactElement }) => {
  return (
    <ErrorBoundary>
      <Suspense>
        {children}
      </Suspense>
    </ErrorBoundary>
  );
};
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ Fetching ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • Suspense๋กœ ๋กœ๋”ฉ, ErrorBoundary๋กœ ์—๋Ÿฌ๋ฅผ ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž ๋ฐฐ์น˜ ๋ฐฉ๋ฒ•

export const App = () => {
  return (
    <GlobalBoundary>
      <FetchBoundary>
        <ComponentA />
      </FetchBoundary>

      <FetchBoundary>
        <ComponentB />
      </FetchBoundary>

      <FetchBoundary>
        <ComponentC />
      </FetchBoundary>

      <FetchBoundary>
        <ComponentD />
      </FetchBoundary>
    </GlobalBoundary>
  );
};
  • GlobalBoundary
    • ์ตœ์ƒ๋‹จ์— ๋ฐฐ์น˜ํ•ด ํ™”๋ฉด ์ „์ฒด์— ๋กœ๋”ฉ, ์—๋Ÿฌ ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฏธ์ฒ˜ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•œ ์—๋Ÿฌ์™€ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํก์ˆ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ธ์ฆ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ex) ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™
  • FetchBoundary
    • ๊ฐ์‹ผ ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ๋”ฉ, ์—๋Ÿฌ ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
    • ๋Œ€์ฒด๋กœ useQuery ํ˜น์€ useSuspenseQuery๋ฅผ ์‚ฌ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ์— ๊ฐ์•„์ค๋‹ˆ๋‹ค.

๐Ÿ”Ž ๋ ˆ์ด์•„์›ƒ ์ฃผ์˜์ 

์œ„์™€ ๊ฐ™์ด ๋ฐฐ์น˜ํ•ด ๋‘๋ฉด ๋“ ๋“ ํ•˜๊ฒŒ ๋Œ€์ฒด UI๋ฅผ ํ‘œ์‹œํ•˜์ง€๋งŒ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ๋“ค๊ณผ ๋Œ€์ฒด UI์™€์˜ ์Šคํƒ€์ผ์ด ๋‹ฌ๋ผ ๊ธ‰๊ฒฉํ•˜๊ฒŒ ๋ณ€ํ•˜๊ฒŒ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

  • Boundary ์ปดํฌ๋„ŒํŠธ์— ๋ ˆ์ด์•„์›ƒ ์กฐ์ • ์†์„ฑ์„ Props๋กœ ๋ฐ›๊ธฐ
  • ๋„ˆ๋ฌด ์ข์€ ๊ณต๊ฐ„์— ์œ„์น˜ํ•œ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ ์•„์ด์ฝ˜๊ณผ ํ˜ธ๋ฒ„ ์นด๋“œ๋กœ ๋Œ€์ฒด UIํ‘œ์‹œํ•˜๊ธฐ
  • ์ปดํฌ๋„ŒํŠธ์˜ ๋ ˆ์ด์•„์›ƒ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๋ฐ”๊นฅ์œผ๋กœ ๋นผ๋‚ด๊ธฐ

๋ฐฉ๋ฒ•์€ ์ •๋ง ๋งŽ์œผ๋‹ˆ ์ž…๋ง›์— ๋งž๊ฒŒ ์ž์œ ๋กญ๊ฒŒ ์ปค์Šคํ…€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์˜ ์˜ˆ์ œ์—์„œ๋Š” ๊ธฐ๋Šฅ ์ค‘์‹ฌ์œผ๋กœ ์„ค๋ช…์„ ๋“œ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ถ€๋ถ„์€ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž GlobalBoundary

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { GlobalErrorFallback } from './GlobalErrorFallback';
import { Loading } from '@components/Loading';

export const GlobalBoundary = ({ children }: { children: React.ReactNode }) => {
  return (
    <ErrorBoundary FallbackComponent={GlobalErrorFallback}>
      <Suspense fallback={<Loading />}>{children}</Suspense>
    </ErrorBoundary>
  );
};
import { FallbackProps } from 'react-error-boundary';
import { getErrorDataByCode } from '@components/boundary/getErrorDataByCode';
import { useNavigate } from 'react-router-dom';
import { Button, Container } from '@components/atoms';

export const GlobalErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
  const navigate = useNavigate();
  const navigatePage = (to: string) => {
    // resetErrorBoundary๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™”
    resetErrorBoundary();
    navigate(to);
  };
  const errorData = getErrorDataByCode(error);
  return (
    <Container.Column>
      <h1>{errorData.code}</h1>
      <h2>{errorData.message}</h2>
      <Button onClick={() => navigatePage(errorData.requireLogin ? '/login' : '/main')}>
        {errorData.requireLogin ? '๋กœ๊ทธ์ธ ์ด๋™' : '๋ฉ”์ธํ™”๋ฉด ์ด๋™'}
      </Button>
    </Container.Column>
  );
};

GlobalErrorFallback์— ์ฃผ๋ชฉํ•ด ์ฃผ์„ธ์š”.

 

ErrorBoundary๊ฐ€ catch ํ•œ error ๊ฐ์ฒด์™€ resetErrorBoundary๋ฅผ props๋กœ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ฐ์ดํ„ฐ๋Š” ์œ„์—์„œ ๋งŒ๋“  getErrorDataByCode ํ•จ์ˆ˜์— error ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด์ฃผ์–ด ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

resetErrorBoundary๋Š” ErrorBoundary์—์„œ ๋Œ€์ฒด UI๋ฅผ ๋„์›Œ์ฃผ๋Š” ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

์—๋Ÿฌ ์ƒํƒœ๋ฅผ ํ•ด์†Œ์‹œํ‚ค๊ณ  ๋‹ค๋ฅธ ๋™์ž‘์œผ๋กœ ์ด์–ด๊ฐ€๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœํ•ด ์ฃผ์„ธ์š”.

 

๐Ÿ”Ž FetchBoundary

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { FetchErrorFallback } from './FetchErrorFallback';
import { Loading } from '@components/Loading';

export const FetchBoundary = ({ children }: { children: React.ReactElement }) => {
  return (
    <ErrorBoundary FallbackComponent={FetchErrorFallback}>
      <Suspense fallback={<Loading />}>{children}</Suspense>
    </ErrorBoundary>
  );
};
import { FallbackProps } from 'react-error-boundary';
import { Button, Container } from '../atoms';
import { getErrorDataByCode } from '@components/boundary/getErrorDataByCode';
import { useQueryErrorResetBoundary } from '@tanstack/react-query';

export const FetchErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
  const { reset } = useQueryErrorResetBoundary();
  const errorData = getErrorDataByCode(error);

  // ์ธ์ฆ์ด ํ•„์š”ํ•œ ์—๋Ÿฌ์ผ ๊ฒฝ์šฐ ์ƒ์œ„ Boundary๋กœ Error๋ฅผ ์ „ํŒŒ
  if (errorData.requireLogin) throw error;

  const handleClickReset = () => {
    resetErrorBoundary();
    reset();
  };

  return (
    <Container.Column>
      <h1>{errorData?.code}</h1>
      <h2>{errorData?.message}</h2>
      <Button onClick={handleClickReset}>์žฌ์‹œ๋„</Button>
    </Container.Column>
  );
};
import { FetchBoundary } from '@components/boundary/FetchBoundary';
import { Content } from '@components/Content';
import { Container } from '@components/atoms';

export const Error400 = () => {
  return (
    <>
      <Container.Card>
        <FetchBoundary>
          <Content param="400" />
        </FetchBoundary>
      </Container.Card>
      <Container.Card>
        <FetchBoundary>
          <Content param="4001" />
        </FetchBoundary>
      </Container.Card>
    </>
  );
};

  • ๊ฐ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ๊ฐœ๋ณ„์ ์ธ ๋กœ๋”ฉ ๋ฐ ์—๋Ÿฌ ํ‘œ์‹œ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • useQueryErrorResetBoundary์˜ reset์„ ์‚ฌ์šฉํ•ด ์—๋Ÿฌ ๋ฐœ์ƒ ์ฟผ๋ฆฌ ์žฌํ˜ธ์ถœ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ๋งˆ๋ฌด๋ฆฌ

์“ฐ๋‹ค ๋ณด๋‹ˆ ๊ธ€์ด ๋„ˆ๋ฌด ๊ธธ์–ด์ง„ ๊ฒƒ ๊ฐ™๋„ค์š”.

 

๊ธฐ๋Šฅ์„ ์„ ์–ธ์ ์œผ๋กœ ์ถ”์ƒํ™”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋กœ๋”ฉ, ์—๋Ÿฌ์™€ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ค‘๊ฐ„์—์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘์ ์ธ ํ๋ฆ„์ด ๋ˆˆ์— ์ต์ง€ ์•Š์•„ ์ฒ˜์Œ์—” ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡์ง€๋งŒ ๋ฒˆ๊ฑฐ๋กœ์šด ๋กœ๋”ฉ ๋ฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์ผ๋ฐ˜ํ™”ํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ๋„ˆ๋ฌด๋‚˜๋„ ๋งค๋ ฅ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋„์ž…์„ ๊ฐ•๋ ฅ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ด ๊ธฐ๋Šฅ์€ ํ”„๋ก ํŠธ๋งŒ ์ž˜ํ•œ๋‹ค๊ณ  ์™„์„ฑ๋˜๋Š” ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๊ธฐ์— ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์™€ ์ถฉ๋ถ„ํ•œ ํ˜‘์˜๋ฅผ ๊ฑฐ์น˜๋ฉฐ ์ ์ฐจ ์™„์„ฑ๋„๋ฅผ ๋†’์—ฌ๊ฐ€์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ผญ ๋ช…์‹ฌํ•˜์„ธ์š”.

๐Ÿ”Ž ์ „์ฒด ์ฝ”๋“œ

์ดํ•ด๊ฐ€ ์ž˜ ๋˜์ง€ ์•Š๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด react+express๋กœ ๊ตฌํ˜„ํ•œ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ค๋†“์•˜์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ๋ฐ›์•„ ์ƒํƒœ๋“ค์˜ ํ๋ฆ„์„ ์ง์ ‘ ์ฒดํ—˜ํ•ด ๋ณด์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

https://github.com/LasBe-code/error-boundary

 

GitHub - LasBe-code/error-boundary: ๊ฐ„๋‹จํ•œ Express API๋ฅผ ์ด์šฉํ•œ React ErrorBoundary ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ Express API๋ฅผ ์ด์šฉํ•œ React ErrorBoundary ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. Contribute to LasBe-code/error-boundary development by creating an account on GitHub.

github.com

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€


์˜คํ”ˆ ์ฑ„ํŒ