5 min read

๋ฆฌ์•กํŠธ์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด ์•Œ์•„๋ณด๊ธฐ


TL;DR

  • useEffect์—์„œ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ •ํ™•ํžˆ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด react-hooks/exhaustive-deps ESLint ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์˜์กด์„ฑ ๋ฐฐ์—ด์—๋Š” ์ฝ”๋“œ ๋‚ด์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๋ณ€์ˆ˜์™€ ํ•จ์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • exhaustive-deps๊ฒฝ๊ณ ์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
    • useCallback์„ ์‚ฌ์šฉํ•˜์—ฌ useEffect๊ฐ€ ์‹คํ–‰๋  ๋•Œ ํ•จ์ˆ˜๊ฐ€ ์žฌ์ƒ์„ฑ๋˜์ง€ ์•Š๋„๋ก ์ตœ์ ํ™”
    • useEffect ๋‚ด๋ถ€๋กœ ํ•จ์ˆ˜๋ฅผ ์ด๋™์‹œ์ผœ useEffect ๋‚ด์—์„œ๋งŒ ์ •์˜๋˜๋„๋ก ํ•˜์—ฌ ์™ธ๋ถ€์— ๋ณ„๋„์˜ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

exhaustive-deps ๊ฒฝ๊ณ ์˜ ์›์ธ

๋ฆฌ์•กํŠธ์—์„œ useEffect๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ react-hooks/exhaustive-deps ๋ผ๋Š” ESLint ๊ฒฝ๊ณ ๋ฅผ ๋งˆ์ฃผํ•  ๋•Œ๊ฐ€ ๋งŽ์•˜๋‹ค. ์ด ๊ฒฝ๊ณ ๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ์ •ํ™•ํžˆ ์„ค์ •๋˜์ง€ ์•Š์•˜์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ๊ณ ์˜€๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด๊ณผ exhaustive-deps ๊ฒฝ๊ณ ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•ด์•ผํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ด ๊ทœ์น™์ด ๋„์ž…๋œ ๋ฐฐ๊ฒฝ์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด์•˜๋‹ค.

๋ฆฌ์•กํŠธ์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด

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

  • ๋นˆ ๋ฐฐ์—ด์„ ๋„ฃ์œผ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ๋  ๋•Œ effect๊ฐ€ ์‹คํ–‰๋˜๋ฉฐ ๋ฐฐ์—ด์„ ์ƒ๋žตํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค effect๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

์ฃผ์˜ํ•  ์ ์€ Effect์˜ ์˜์กด์„ฑ์„ โ€œ์„ ํƒโ€ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์ด๋‹ค. ์˜์กด์„ฑ ๋ฐฐ์—ด์—๋Š” ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ’์ด ํฌํ•จ๋œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ ์—ฌ๊ธฐ์„œ ๋ฐ˜์‘ํ˜• ๊ฐ’์ด๋ž€ props์™€ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๋ชจ๋“  ๋ณ€์ˆ˜, ํ•จ์ˆ˜๋“ค์ด ํฌํ•จ๋œ๋‹ค. useEffect ๋‚ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์™ธ๋ถ€ ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋ฅผ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋ชจ๋‘ ์ถ”๊ฐ€ํ•ด์•ผํ•˜๋Š”๋ฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค๋ณด๋ฉด ์˜์กด์„ฑ์— ํฌํ•จํ•ด์„œ ๋ถˆํ•„์š”ํ•œ useEffect์˜ ์‹คํ–‰์„ ์ผ์œผํ‚ค๋Š” ๋ฐ˜์‘ํ˜• ๊ฐ’์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐ˜์‘ํ˜• ๊ฐ’์„ ์ œ๊ฑฐํ•œ๋‹ค๋ฉด exhaustive-deps ๊ฒฝ๊ณ ๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ  ์ด๋ฅผ ๋‹จ์ˆœํžˆ // eslint-disable-next-line react-hooks/exhaustive-deps ์ฃผ์„์„ ์‚ฌ์šฉํ•ด์„œ ๊ทœ์น™์„ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์ฝ”๋“œ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜์—ฌ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ•œ๋‹ค.

์˜์กด์„ฑ ๋ฐฐ์—ด ๊ฒฝ๊ณ ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…

๋จผ์ € ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ฒ˜์Œ์—” ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค.

import { useState, useEffect } from 'react';

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

// ๋ฐ์ดํ„ฐ fetching ํ•จ์ˆ˜
async function fetchData() {
  const response = await fetch(`https://api.example.com/search?q=${searchKeyWord}`);
  const result = await response.json();
  setData(result);
}

useEffect(() => {
    fetchData();
}, []);

  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

useEffect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ๋นˆ ๋ฐฐ์—ด๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค. searchKeyWord๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ useEffect๊ฐ€ ์žฌ์‹คํ–‰๋˜์ง€ ์•Š์•„ ๊ฒ€์ƒ‰์–ด๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ fetchingํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด useEffect ์•ˆ์œผ๋กœ fetchData ํ•จ์ˆ˜๋ฅผ ์ด๋™ํ•˜๊ณ  ์˜์กด์„ฑ ๋ณ€์ˆ˜์— searchKeyWord ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

useEffect(() => {
  async function fetchData() {
    const response = await fetch(`https://api.example.com/search?q=${searchKeyWord}`);
    const result = await response.json();
    setData(result);
  }

  fetchData();
}, [searchKeyWord]);

์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด์„œ searchKeyWord ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค fetchData ์‹คํ–‰๋˜์–ด ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธ๋œ๋‹ค.

์™œ ์ด ๋ฐฉ์‹์ด ๋” ๋‚˜์„๊นŒ?

  1. ์šฐ์„  ์˜์กด์„ฑ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ดํ•ด์ง„๋‹ค. fetchData๊ฐ€ useEffect ๋‚ด์—์„œ๋งŒ ์ •์˜๋˜๋ฉด ์™ธ๋ถ€์— ๋ณ„๋„์˜ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•  ํ•„์š” ์—†์ด useEffect์—์„œ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ„ํŽธํ•˜๋‹ค.
  2. ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋œ๋‹ค. ํ•จ์ˆ˜๊ฐ€ useEffect ์•ˆ์— ๋ฌถ์—ฌ ์žˆ์–ด ์ฝ”๋“œ ํ๋ฆ„์ด ํ•œ๋ˆˆ์— ๋“ค์–ด์˜ค๊ณ  ํ›…์ด ์‹คํ–‰๋  ๋•Œ ๋ฌด์—‡์ด ์‹คํ–‰๋˜๋Š”์ง€ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  3. searchKeyWord๋ฅผ ํด๋กœ์ €๋กœ ์ฐธ์กฐํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์ด์ „ ๊ฐ’์ด ๊ณ„์† ์ฐธ์กฐ๋  ์œ„ํ—˜์ด ์žˆ์ง€๋งŒuseEffect ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์ถ”๊ฐ€๋˜์–ด ์ด ๊ฐ’๋“ค์„ ์ตœ์‹  ์ƒํƒœ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ชจ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ’์„ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์–ด์•ผ ํ• ๊นŒ?

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

์•„๋ž˜์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ํ•จ์ˆ˜๊ฐ€ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋‚˜ props์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด getFetchUrl ํ•จ์ˆ˜๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ ์™ธ๋ถ€๋กœ ๋‘์–ด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// โœ… Not affected by the data flow
function getFetchUrl(query) {
  return 'https://hn.algolia.com/api/v1/search?query=' + query;
}

function SearchResults() {
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, []); // โœ… Deps are OK

  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, []); // โœ… Deps are OK

  // ...
}

ํ•จ์ˆ˜๊ฐ€ ์ƒํƒœ๋‚˜ props์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” useCallback ์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. getFetchUrl ํ•จ์ˆ˜๊ฐ€ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํฌํ•จ๋˜์–ด๋„ getFetchUrl ์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ useEffect๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์žฌ์‹คํ–‰๋˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.

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

exhaustive-deps ๊ทœ์น™์ด ๋„์ž…๋œ ๋ฐฐ๊ฒฝ

๋ฆฌ์•กํŠธ ํ›…์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜๋Š” stale closure ๋ฌธ์ œ์ด๋‹ค. ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒํƒœ๋‚˜ props๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์ตœ์‹  ๊ฐ’์„ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๊ณ  ์ด์ „ ๊ฐ’์„ ๊ณ„์† ์ฐธ์กฐํ•˜๊ฒŒ ๋˜์–ด ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ •ํ™•ํžˆ ์„ค์ •ํ•˜๋„๋ก ํ•ด๋‹น ๊ทœ์น™์ด ๋„์ž…์ด ๋˜์—ˆ๋‹ค. ์ด ๊ทœ์น™์„ ๋”ฐ๋ฅธ๋‹ค๋ฉด ํ›…์˜ ์˜๋„๋œ ๋™์ž‘์„ ๋ณด์žฅํ•˜๊ณ  ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ๋ฒ„๊ทธ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

exhaustive-deps ๊ทœ์น™์„ ์ง€ํ‚ค๋Š” ์ด์œ 

  • ์˜์กด์„ฑ์„ ์ •ํ™•ํžˆ ๋‚˜์—ดํ•˜๋ฉด ํ•ด๋‹น ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ํ›…์ด ๋‹ค์‹œ ์‹คํ–‰๋˜๋ฏ€๋กœ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜์กด์„ฑ์„ ์ •ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•จ์œผ๋กœ์จ ๋ฆฌ์•กํŠธ๊ฐ€ ์žฌ๋ Œ๋”๋ง์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. (useCallback)
  • ์ž˜๋ชป๋œ ์˜์กด์„ฑ์œผ๋กœ ์ธํ•œ ๋ฒ„๊ทธ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ