useEffect

ํ•™์Šต ํ‚ค์›Œ๋“œ

  • useEffect

    • Side Effect?

    • Clean-up

    • Effect๊ฐ€ ๋‘ ๋ฒˆ ์‹คํ–‰๋˜๋Š” ๋ฌธ์ œ

  • useLayoutEffect

๐Ÿ“– useEffect

Effect Hook์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ side effect๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. by.useEffect ๊ณต์‹๋ฌธ์„œ

  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ์ดํ›„์— side effect(ํŠน์ •์ž‘์—…)๋ฅผ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ

  • Side-Effect ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” hook

๐Ÿค” Side Effect?

ํ•จ์ˆ˜ ๋‚ด ํŠน์ • ๋™์ž‘์ด ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์˜ํ–ฅ์„ ๋ผ์ณ, ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘์„ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“œ๋Š” ํ–‰์œ„

  • ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋ฉด์„œ ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์กด์žฌํ•˜๋Š” ๊ฐ’์ด๋‚˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ค๋Š” ๋“ฑ์˜ ํ–‰์œ„๋ฅผ ์˜๋ฏธ

  • ์˜ˆ๋ฅผ๋“ค์–ด ํ•จ์ˆ˜๋‚ด์—์„œ ์ „์—ญ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ํ˜น์€ ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์กด์žฌํ•˜๋Š” ๋ฒ„ํŠผ์˜ ํ…์ŠคํŠธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, ํŒŒ์ผ์„ ์“ฐ๊ฑฐ๋‚˜, ์ฟ ํ‚ค ์ €์žฅ, ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์‹ ํ•˜๋Š” ๊ฒƒ

โœ… Side Effect๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ด์œ 

Side-Effect๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์ฝ๊ธฐ ์–ด๋ ต๊ฒŒ ํ•˜๊ณ , ์‹คํ–‰์ƒํƒœ๋ฅผ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ํ•˜๋ฉฐ ๊ฐœ๋ฐœ๋น„์šฉ์„ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค๊ณ  ๋ณด๊ธฐ ๋•Œ๋ฌธ์— ์ตœ๊ทผ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” Side-Effect๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€ํ•˜๊ณ  ์žˆ๋‹ค. ํ•จ์ˆ˜๋Š” ์ „๋‹ฌ๋ฐ›์€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฉฐ ๊ทธ ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ์ผ๊ด€๋˜๊ณ  ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ”„๋กœ๊ทธ๋žจ์ด ์‰ฝ๊ณ  ๋‹จ์ˆœํ•˜๋ฉฐ ์œ ์ง€๋ณด์ˆ˜ ํ•˜๊ธฐ ์‰ฌ์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

useEffect ์‚ฌ์šฉ ์˜ˆ์‹œ

function UserProfile({ name }) {
  const message = `${name}๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!`; //ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ๊ฐ’ ์ƒ์„ฑ

  // Bad!
  document.title = `${name}์˜ ๊ฐœ์ธ์ •๋ณด`; //ํ•จ์ˆ˜ ์™ธ๋ถ€์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” Side-effect ์ฝ”๋“œ
  return <div>{message}</div>;
}
function UserProfile({ name }) {
  const message = `${name}๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!`; 
   
   useEffect(()=> {
      document.title = `${name}์˜ ๊ฐœ์ธ์ •๋ณด`; 
   },[name]);
 
  return <div>{message}</div>;
}

โœ๐Ÿป useEffect ์ •๋ฆฌ

  • useEffect๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ๋Œ€ํ•œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๊ฐ€ ๋  ์ˆ˜ ์žˆ๋„๋ก Side Effect๋ฅผ ๋”ฐ๋กœ ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

  • ๋งค๋ฒˆ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ ํŠน์ • ์กฐ๊ฑด์— ์˜์กดํ•˜์—ฌ ์ˆ˜ํ–‰๋˜๋ฉฐ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ๋Œ€ํ•œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํ•จ์ˆ˜

๐Ÿค– useState ์‚ฌ์šฉ๋ฒ•

import { useEffect } from "react"; //์‚ฌ์šฉ์„ ์œ„ํ•ด import ํ•ด์ค˜์•ผ ํ•˜๊ณ ,

useEffece(()=> {}, [dependency Array]) //์ฝœ๋ฐฑํ•จ์ˆ˜์™€ ์˜์กด์„ฑ๋ฐฐ์—ด์„ ์ธ์ž๋กœ ๋„ฃ์–ด์ค€๋‹ค.

useEffect๋Š” ํ•จ์ˆ˜์˜ ์ธ์ž(์˜์กด์„ฑ๋ฐฐ์—ด)์ •๋ณด์— ๋”ฐ๋ผ ํฌ๊ฒŒ ์„ธ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

๋ฌดํ•œ๋ฐ˜๋ณต

์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค useEffect์˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•œ ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

import { useEffect, useState } from 'react';

export default function TimerControl() {
  const [playing, setPlaying] = useState(false);

  useEffect(() => {
    console.log('๋งค๋ฒˆ ์‹คํ–‰');
  });

  const handleClick = () => {
    console.log('Click!');
    setPlaying(!playing);
  };

  return (
    <div>
      <button type="button" onClick={handleClick}>
        ํ† ๊ธ€ ๋ฒ„ํŠผ {playing ? `on` : `off`}
      </button>
    </div>
  );
}

์ฒ˜์Œ์— ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๊ธฐ

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

์ฃผ๋กœ API๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

import { useEffect, useState } from 'react';

export default function TimerControl() {
  const [playing, setPlaying] = useState(false);

  useEffect(() => {
    console.log('์ตœ์ดˆ ๋ Œ๋”๋ง์‹œ์—๋งŒ ์‹คํ–‰!');
  },[]); // ๋นˆ๋ฐฐ์—ด

  const handleClick = () => {
    console.log('Click!');
    setPlaying(!playing);
  };

  return (
    <div>
      <button type="button" onClick={handleClick}>
        ํ† ๊ธ€ ๋ฒ„ํŠผ {playing ? `on` : `off`}
      </button>
    </div>
  );
}

์˜์กด์„ฑ ๋ฐฐ์—ด ์‚ฌ์šฉ

import { useEffect, useState } from 'react';

export default function TimerControl() {
  const [playing, setPlaying] = useState(false);
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('playing๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์‹คํ–‰!');
  }, [playing]); // playing ์ƒํƒœ๊ฐ’ ์ธ์ž๋กœ ์ „๋‹ฌ

  const handleToggleClick = () => {
    console.log('Toggle Click!');
    setPlaying(!playing);
  };

  const handleAddClick = () => {
    console.log('Add Click!');
    setCount(count+1);
  };

  return (
    <div>
      <button type="button" onClick={handleToggleClick}>
        ํ† ๊ธ€ ๋ฒ„ํŠผ {playing ? `on` : `off`}
      </button>
      <p>{count}</p>
       <button type="button" onClick={handleAddClick}>
        ์ฆ๊ฐ€ ๋ฒ„ํŠผ 
      </button>
    </div>
  );
}

์œ„์˜ ์ฝ”๋“œ๋Š” ์˜์กด์„ฑ๋ฐฐ์—ด์— playing state ๊ฐ’์„ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜์˜€๋‹ค. playing์˜ ์ƒํƒœ๊ฐ’์ด ๋ณ€๊ฒฝ ๋  ๋•Œ ๋งˆ๋‹ค useEffect์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ •๋ฆฌ(clean-up)๋ฅผ ์ด์šฉํ•˜๋Š” Effects

์ •๋ฆฌ(clean-up)๊ฐ€ ํ•„์š”ํ•œ Side effect๋„ ์žˆ๋‹ค. ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ์— ๊ตฌ๋…(subscription)์„ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ,์ด๋Ÿฐ ๊ฒฝ์šฐ์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ •๋ฆฌ(clean-up)ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค.

  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒ๋˜์ง€ ์•Š๋„๋ก ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ œ๊ฑฐํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

import { useEffect, useState } from 'react';

export default function TimerControl() {
  const [count, setCount] = useState(1000);

  useEffect(() => {
    console.log('useEffect ์‹คํ–‰');
    return () => {
      console.log('return ๋ฌธ์˜ ํ•จ์ˆ˜ === cleanup');
    };

  }, [count]);

  const countHander = (e) => {
    setCount((c) => c + 1000);
  };

  return (
    <div className="App">
      <h1>{count}</h1>
      <button onClick={countHander}>์นด์šดํŠธ ์ฆ๊ฐ€</button>
    </div>
  );
}

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด useEffect ํ•จ์ˆ˜์— return๋ฌธ์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. return๋ฌธ์˜ ์ฒซ ๋ Œ๋”๋ง ์‹œ์ ์—๋Š” return๋ฌธ์˜ ํ•จ์ˆ˜๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์นด์šดํŠธ ์ฆ๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด useEffect๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— useEffect์˜ return์— ์ž‘์„ฑํ•œ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋œ๋‹ค.

โ‡’ useEffect์˜ return์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ํ•จ์ˆ˜๋Š”

  • ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ๊ฐ’์ด ๋นˆ๋ฐฐ์—ด ๊ฒฝ์šฐ : ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ DOM์—์„œ unmount๋˜๋Š” ์‹œ์ ์—๋งŒ ์ˆ˜ํ–‰

  • ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ : ๊ฐ’์˜ ๋ณ€๊ฒฝ์ด ๊ฐ์ง€๋  ๋•Œ๋งˆ๋‹ค useEffect๊ฐ€ ์žฌ์ˆ˜ํ–‰๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— return๋ฌธ ํ•จ์ˆ˜ ๋˜ํ•œ ๊ณ„์† ์‹คํ–‰

โœ๐Ÿป useEffect ์‚ฌ์šฉ๋ฒ• ์ •๋ฆฌ

useEffect(() => {
  // ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์‹คํ–‰
});

useEffect(() => {
  // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง๋œ ์‹คํ–‰
}, []);

useEffect(() => {
  // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง๋œ ์ดํ›„ ์‹คํ–‰
  // a๋‚˜ b๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋ Œ๋”๋ง๋œ ์ดํ›„ ์‹คํ–‰
}, [a, b]);

useEffect(() => {
  return () => {
    // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ์‹œ์  
  }
}, []);

useEffect(() => {
  // 2. return๋ฌธ์˜ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰ ๋œ ํ›„ ์‹คํ–‰ 
  return () => {
    // 1. a๋‚˜ b๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ์—…๋ฐ์ดํŠธ๊ฐ€ ๋˜๊ธฐ ์ „์— ๋จผ์ € ์‹คํ–‰
  }
}, [a, b]);

Effect๊ฐ€ ๋‘ ๋ฒˆ ์‹คํ–‰๋˜๋Š” ๋ฌธ์ œ

<React.StrictMode>๋กœ ์ปดํฌ๋„ŒํŠธ ์ „์ฒด๋ฅผ ๊ฐ์Œ€ ๊ฒฝ์šฐ, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ Side Effect๋ฅผ ์ฐพ์œผ๋ ค๊ณ  Effect ๋“ฑ์„ ๋‘ ๋ฒˆ์”ฉ ์‹คํ–‰ํ•œ๋‹ค.

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

๐Ÿ“– useLayoutEffect๋ž€?

  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๋‹ค์‹œ ์ฑ„์šฐ๊ธฐ ์ „์— ์‹คํ–‰๋˜๋Š” ๋ฒ„์ „์˜ useEffect

  • ๋ Œ๋”๋งํž ์ƒํƒœ๊ฐ€ effect ๋‚ด์—์„œ ์ดˆ๊ธฐํ™” ๋˜์–ด์•ผ ํ•  ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

useEffect vs useLayoutEffect

  • ์ด๋ฒคํŠธ์˜ ํ˜ธ์ถœ(์‹คํ–‰)์‹œ์ ์˜ ์ฐจ์ด

useEffect์˜ ๊ฒฝ์šฐ DOM์˜ ๋ ˆ์ด์•„์›ƒ ๋ฐฐ์น˜์™€ ํŽ˜์ธํŠธ๊ฐ€ ๋๋‚œ ํ›„ ์ดํŽ™ํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. useEffect ๋‚ด๋ถ€์— Dom์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋Š” ํ™”๋ฉด์˜ ๊นœ๋นก์ž„์„ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋œ๋‹ค. useLayoutEffect์˜ ๊ฒฝ์šฐ DOM์˜ ๋ ˆ์ด์•„์›ƒ ๋ฐฐ์น˜๊ฐ€ ๋๋‚œ ํ›„ ์ดํŽ™ํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํŽ˜์ธํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. paint๊ฐ€ ๋˜๊ธฐ์ „์— ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— Dom์„ ์กฐ์ž‘ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๋”๋ผ๋„ ์‚ฌ์šฉ์ž๋Š” ๊นœ๋นก์ž„์„ ๊ฒฝํ—˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.

import { useEffect, useState } from "react";

function App() {
  const [age, setAge] = useState(0);
  const [name, setName] = useState("");
  
  useEffect(() => { // useLayoutEffect ๋ณ€๊ฒฝํ•ด๋ณด๋ฉด ์ฐจ์ด๋ฅผ ๋Š๋‚„ ์ˆ˜ ์žˆ๋‹ค.
    setAge(25);
    setName("์ฐฌ๋ฏผ");
  }, []);
  
  return (
    <>
      <div className="App">{`๊ทธ์˜ ์ด๋ฆ„์€ ${name} ์ด๋ฉฐ, ๋‚˜์ด๋Š” ${age}์‚ด ์ž…๋‹ˆ๋‹ค.`}</div>
    </>
  );
}

export default App;

๐Ÿ”— ์ฐธ๊ณ 

Last updated