August 8, 2025

๐Ÿ‘ป ์ข€๋น„ ์ปดํฌ๋„ŒํŠธ: Unmount ๋˜์ง€ ์•Š์€ ๋น„๋™๊ธฐ ๊ตฌ๋…๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜

JavaScript
React
์„ฑ๋Šฅ
์—๋Ÿฌ์ฒ˜๋ฆฌ
์ปดํฌ๋„ŒํŠธ
๋น„๋™๊ธฐ์ฒ˜๋ฆฌ
์•„ํ‚คํ…์ฒ˜

Summary

์ปดํฌ๋„ŒํŠธ unmount ์‹œ ๋น„๋™๊ธฐ ๊ตฌ๋…์„ ์ •๋ฆฌํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. useEffect ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ฆฌํ•˜์„ธ์š”.

Why Wrong?

์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount๋œ ํ›„์—๋„ ์—ฌ์ „ํžˆ ์‹คํ–‰ ์ค‘์ธ ๋น„๋™๊ธฐ ์ž‘์—…(์˜ˆ: setInterval, setTimeout, RxJS Observable ๊ตฌ๋…, fetch)์€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€์ž‘์šฉ์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋” ์ด์ƒ ์กด์žฌํ•˜์ง€ ์•Š์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ด์ „ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๊ฑฐ๋‚˜, ์ด๋ฏธ ํ•ด์ œ๋œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฐธ์กฐํ•˜๋ ค ํ•  ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ '์ข€๋น„ ์ปดํฌ๋„ŒํŠธ'๋Š” ์•ฑ ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ค๊ณ , ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋ง์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

How to Fix?

์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount๋  ๋•Œ ๋ชจ๋“  ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ •๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. useEffect ํ›…์˜ ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ setInterval, setTimeout์„ ํ•ด์ œํ•˜๊ณ , RxJS Observable ๊ตฌ๋…์„ ์ทจ์†Œํ•˜๋ฉฐ, ์ง„ํ–‰ ์ค‘์ธ fetch ์š”์ฒญ์„ ์ค‘๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount๋œ ํ›„ ๋ถˆํ•„์š”ํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋‚˜ ๋ถ€์ž‘์šฉ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Before Code (Bad)

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const intervalId = setInterval(() => {
      fetch('/api/data')
        .then(response => response.json())
        .then(newData => {
          setData(newData);
        });
    }, 1000);

    // ํด๋ฆฐ์—… ํ•จ์ˆ˜๊ฐ€ ์—†์Œ! ์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount ๋˜์–ด๋„ interval์€ ๊ณ„์† ์‹คํ–‰๋จ
  }, []);

  return <div>{data ? data.value : 'Loading...'}</div>;
}

export default MyComponent;

After Code (Good)

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isMounted = true; // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜์–ด ์žˆ๋Š”์ง€ ์ถ”์ 
    const intervalId = setInterval(() => {
      fetch('/api/data')
        .then(response => response.json())
        .then(newData => {
          if (isMounted) { // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
            setData(newData);
          }
        });
    }, 1000);

    return () => {
      clearInterval(intervalId); // ์ปดํฌ๋„ŒํŠธ unmount ์‹œ interval ํ•ด์ œ
      isMounted = false; // ์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount๋˜์—ˆ์Œ์„ ํ‘œ์‹œ
    };
  }, []);

  return <div>{data ? data.value : 'Loading...'}</div>;
}

export default MyComponent;