Lifecycle of Reactive Effects

date
2026-01-26
order
5
link
  • Lifecycle of Reactive Effects โ€“ React

You will learn

  • effect์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ์˜ ์ฐจ์ด
  • ๊ฐ effect๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ƒ๊ฐํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ์–ธ์ œ ๊ทธ๋ฆฌ๊ณ  ์™œ effect๊ฐ€ ์žฌ๋™๊ธฐํ™” ํ•ด์•ผ ํ•˜๋Š”์ง€
  • ์–ด๋–ป๊ฒŒ effect์˜ ์˜์กด์„ฑ์ด ๊ฒฐ์ •๋˜๋Š”์ง€
  • ์–ด๋–ค ๊ฐ’์ด '๋ฐ˜์‘ํ˜• reactive' ์ด๋ผ๋Š” ๊ฒƒ์˜ ์˜๋ฏธ
  • ๋น„์–ด์žˆ๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ๊ฐ€์ง€๋Š” ์˜๋ฏธ
  • ๋ฆฌ์•กํŠธ๋Š” ์–ด๋–ป๊ฒŒ effect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ๊ฒ€์ฆํ•˜๋Š”์ง€
  • ๋ฆฐํ„ฐ์— ๋™์˜ํ•˜์ง€ ์•Š์„ ๋•Œ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•˜๋Š”์ง€

์ดํŽ™ํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ

์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค :

  • ๋งˆ์šดํŠธ : ํ™”๋ฉด์— ์ถ”๊ฐ€
  • ์—…๋ฐ์ดํŠธ : ์œ ์ € ์ด๋ฒคํŠธ๋‚˜ ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ๋“ฑ์œผ๋กœ ์ธํ•œ props๋‚˜ state์˜ ๋ณ€๊ฒฝ์ด ์žˆ์„ ๋•Œ ๋งˆ๋‹ค ๋ฆฌ๋ Œ๋”๋ง
  • ์–ธ๋งˆ์šดํŠธ : ํ™”๋ฉด์—์„œ ์ œ๊ฑฐ

๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฐ ์ปดํฌ๋„ŒํŠธ ์ƒ๋ช…์ฃผ๊ธฐ์˜ ํ‹€ ์•ˆ์—์„œ ์ดํŽ™ํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ƒ๊ฐํ•˜๋Š”๊ฑด ์•ˆ์ข‹์€ ๋ฐฉ๋ฒ•์ด(๋ผ๊ณ  ํ•œ)๋‹ค.

roomId๋ผ๋Š” prop์„ ๊ฐ€์ง€๋Š” ChatRoom ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž. ์ด ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค :

  1. ChatRoom ์ด ๋งˆ์šดํŠธ ๋œ๋‹ค. roomId ๋Š” "general".
  2. ChatRoom ์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค. roomId ๋ฅผ "travel"๋กœ ๋ณ€๊ฒฝ.
  3. ChatRoom ์ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค. roomId ๋ฅผ "music"๋กœ ๋ณ€๊ฒฝ.
  4. ChatRoom ์–ธ๋งˆ์šดํŠธ.

์ด ๊ด€์ ์—์„œ ์ฑ„ํŒ… ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐํ•˜๋Š” ์ดํŽ™ํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค :

  1. ์ดํŽ™ํŠธ๊ฐ€ "general" ์ฑ„ํŒ…๋ฐฉ๊ณผ ์—ฐ๊ฒฐํ•œ๋‹ค.
  2. ์ดํŽ™ํŠธ๊ฐ€ "general" ์ฑ„ํŒ…๋ฐฉ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ง€ํ•˜๊ณ (ํด๋ฆฐ์—…) "travel" ์ฑ„ํŒ…๋ฐฉ๊ณผ ์—ฐ๊ฒฐํ•œ๋‹ค.
  3. ์ดํŽ™ํŠธ๊ฐ€ "travel" ์ฑ„ํŒ…๋ฐฉ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ง€ํ•˜๊ณ  "music" ์ฑ„ํŒ…๋ฐฉ๊ณผ ์—ฐ๊ฒฐํ•œ๋‹ค.
  4. ์ดํŽ™ํŠธ๊ฐ€ "music" ์ฑ„ํŒ…๋ฐฉ๊ณผ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ง€ํ•œ๋‹ค.

๋งˆ์น˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋  ๋•Œ ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑ๊ณผ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐ์ด ๋“ ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋” ๋ณต์žกํ•ด์ง€๊ฒŒ ๋œ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๊ด€์ ์—์„œ ์ดํŽ™ํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ํŠน์ • "์‹œ์ "์— ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ์ง‘์ฐฉํ•˜๊ฒŒ ๋˜๊ณ  ์ž˜๋ชป๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋  ์ˆ˜ ์žˆ๋‹ค.

[!faq] ๋ฌด์—‡์ด "๋ณต์žกํ•ด"์ง„๋‹ค๋Š” ๊ฑธ๊นŒ?

์ปดํฌ๋„ŒํŠธ์™€ ๋ถ„๋ฆฌํ•ด ์ดํŽ™ํŠธ์˜ ์‹œ์ž‘๊ณผ ์ค‘์ง€ ์‚ฌ์ดํด์— ์ง‘์ค‘ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค :

  1. "general" ๋ฐฉ์— ์—ฐ๊ฒฐ๋œ ์ดํŽ™ํŠธ
  2. "travel" ๋ฐฉ์— ์—ฐ๊ฒฐ๋œ ์ดํŽ™ํŠธ
  3. "music" ๋ฐฉ์— ์—ฐ๊ฒฐ๋œ ์ดํŽ™ํŠธ

๊ฐ ์ดํŽ™ํŠธ๋“ค์ด ๋…๋ฆฝ์ ์œผ๋กœ ์–ธ์ œ ์—ฐ๊ฒฐ๋˜๊ณ  ์–ธ์ œ ์ข…๋ฃŒํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ๋งŒ ์ง‘์ค‘ํ•˜๋ฉด ๊ฐ ์—ฐ๊ฒฐ๋“ค์ด ์–ธ์ œ ์‹œ์ž‘๋˜๊ณ  ์–ธ์ œ ์ข…๋ฃŒ๋˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ๋งŒ ์„ค๊ณ„ํ•˜๋ฉด ๋œ๋‹ค.

์˜ˆ์‹œ ๋ฌธ์ œ

1,2,4 ๋ฒˆ ๋ฌธ์ œ๋Š” ์ƒ๋žต.

3. stale ๊ฐ’ ๋ฒ„๊ทธ

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [canMove, setCanMove] = useState(true);

  function handleMove(e) {
    if (canMove) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
  }

  useEffect(() => {
    window.addEventListener('pointermove', handleMove);
    return () => window.removeEventListener('pointermove', handleMove);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (

canMove๋ฅผ ๋ณ€๊ฒฝํ•ด๋„ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ๊ฐ€ ์›€์ง์ด๋Š” ์ด์œ ๋Š” ๋ญ˜๊นŒ? ๊ทธ ์ด์œ ๋Š” ์ดํŽ™ํŠธ์—์„œ ์ฐธ์กฐํ•˜๋Š” handleMove ํ•จ์ˆ˜๊ฐ€ canMove๊ฐ€ ์ฐธ์ผ๋•Œ ์ •์˜๋œ ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ•จ์ˆ˜๋Š” ํด๋กœ์ €์—ฌ์„œ ๊ทธ๋ ‡๋‹ค.

๊ทธ๋ž˜์„œ ์•„์˜ˆ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ๋ Œ๋”๋ง๋งˆ๋‹ค ์ดํŽ™ํŠธ๊ฐ€ ์‹คํ–‰๋˜๋ฉด์„œ ์ตœ์‹ ์˜ handleMove๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ.

  useEffect(() => {
    window.addEventListener('pointermove', handleMove);
    return () => window.removeEventListener('pointermove', handleMove);
  });

ํ•˜์ง€๋งŒ ์ด๋Š” ์ตœ์„ ์˜ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋‹ค. ์ง์ ‘์ ์ธ ์—ฐ๊ด€์ด ์žˆ๋Š” canMove์ƒํƒœ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๋ชจ๋“  ์ƒํƒœ๋“ค์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๋งˆ๋‹ค ์œ„์˜ ์ดํŽ™ํŠธ๊ฐ€ ์‹คํ–‰๋˜๋‹ˆ๊นŒ.

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด๊ฒฐํ•˜๋Š”๊ฒŒ ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

useEffect(() => {
    function handleMove(e) {
      if (canMove) {
        setPosition({ x: e.clientX, y: e.clientY });
      }
    }

    window.addEventListener('pointermove', handleMove);
    return () => window.removeEventListener('pointermove', handleMove);
  }, [canMove]);

์™œ handleMove์— ์˜์กดํ•˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๋ผ handleMove๋ฅผ ๋น„๋ฐ˜์‘ํ˜• ๊ฐ’์œผ๋กœ ๋งŒ๋“ค๊ณ  canMove์ƒํƒœ์— ์˜์กดํ•˜๋„๋ก ์ž‘์„ฑํ• ๊นŒ? handleMove๋Š” canMove์™€ ์ƒ๊ด€ ์—†์ด ๋ Œ๋”๋ง ๋  ๋•Œ ๋งˆ๋‹ค ๋ฐ”๋€Œ๊ฒŒ ๋˜๋Š”๋ฐ, ๊ทธ๋Ÿผ ์œ„์˜ ์ฝ”๋“œ์™€ ๋˜‘๊ฐ™์ด ๊ฒฐ๊ตญ ๋ชจ๋“  ๋ Œ๋”๋ง๋งˆ๋‹ค ์‹คํ–‰ํ•˜๋Š”๊ฒƒ์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

5. ์…€๋ ‰ํŠธ ๋ฐ•์Šค ์ฒด์ธ

์•„๋ž˜์˜ ์ฝ”๋“œ์—์„œ planet ์„ ํƒ์‹œ์— placeList ๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

export default function Page() {
  const [planetList, setPlanetList] = useState([])
  const [planetId, setPlanetId] = useState('');

  const [placeList, setPlaceList] = useState([]);
  const [placeId, setPlaceId] = useState('');

  useEffect(() => {
    let ignore = false;
    fetchData('/planets').then(result => {
      if (!ignore) {
        console.log('Fetched a list of planets.');
        setPlanetList(result);
        setPlanetId(result[0].id); // Select the first planet
      }
    });
    return () => {
      ignore = true;
    }
  }, []);

  return (
  //...

์ผ๋‹จ planetId์— ์˜์กดํ•˜๋Š” ์ดํŽ™ํŠธ ํ•˜๋‚˜๋ฅผ ๋” ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

useEffect(() => {
    if (planetId === '') {
      // Nothing is selected in the first box yet
      return;
    }

    let ignore = false;
    fetchData('/planets/' + planetId + '/places').then(result => {
      if (!ignore) {
        console.log('Fetched a list of places on "' + planetId + '".');
        setPlaceList(result);
        setPlaceId(result[0].id); // Select the first place
      }
    });
    return () => {
      ignore = true;
    }
  }, [planetId]);

์ฝ”๋“œ ๋ฐ˜๋ณต์„ ํ”ผํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ปค์Šคํ…€ ํ›…์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

// ์ปดํฌ๋„ŒํŠธ
export default function Page() {
  const [
    planetList,
    planetId,
    setPlanetId
  ] = useSelectOptions('/planets');

  const [
    placeList,
    placeId,
    setPlaceId
  ] = useSelectOptions(planetId ? `/planets/${planetId}/places` : null);

// useSelectOptions ํ›…  
export function useSelectOptions(url) {
  const [list, setList] = useState(null);
  const [selectedId, setSelectedId] = useState('');
  useEffect(() => {
    if (url === null) {
      return;
    }

    let ignore = false;
    fetchData(url).then(result => {
      if (!ignore) {
        setList(result);
        setSelectedId(result[0].id);
      }
    });
    return () => {
      ignore = true;
    }
  }, [url]);
  return [list, selectedId, setSelectedId];
}