[๐ŸŽ์ฝ”๋”ฉ์• ํ”Œ ๊ฐ•์˜์š”์•ฝ]Lifecycle๊ณผ useEffect 1 โšก

๐ŸŒˆ KJยท2025๋…„ 6์›” 13์ผ

codingapple

๋ชฉ๋ก ๋ณด๊ธฐ
22/23
post-thumbnail

์˜ค๋Š˜์˜ ์ˆ™์ œ ๐Ÿ“

Detail ํŽ˜์ด์ง€ ์•ˆ์— ๋…ธ๋ž€ ๋ฐ•์Šค ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , Detail ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ ํ›„ 2์ดˆ ํ›„์— ๋ฐ•์Šค๊ฐ€ ์‚ฌ๋ผ์ง€๊ฒŒ ํ•ด๋ณด์‹ญ์‹œ์˜ค.

ํŒ: ๋™์ ์ธ UI ์–ด๋–ป๊ฒŒ ๋งŒ๋“ ๋‹ค๊ณ  ํ–ˆ๋‚˜์š”? ๐Ÿค”

์ปดํฌ๋„ŒํŠธ์˜ Lifecycle (์ƒ๋ช…์ฃผ๊ธฐ) ๐Ÿ”„

์ปดํฌ๋„ŒํŠธ๋„ ์ธ์ƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค! ์‚ฌ๋žŒ์ฒ˜๋Ÿผ ํƒœ์–ด๋‚˜๊ณ , ์‚ด๋‹ค๊ฐ€, ์ฃฝ์Šต๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ์˜ 3๋‹จ๊ณ„ ์ธ์ƒ

  1. ์ƒ์„ฑ (Mount) ๐ŸŽ‚ - ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋‚˜ํƒ€๋‚  ๋•Œ
  2. ์žฌ๋ Œ๋”๋ง (Update) ๐Ÿ”„ - ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ
  3. ์‚ญ์ œ (Unmount) ๐Ÿ’€ - ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ๋ผ์งˆ ๋•Œ

Lifecycle์„ ๋ฐฐ์šฐ๋Š” ์ด์œ  ๐Ÿ’ก

์ปดํฌ๋„ŒํŠธ ์ธ์ƒ์˜ ์ค‘๊ฐ„์ค‘๊ฐ„์— ๊ฐ„์„ญํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค!

  • "์ปดํฌ๋„ŒํŠธ ๋“ฑ์žฅํ•  ๋•Œ ์ด๊ฒƒ ์ข€ ํ•ด์ค˜"
  • "์ปดํฌ๋„ŒํŠธ ์‚ฌ๋ผ์ง€๊ธฐ ์ „์— ์ด๊ฒƒ ์ข€ ํ•ด์ค˜"
  • "์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ ํ›„์— ์ด๊ฒƒ ์ข€ ํ•ด์ค˜"

์ด๋Ÿฐ ๊ฐ„์„ญ์€ ๊ฐˆ๊ณ ๋ฆฌ(hook)๋ฅผ ๋‹ฌ์•„์„œ ํ•ฉ๋‹ˆ๋‹ค!

Lifecycle Hook ์‚ฌ์šฉ๋ฒ• ๐Ÿช

์˜›๋‚  ๋ฐฉ์‹ (Class Component)

class Detail2 extends React.Component {
  componentDidMount(){
    //Detail2 ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋“œ๋˜๊ณ ๋‚˜์„œ ์‹คํ–‰ํ•  ์ฝ”๋“œ
  }
  componentDidUpdate(){
    //Detail2 ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๊ณ ๋‚˜์„œ ์‹คํ–‰ํ•  ์ฝ”๋“œ
  }
  componentWillUnmount(){
    //Detail2 ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ญ์ œ๋˜๊ธฐ์ „์— ์‹คํ–‰ํ•  ์ฝ”๋“œ
  }
}

์š”์ฆ˜ ๋ฐฉ์‹ (Function Component + useEffect)

import {useState, useEffect} from 'react';

function Detail(){
  useEffect(() => {
    //์—ฌ๊ธฐ์ ์€ ์ฝ”๋“œ๋Š” ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ & ์—…๋ฐ์ดํŠธ ๋งˆ๋‹ค ์‹คํ–‰๋จ
    console.log('์•ˆ๋…•')
  });
  
  return (
    // ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ
  )
}

useEffect ํ…Œ์ŠคํŠธํ•ด๋ณด๊ธฐ

import {useState, useEffect} from 'react';

function Detail(){
  useEffect(() => {
    console.log('์•ˆ๋…•')
  });

  let [count, setCount] = useState(0)
  
  return (
    <button onClick={() => { setCount(count+1) }}>๋ฒ„ํŠผ</button>
  )
}

์ฐธ๊ณ : <React.StrictMode> ํƒœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ 2๋ฒˆ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค (๋””๋ฒ„๊น…์šฉ)

useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ง„์งœ ์ด์œ  ๐ŸŽฏ

"useEffect ๋ฐ–์— ์ ์–ด๋„ ๋˜‘๊ฐ™์€๋ฐ์š”?" ๐Ÿคจ

๋งž์Šต๋‹ˆ๋‹ค! useEffect ๋ฐ”๊นฅ์— ์ ์–ด๋„ ์ปดํฌ๋„ŒํŠธ mount & update ์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์ฐจ์ด์ : ์‹คํ–‰ ์‹œ์ ! โฐ

useEffect ๋ฐ–์— ์ฝ”๋“œ ์ž‘์„ฑ:

function Detail(){
  (๋ฐ˜๋ณต๋ฌธ 10์–ต๋ฒˆ ๋Œ๋ฆฌ๋Š” ์ฝ”๋“œ)  // โ† html ๋ Œ๋”๋ง ์ „์— ์‹คํ–‰
  return (์ƒ๋žต)
}

โ†’ ๋ฐ˜๋ณต๋ฌธ ์™„๋ฃŒ ํ›„ HTML ๋ณด์—ฌ์คŒ

useEffect ์•ˆ์— ์ฝ”๋“œ ์ž‘์„ฑ:

function Detail(){
  useEffect(() => {
    (๋ฐ˜๋ณต๋ฌธ 10์–ต๋ฒˆ ๋Œ๋ฆฌ๋Š” ์ฝ”๋“œ)  // โ† html ๋ Œ๋”๋ง ํ›„์— ์‹คํ–‰
  });
  
  return (์ƒ๋žต)
}

โ†’ HTML ๋จผ์ € ๋ณด์—ฌ์ฃผ๊ณ  ๋ฐ˜๋ณต๋ฌธ ์‹คํ–‰

useEffect์˜ ์šฉ๋„ ๐Ÿ› ๏ธ

Side Effect๋ž€?

  • ํ•ต์‹ฌ ๊ธฐ๋Šฅ: HTML ๋ Œ๋”๋ง
  • Side Effect: ๊ทธ ์™ธ์˜ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ๋“ค

useEffect์— ๋„ฃ์œผ๋ฉด ์ข‹์€ ๊ฒƒ๋“ค

  • ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฐ˜๋ณต ์—ฐ์‚ฐ ๐Ÿ”„
  • ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ์ž‘์—… ๐Ÿ“ก
  • ํƒ€์ด๋จธ ์„ค์ •ํ•˜๊ธฐ โฑ๏ธ
  • DOM ์กฐ์ž‘ํ•˜๊ธฐ ๐ŸŽจ

๋ชฉ์ : HTML ๋ Œ๋”๋ง์„ ๋น ๋ฅด๊ฒŒ ํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ ! โšก


๋ณด์ถฉ ์„ค๋ช… ๐Ÿ’ก

useEffect์˜ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ํŒจํ„ด ๐ŸŽจ

1. ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• (mount + update ์‹œ ์‹คํ–‰)

useEffect(() => {
  console.log('์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง๋จ');
});

2. ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ (mount ์‹œ์—๋งŒ)

useEffect(() => {
  console.log('์ปดํฌ๋„ŒํŠธ ์ฒ˜์Œ ๋ Œ๋”๋ง๋จ');
}, []); // ๋นˆ ๋ฐฐ์—ด์ด ํ•ต์‹ฌ!

3. ํŠน์ • ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ์‹คํ–‰

useEffect(() => {
  console.log('count๊ฐ€ ๋ณ€๊ฒฝ๋จ');
}, [count]); // count ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ์‹คํ–‰

4. cleanup ํ•จ์ˆ˜ (unmount ์‹œ ์‹คํ–‰)

useEffect(() => {
  const timer = setInterval(() => {
    console.log('1์ดˆ๋งˆ๋‹ค ์‹คํ–‰');
  }, 1000);
  
  return () => {
    clearInterval(timer); // ์ปดํฌ๋„ŒํŠธ ์ œ๊ฑฐ ์‹œ ํƒ€์ด๋จธ ์ •๋ฆฌ
  };
}, []);

์‹ค์ „ useEffect ํ™œ์šฉ ์˜ˆ์‹œ ๐ŸŽฏ

1. API ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

function ProductList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch('/api/products')
      .then(response => response.json())
      .then(data => {
        setProducts(data);
        setLoading(false);
      })
      .catch(error => {
        console.error('๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ:', error);
        setLoading(false);
      });
  }, []); // ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์‹œ ํ•œ ๋ฒˆ๋งŒ
  
  if (loading) return <div>๋กœ๋”ฉ ์ค‘...</div>;
  
  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}

2. ํƒ€์ด๋จธ ๊ตฌํ˜„

function Timer() {
  const [seconds, setSeconds] = useState(0);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);
    
    // cleanup: ์ปดํฌ๋„ŒํŠธ ์ œ๊ฑฐ ์‹œ ํƒ€์ด๋จธ ์ •๋ฆฌ
    return () => clearInterval(interval);
  }, []);
  
  return <div>{seconds}์ดˆ ๊ฒฝ๊ณผ</div>;
}

3. ์œˆ๋„์šฐ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ

function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0);
  
  useEffect(() => {
    const handleScroll = () => {
      setScrollY(window.scrollY);
    };
    
    window.addEventListener('scroll', handleScroll);
    
    // cleanup: ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ œ๊ฑฐ
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);
  
  return <div>์Šคํฌ๋กค ์œ„์น˜: {scrollY}px</div>;
}

useEffect ์ตœ์ ํ™” ํŒ โšก

1. ์˜์กด์„ฑ ๋ฐฐ์—ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ

// โŒ ์ž˜๋ชป๋œ ์˜ˆ: ๋ฌดํ•œ ๋ฃจํ”„ ์œ„ํ—˜
useEffect(() => {
  setData(processData(props.rawData));
}); // ์˜์กด์„ฑ ๋ฐฐ์—ด ์—†์Œ

// โœ… ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ: props.rawData ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ์‹คํ–‰
useEffect(() => {
  setData(processData(props.rawData));
}, [props.rawData]);

2. useCallback๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ

function Parent() {
  const [count, setCount] = useState(0);
  
  // useCallback์œผ๋กœ ํ•จ์ˆ˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜
  const fetchData = useCallback(async () => {
    const response = await fetch(`/api/data/${count}`);
    return response.json();
  }, [count]);
  
  return <Child onFetch={fetchData} />;
}

function Child({ onFetch }) {
  useEffect(() => {
    onFetch().then(data => console.log(data));
  }, [onFetch]); // onFetch๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์‹คํ–‰
}

์ž์ฃผ ํ•˜๋Š” ์‹ค์ˆ˜์™€ ํ•ด๊ฒฐ๋ฒ• ๐Ÿšจ

1. ๋ฌดํ•œ ๋ฃจํ”„

// โŒ ๋ฌธ์ œ: ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐœ์ƒ
function BadComponent() {
  const [data, setData] = useState([]);
  
  useEffect(() => {
    setData([...data, 'new item']); // data ๋ณ€๊ฒฝ โ†’ ๋ฆฌ๋ Œ๋”๋ง โ†’ useEffect ์žฌ์‹คํ–‰
  }, [data]);
}

// โœ… ํ•ด๊ฒฐ: ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ ์‚ฌ์šฉ
function GoodComponent() {
  const [data, setData] = useState([]);
  
  useEffect(() => {
    setData(prev => [...prev, 'new item']);
  }, []); // ๋นˆ ๋ฐฐ์—ด๋กœ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
}

2. Memory Leak ๋ฐฉ์ง€

function Component() {
  useEffect(() => {
    const subscription = subscribe();
    
    // ๋ฐ˜๋“œ์‹œ cleanup ํ•จ์ˆ˜์—์„œ ์ •๋ฆฌํ•˜๊ธฐ
    return () => {
      subscription.unsubscribe();
    };
  }, []);
}

์ˆ™์ œ ํ•ด๊ฒฐ ํžŒํŠธ ๐Ÿ’ก

๋…ธ๋ž€ ๋ฐ•์Šค๊ฐ€ 2์ดˆ ํ›„ ์‚ฌ๋ผ์ง€๊ฒŒ ํ•˜๋ ค๋ฉด:

  1. useState๋กœ ๋ฐ•์Šค ํ‘œ์‹œ/์ˆจ๊น€ ์ƒํƒœ ๊ด€๋ฆฌ
  2. useEffect์—์„œ setTimeout ์‚ฌ์šฉ
  3. ๋™์  UI ํŒจํ„ด ์ ์šฉ (์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง)
// ํžŒํŠธ ๊ตฌ์กฐ
function Detail() {
  const [showBox, setShowBox] = useState(true);
  
  useEffect(() => {
    // 2์ดˆ ํ›„ ๋ฐ•์Šค ์ˆจ๊ธฐ๊ธฐ
    setTimeout(() => {
      setShowBox(false);
    }, 2000);
  }, []);
  
  return (
    <div>
      {showBox && <div className="yellow-box">๋…ธ๋ž€ ๋ฐ•์Šค</div>}
      {/* ๋‚˜๋จธ์ง€ ๋‚ด์šฉ */}
    </div>
  );
}

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