๋ฐ˜์‘ํ˜•

๋ฆฌ์•กํŠธ์˜ useEffect๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ, ๊ตฌ๋… ๊ด€๋ฆฌ, DOM ์—…๋ฐ์ดํŠธ, ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ฒ˜๋ฆฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์ž‘์—…์— ์‚ฌ์šฉ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ useEffect๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜, ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง, ๋””๋ฒ„๊น…์˜ ๋ณต์žก์„ฑ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. "Leave useEffect Alone!" ๋ผ๋Š” ๊ฐ€์ด๋“œ ๊ธ€์„ ์ฐธ๊ณ ํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ useEffect ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์„ค๋ช…์„ ๋ง๋ถ™์—ฌ์„œ ์ •๋ฆฌํ•ด ๋ดค๋‹ค.

 

 

๊ฒฝ์Ÿ ์ƒํƒœ(Race Condition) โญ


๊ฒฝ์Ÿ ์ƒํƒœ๋Š” ์—ฌ๋Ÿฌ ๋น„๋™๊ธฐ ์ž‘์—…์ด ๋™์‹œ์— ์‹คํ–‰๋  ๋•Œ, ์‹คํ–‰ ์ˆœ์„œ๋‚˜ ๊ฒฐ๊ณผ๊ฐ€ ์˜ˆ์ธกํ•˜์ง€ ์•Š์€ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•˜๋Š” ํ˜„์ƒ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ๋ฒ„ํŠผ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ํด๋ฆญํ•˜๋ฉด counter ๊ฐ’์ด ์ฆ๊ฐ€ํ•˜๊ณ , ๊ฐ ์š”์ฒญ์€ ๋žœ๋คํ•œ ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐํ•œ ํ›„ response ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๊ณผ์ •์—์„œ ๊ฒฝ์Ÿ ์ƒํƒœ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค(์ฝ”๋“œํŽœ ๋งํฌ).

function RaceConditionExample() {
  const [counter, setCounter] = useState(0);
  const [response, setResponse] = useState(0);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const request = async (requestId) => {
      setIsLoading(true);
      await sleep(Math.random() * 3000);
      setResponse(requestId);
      setIsLoading(false);
    };
    request(counter);
  }, [counter]);

  const handleClick = () => {
    setCounter((prev) => ++prev);
  };

  return (
    <>
      <h3>Current Value: {counter}</h3>
      <h3>Settled Response: {response}</h3>
      <button onClick={handleClick}>Increment</button>
      {/* ... */}
    </>
  );
}

 

  1. ์ฒซ ๋ฒˆ์งธ ํด๋ฆญ: counter + 1, requestId 1 ์š”์ฒญ ์‹œ์ž‘
  2. ๋‘ ๋ฒˆ์งธ ํด๋ฆญ: counter + 1, requestId 2 ์š”์ฒญ ์‹œ์ž‘
  3. ๋งŒ์•ฝ ๋‘ ๋ฒˆ์งธ ์š”์ฒญ์ด ๋” ๋นจ๋ฆฌ ์™„๋ฃŒ๋˜๋ฉด response ์ƒํƒœ๋Š” 2๋กœ ์—…๋ฐ์ดํŠธ
  4. ์ดํ›„ ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด์„œ response ์ƒํƒœ๋ฅผ 1๋กœ ๋ฎ์–ด์”€

 

 

์ด์ฒ˜๋Ÿผ response ์ƒํƒœ๋Š” ๊ฐ€์žฅ ์ตœ์‹  ์š”์ฒญ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•ด์•ผ ํ•˜์ง€๋งŒ, ๋น„๋™๊ธฐ ์š”์ฒญ์˜ ์™„๋ฃŒ ์ˆœ์„œ๊ฐ€ ๋žœ๋ค ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜๋„์น˜ ์•Š์€ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค.

 

์ด๋Ÿฌํ•œ ๊ฒฝ์Ÿ ์ƒํƒœ๋Š” ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ํด๋ฆฐ์—… ํ•จ์ˆ˜๋Š” ๋‹ค์Œ ์ดํŽ™ํŠธ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋˜๋ฉฐ, ์ด์ „ ์ดํŽ™ํŠธ ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„์—์„œ ๋™์ž‘ํ•œ๋‹ค.

useEffect(() => {
  let ignore = false; // ํ˜„์žฌ useEffect ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„
  const request = async (requestId) => {
    setIsLoading(true);
    await sleep(Math.random() * 3000);
    if (!ignore) {
      setResponse(requestId);
      setIsLoading(false);
    }
  };
  request(counter);

  return () => {
    ignore = true; // ์ด์ „ useEffect ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„
  };
}, [counter]);

 

  1. ์ฒซ ๋ฒˆ์งธ ํด๋ฆญ: counter + 1, requestId 1 ์š”์ฒญ ์‹œ์ž‘, ignore = false๋กœ ์ดˆ๊ธฐํ™”
  2. ๋‘ ๋ฒˆ์งธ ํด๋ฆญ: counter + 1, requestId 2 ์š”์ฒญ ์‹œ์ž‘
    • ์ฒซ ๋ฒˆ์งธ ํด๋ฆญ์—์„œ ์‹คํ–‰๋œ useEffect ํด๋ฆฐ์—… ํ•จ์ˆ˜ ํ˜ธ์ถœ → ignore = true๋กœ ๋ณ€๊ฒฝ (์ด์ „ ์š”์ฒญ ๋ฌดํšจํ™”)
    • ๋‘ ๋ฒˆ์งธ ํด๋ฆญ์—์„œ ์‹คํ–‰๋œ useEffect ํ•จ์ˆ˜ ํ˜ธ์ถœ → ignore = false๋กœ ์ดˆ๊ธฐํ™”
  3. ๋‘ ๋ฒˆ์งธ ์š”์ฒญ ์™„๋ฃŒ: ๋‘ ๋ฒˆ์งธ useEffect์˜ ignore ๋ณ€์ˆ˜๊ฐ€ false ์ด๋ฏ€๋กœ response ์ƒํƒœ 2๋กœ ์—…๋ฐ์ดํŠธ
  4. ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ ์™„๋ฃŒ: ์ฒซ ๋ฒˆ์งธ useEffect์˜ ignore ๋ณ€์ˆ˜๊ฐ€ true ์ด๋ฏ€๋กœ response ์ƒํƒœ ๋ณ€๊ฒฝ ์•ˆ ํ•จ

 

 

๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง


์•„๋ž˜ ์ฝ”๋“œ๋Š” useEffect ์‚ฌ์šฉ์œผ๋กœ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์ด ํ•œ ๋ฒˆ ๋” ๋ฐœ์ƒํ•œ๋‹ค.

function Parent() {
  const [someState, setSomeState] = useState();
  return <Child onChange={(v) => setSomeState(v)} />;
}

function Child({ onChange }) {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    onChange(isOn); // ์ถ”๊ฐ€ ๋ Œ๋”๋ง ์œ ๋ฐœ
  }, [isOn, onChange]);

  function handleClick() {
    setIsOn(!isOn);
  }

  return <button onClick={handleClick}>Toggle</button>;
}

 

  1. ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฐœ์ƒ → Child ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ์ปฌ ์ƒํƒœ(isOn) ์—…๋ฐ์ดํŠธ
  2. useEffect ์‹คํ–‰ → Parent ์ปดํฌ๋„ŒํŠธ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ํ•ธ๋“ค๋Ÿฌ(onChange) ์‹คํ–‰
  3. Parent ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ ์—…๋ฐ์ดํŠธ → ๋ฆฌ๋ Œ๋”๋ง
  4. Child ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง

 

ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ์ฝœ๋ฐฑ(onChange)์„ ์‹คํ–‰ํ•˜๋Š” ๋กœ์ง์ด๋ฏ€๋กœ useEffect๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ํด๋ฆญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ onChange๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ์ค„์ผ ์ˆ˜ ์žˆ๊ณ , ์ฝ”๋“œ๋„ ๋” ๊น”๋”ํ•ด์ง„๋‹ค.

function Parent() {
  const [someState, setSomeState] = useState();
  return <Child onChange={(v) => setSomeState(v)} />;
}

function Child({ onChange }) {
  const [isOn, setIsOn] = useState(false);

  function handleClick() {
    const newValue = !isOn;
    setIsOn(newValue); // ๋กœ์ปฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
    onChange(newValue); // ๋ถ€๋ชจ ํ•ธ๋“ค๋Ÿฌ ํ˜ธ์ถœ
  }

  return <button onClick={handleClick}>Toggle</button>;
}

 

 

๋ฐ์ดํ„ฐ ํ๋ฆ„


๋ฆฌ์•กํŠธ์—์„  ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„(๋ถ€๋ชจ → ์ž์‹)์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์œ ์ง€๋ณด์ˆ˜๋‚˜ ๊ฐ€๋…์„ฑ ์ธก๋ฉด์—์„œ ์œ ๋ฆฌํ•˜๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ถ”์ ์ด ์šฉ์ดํ•ด์ง€๊ณ , ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์˜์กด์„ฑ์„ ์ค„์—ฌ์„œ ์ฝ”๋“œ ์•ˆ์ •์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ํ›„ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์ƒํƒœ์™€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋กœ์ง์ด ๋ถ„์‚ฐ๋˜๋ฉด์„œ ๊ด€๋ฆฌ์™€ ๋””๋ฒ„๊น…์ด ๋ณต์žกํ•ด์ง„๋‹ค. ๋˜ํ•œ, ๋ฐ์ดํ„ฐ ์†Œ์œ ๊ถŒ์ด ๋ถˆ๋ช…ํ™•ํ•ด์ง€๊ณ  ์ปดํฌ๋„ŒํŠธ์˜ ์ฑ…์ž„ ๊ฒฝ๊ณ„๊ฐ€ ํ๋ ค์ง€๋Š” ๋‹จ์ ๋„ ์žˆ๋‹ค.

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

  return <Child onFetched={setData} />;
}

function Child({ onFetched }) {
  const data = useFetchData();

  useEffect(() => {
    if (data) onFetched(data);
  }, [onFetched, data]);

  return <>{JSON.stringify(data)}</>;
}

 

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ , ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋ฉด, ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ๋‹จ์ˆœํ•ด์ง€๊ณ  ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ฑ…์ž„ ๋ถ„๋ฆฌ๊ฐ€ ์‰ฌ์›Œ์ง„๋‹ค.

function Parent() {
  const data = useFetchData();
  return <Child data={data} />;
}

function Child({ data }) {
  return <>{JSON.stringify(data)}</>;
}

 

๋งŒ์•ฝ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด, ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ •์˜ํ•œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋กœ์ง์˜ ์ฑ…์ž„์„ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

function Parent() {
  const handleSuccess = (data) => { /* ... */ };
  const handleError = (error) => { /* ... */ };

  return <Child onSuccess={handleSuccess} onError={handleError} />;
}

function Child({ onSuccess, onError }) {
  const { data } = useMutateData({ onSuccess, onError });

  return <>{JSON.stringify(data)}</>;
}

 

 

์ดˆ๊ธฐํ™”


์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ ๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š” ์ดˆ๊ธฐํ™” ๋กœ์ง์€ useEffect์˜ ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋นˆ ๋ฐฐ์—ด []์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

function App() {
  useEffect(() => {
    someOneTimeLogic();
  }, []);
  // ...
}

 

ํ•˜์ง€๋งŒ ๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์—„๊ฒฉ(strict) ๋ชจ๋“œ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋‹ค๋ฉด, ์‚ฌ์ดํŠธ ์ดํŽ™ํŠธ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด useEffect๊ฐ€ ํ•œ ๋ฒˆ ๋” ์‹คํ–‰๋œ๋‹ค.

 

๋˜ํ•œ React 18 ๋ฒ„์ „๋ถ€ํ„ฐ ๋„์ž…๋œ Concurrent Mode๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด, ์ž‘์—…์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์ž‘์—…์ด ์ทจ์†Œ๋œ ํ›„ ๋‹ค์‹œ ์‹œ๋„๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ Œ๋”๋ง์ด ์ค‘๋‹จ๋˜๊ฑฐ๋‚˜ ์žฌ์‹คํ–‰๋˜๋Š” ๊ณผ์ •์—์„œ useEffect๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

 

์ดˆ๊ธฐํ™” ๋กœ์ง์ด ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ๋ณด์žฅํ•˜๋ ค๋ฉด ์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// ๋ฐฉ๋ฒ•1: ์ตœ์ƒ์œ„ ํ”Œ๋ž˜๊ทธ ์‚ฌ์šฉ
let didInit = false;

function App() {
  useEffect(() => {
    if (!didInit) {
      didInit = true;
      someOneTimeLogic();
    }
  }, []);
  // ...
}

// ๋ฐฉ๋ฒ•2: ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ด์ „์— ์ดˆ๊ธฐํ™”
if (typeof window !== "undefined") {
  someOneTimeLogic();
}

function App() {
  // ...
}

 

 

์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ


useEffect ๋‚ด๋ถ€์—์„œ ๋น„๋™๊ธฐ ์ž‘์—… ํ›„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ์‹œ๋„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๋‹ค.

Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

 

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

 

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

// ์–ธ๋งˆ์šดํŠธ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ›…
const useMountedRef = () => {
  const mounted = useRef(true);
  
  useEffect(() => {
    return () => {
      mounted.current = false;
    };
  }, []);
  
  return mounted;
};

const MyCompoenet = () => {
  const [loading, setLoading] = useState(false);
  const mountedRef = useMountedRef();

  const handleDeleteBill = async (id) => {
    setLoading(true);
    await axios.delete(`/bill/${id}`);
    // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜์ง€ ์•Š์•˜์„ ๋•Œ๋งŒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
    if (mountedRef.current) setLoading(false);
  };

  return (
    <button onClick={handleDeleteBill} disabled={loading}>
      Delete Bill
    </button>
  );
};

 

ํ•˜์ง€๋งŒ ๋ฆฌ์•กํŠธ ํŒ€์€ ์ด๋Ÿฌํ•œ ๊ฒฝ๊ณ ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์˜คํžˆ๋ ค ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์ €ํ•ดํ•œ๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ๊ฒฝ๊ณ ๋ฅผ ์ œ๊ฑฐํ–ˆ๋‹ค(์ฐธ๊ณ  ๋งํฌ). React 18 ๋ฒ„์ „๋ถ€ํ„ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋œ ๊ฒฝ์šฐ์—๋„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ  ์—†์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

 

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

const MyCompoenet = () => {
  const [loading, setLoading] = useState(false);

  const handleDeleteBill = async (id) => {
    setLoading(true);
    await axios.delete(`/bill/${id}`);
    setLoading(false);
  };

  return (
    <button onClick={handleDeleteBill} disabled={loading}>
      Delete Bill
    </button>
  );
};

 

๐Ÿ’ก useEffect ๋‚ด๋ถ€์—์„œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ, ํƒ€์ด๋จธ, ์›น์†Œ์ผ“ ๊ตฌ๋… ๋“ฑ์„ ์„ค์ •ํ•˜๋Š” ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‚˜ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

 

๋ ˆํผ๋Ÿฐ์Šค


 

Leave useEffect Alone!

Avoiding unnecessary re-renders in React. Every frontend framework has its inner workings and challenges, React is no different.

medium.com

 

๋ฐ˜์‘ํ˜•