๐Ÿ’ก 06. useEffect ํŒŒ๊ดดํ•˜๊ธฐ (2): ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ๊ฑฐ์ง“๋ง

๐Ÿ“‹ ๊ฐœ์š”

lint ๊ฒฝ๊ณ ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์†์ผ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌดํ•œ ๋ฃจํ”„ ์‚ฌํƒœ์™€, ์˜ฌ๋ฐ”๋ฅธ ํ•จ์ˆ˜/๊ฐ์ฒด ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • IDE(์—๋””ํ„ฐ)๊ฐ€ ๋„์›Œ์ฃผ๋Š” react-hooks/exhaustive-deps ๊ฒฝ๊ณ ๋ฅผ ์™œ ์ ˆ๋Œ€ ๋ฌด์‹œํ•˜๋ฉด ์•ˆ ๋˜๋Š”์ง€ ๊นจ๋‹ซ๋Š”๋‹ค.
  • ํ•จ์ˆ˜๋‚˜ ๊ฐ์ฒด๋ฅผ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์—ˆ์„ ๋•Œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ํƒ€์ด๋จธ๊ฐ€ ๊ณ„์† ์žฌ์‹œ์ž‘๋˜๋Š” ์›์ธ(๋ฉ”๋ชจ๋ฆฌ ์ฐธ์กฐ๊ฐ’ ๋ฆฌ์…‹)์„ ์™„์ „ํžˆ ์ดํ•ดํ•œ๋‹ค.
  • ํšจ๊ณผ ๋‚ด๋ถ€์—์„œ ํ•„์š”ํ•œ ํ•จ์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ๋ฐฐ์น˜ํ•ด์•ผ ๋ฌดํ•œ ๋ Œ๋” ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์šฐ์•„ํ•œ ์„ค๊ณ„๋ฒ•์„ ์ฒด๋“ํ•œ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 15๋ถ„ / ํ•ต์‹ฌ ํŒŒํŠธ: 10๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ฒ (์‹ ์ž…): "์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋ญ ๋„ฃ์œผ๋ผ๊ณ  ๋…ธ๋ž€์ƒ‰ lint ๊ฒฝ๊ณ ์ค„์ด ์—„์ฒญ ๋œจ๋Š”๋ฐ, ๋„ฃ์œผ๋‹ˆ๊นŒ ํŽ˜์ด์ง€๊ฐ€ ๋ฌดํ•œ ์ƒˆ๋กœ๊ณ ์นจ ๋˜๋ฉด์„œ ๋ป—์–ด๋ฒ„๋ฆฌ๋˜๋ฐ์š”? ๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ ๊ฒฝ๊ณ  ๋ฌด์‹œํ•˜๋Š” ์ฃผ์„(// eslint-disable-next-line) ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค!"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜... ๋ฆฌ์•กํŠธ์—๊ฒŒ ๊ฑฐ์ง“๋ง์„ ํ•˜๋ฉด ๊ทธ ๋Œ€๊ฐ€๋Š” ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ € ๋ฉ”๋ชจ๋ฆฌ ํญ๋ฐœ๋กœ ๋Œ์•„์˜ต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋‹น์žฅ ๊ทธ ์ฃผ์„๋ถ€ํ„ฐ ์ง€์šฐ์‹œ์ฃ ."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: ๋ฆฌ์•กํŠธ์— ๊ฑฐ์ง“๋ง์„ ํ•œ ๋Œ€๊ฐ€

useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด ๊ฑฐ์˜ ๋ชจ๋“  ์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋“ค์ด ์˜์กด์„ฑ ๋ฐฐ์—ด(Dependency Array, [])์˜ ์žฅ๋ฒฝ์— ๋ถ€๋”ชํž™๋‹ˆ๋‹ค.

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
ํŠน์ • ์˜ต์…˜(options)์ด ๋ฐ”๋€Œ๋ฉด API ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•ด.
์™œ ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฌดํ•œ ๋ฃจํ”„(์š”์ฒญ ํญ๊ฒฉ๊ธฐ)์— ๋น ์ ธ์„œ ๋ฐฑ์—”๋“œ(์˜์ˆ˜) ์„œ๋ฒ„๊ฐ€ ํ„ฐ์ ธ๋ฒ„๋ฆด๊นŒ?

// โŒ ์˜์ฒ ์ด์˜ ๋ฐฑ์—”๋“œ ํ…Œ๋Ÿฌ ์ฝ”๋“œ (DDOS ์ˆ˜์ค€)
function ProductList() {
  const [data, setData] = useState(null);
  
  // ์˜์ฒ : "์•„! ์กฐ๊ฑด ์˜ต์…˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‘ฌ์•ผ์ง€."
  const options = { limit: 10, sort: 'desc' };
 
  // ์˜์ฒ : "API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” effect๋‹ˆ๊นŒ, options ์˜ต์…˜์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜์–ด์•ผ์ง€!"
  useEffect(() => {
    fetchData('/api/products', options).then((res) => setData(res));
  }, [options]); // ๐Ÿšจ ์—๋Ÿฌ ํญํƒ„ ํŠธ๋ฆฌ๊ฑฐ! ๋ฌดํ•œ ๋ฃจํ”„!
 
  return <div>{JSON.stringify(data)}</div>;
}

VS Code๋Š” ์˜์กด์„ฑ์— ๋Œ€ํ•ด ์–ด๋– ํ•œ ์—๋Ÿฌ๋„ ๋„์šฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฌดํ•œํžˆ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉฐ ์ดˆ๋‹น 10๋ฒˆ์”ฉ ์„œ๋ฒ„์— API ์š”์ฒญ์„ ๋•Œ๋ฆฝ๋‹ˆ๋‹ค.

์˜์ฒ ์ด๋Š” ๋‹นํ™ฉํ•ด์„œ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ˆ˜๋™์œผ๋กœ [] ๋นˆ ๊ป๋ฐ๊ธฐ๋กœ ๋ฐ”๊พธ๊ณ (๊ฑฐ์ง“๋ง), ๋…ธ๋ž€ ๊ฒฝ๊ณ ์ค„์„ ๋ฌด์‹œํ•˜๋Š” ์ฃผ์„์„ ๋‹ฌ์•„๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๊ณผ์—ฐ ์ด ์ฝ”๋“œ๋Š” ๋ฌด์—‡์ด ๋ฌธ์ œ์˜€์„๊นŒ์š”?


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
์—„๋งˆ(๋ฆฌ์•กํŠธ)๊ฐ€ ์˜์ฒ ์ด์—๊ฒŒ ์ž”์†Œ๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. "๋„ค๊ฐ€ ์†์— ์ฅ๊ณ  ์žˆ๋Š” ๊ณผ์ž ๋ด‰์ง€(์˜์กด์„ฑ options)๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋‚ด๊ฐ€ ์ €๊ธฐ ๊ฐ€์„œ ์ฐฝ๋ฌธ ์—ด์–ด์ค„๊ฒŒ(useEffect ์‹คํ–‰)."

  1. ๋ฐฉ์„ ์ƒˆ๋กœ ๊พธ๋ฐ€ ๋•Œ๋งˆ๋‹ค(๋ฆฌ๋ Œ๋”๋ง) ๋ฆฌ์•กํŠธ๋Š” options = { limit: 10 } ์ด๋ผ๋Š” ์ƒˆ ๊ณผ์ž ๋ด‰์ง€๋ฅผ ํ•˜๋‚˜ ๋œฏ์Šต๋‹ˆ๋‹ค.
  2. ์—„๋งˆ๊ฐ€ ๋ฌป์Šต๋‹ˆ๋‹ค. "์ €๋ฒˆ ๋ Œ๋”๋ง ์Šค๋ƒ…์ƒท ๋•Œ ์ฅ” ๊ณผ์ž๋ด‰์ง€(๊ณผ๊ฑฐ options)๋ž‘, ๋ฐฉ๊ธˆ ์ƒˆ๋กœ ์„ ์–ธ๋œ ๊ณผ์ž๋ด‰์ง€(ํ˜„์žฌ options)๋ž‘ ๋˜‘๊ฐ™์•„?"
  3. ์˜์ฒ ์ด๊ฐ€ ๋Œ€๋‹ตํ•ฉ๋‹ˆ๋‹ค. "๋‚ด์šฉ๋ฌผ(10)์€ ๊ฐ™์€๋ฐ์š”, ํฌ์žฅ์ง€ ๊ป๋ฐ๊ธฐ(๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ’)๊ฐ€ ์™„์ „ ์ƒˆ ๊ฑฐ์˜ˆ์š”! ๋ฌด์กฐ๊ฑด ์ƒˆ๋กœ ๋œฏ์—ˆ๊ฑฐ๋“ ์š”!" (๊ฐ์ฒด ์–•์€ ๋™๋“ฑ์„ฑ ์‹คํŒจ)
  4. ์—„๋งˆ: "์–ด? ๊ณผ์ž๊ฐ€ ๋ฐ”๋€Œ์—ˆ๋„ค! ์ฐฝ๋ฌธ ๋‹ค์‹œ ์—ฐ๋‹ค!! (useEffect ์‹คํ–‰ -> setData ํ˜ธ์ถœ -> ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ -> 1๋ฒˆ์œผ๋กœ ๋Œ์•„๊ฐ€ ๋ฌดํ•œ ๋ฐ˜๋ณต)"

โœ… ํ•ต์‹ฌ ์›๋ฆฌ:
03. ๋ถˆ๋ณ€์„ฑ ๋ฌธ์„œ์—์„œ ๋ฐฐ์› ์ง€? ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด, ํ•จ์ˆ˜๋ฅผ ๋งค๋ฒˆ ํ˜ธ์ถœ(์ƒˆ ๋ฆฌ๋ Œ๋”๋ง)ํ•  ๋•Œ๋งˆ๋‹ค ๋‚ด์šฉ์ด ์™„๋ฒฝํžˆ ๋˜‘๊ฐ™์•„๋„ ํž™(Heap) ๋ฉ”๋ชจ๋ฆฌ์— ์ƒˆ๋กœ์šด ์ฃผ์†Œ๋กœ ํ• ๋‹นํ•ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
์ด ๊ฐ์ฒด/ํ•จ์ˆ˜ ์ฐธ์กฐ(Reference)๊ฐ€ useEffect์˜ ์˜์กด์„ฑ ๊ฐ์‹œ๊ธฐ(๊ฐ์‹œ๋ด‡)์— ๋“ค์–ด๊ฐ€๋ฉด, ๊ฐ์‹œ๋ด‡์€ "์•—! ์ฃผ์†Œ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋‹ค! ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ์—ˆ๋„ค!" ๋ผ๊ณ  ์ฐฉ๊ฐํ•˜๊ณ  ๋ฌด์กฐ๊ฑด Effect ํ›…์„ ์žฌ์‹คํ–‰ํ•ด ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. Effect ์•ˆ์—์„œ setData๊นŒ์ง€ ์ณค์œผ๋‹ˆ, ์ด๋Š” ๋์—†๋Š” ์žฌ๋ Œ๋”๋ง๊ณผ Effect ์žฌํ˜ธ์ถœ์˜ ๋ฌด๊ฐ„์ง€์˜ฅ(๋ฌดํ•œ ๋ฃจํ”„)์ด ๋˜๋Š” ๊ฒ๋‹ˆ๋‹ค.


๐Ÿงฉ ํ•จ์ˆ˜์™€ ๊ฐ์ฒด, ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฅ์–ด์ฃผ๊ธฐ

โŒ ์˜์ฒ ์ด์˜ ํšŒํ”ผ ๊ธฐ๋™

์˜์ฒ ์ด๋Š” ์ด ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๊ฒช์ž, ๋ฌด์„œ์›Œ์„œ ๊ฐ•์ œ๋กœ ์˜์กด์„ฑ ๋ฐฐ์—ด์—์„œ options๋ฅผ ์ง€์›Œ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

  useEffect(() => {
    fetchData('/api/products', options).then((res) => setData(res));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // ๐Ÿšจ ๋ฆฌ์•กํŠธ์— ๊ฑฐ์ง“๋ง์„ ํ–ˆ์Œ.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฃจํ”„๋Š” ๋ฉˆ์ถฅ๋‹ˆ๋‹ค! ํ•˜์ง€๋งŒ ์ง„์งœ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ๊ฐ€ ์ž ๋ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์œ ์ €๊ฐ€ ์˜ต์…˜์„ '10๊ฐœ ๋ณด๊ธฐ'์—์„œ '50๊ฐœ ๋ณด๊ธฐ'๋กœ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ณ€๊ฒฝํ•˜๋ฉด, options ๋ณ€์ˆ˜๋Š” ๋ฐ”๋€Œ์ง€๋งŒ useEffect๋Š” ์ž์‹ ์ด ์˜์กดํ•˜๋Š” ๊ฐ’์ด ์•ˆ ๋ฐ”๋€Œ์—ˆ๋‹ค([]์ด๋ฏ€๋กœ ๋ณ€ํ•  ์ผ ์—†์Œ)๊ณ  ์ƒ๊ฐํ•ด์„œ ํ‰์ƒ ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”(Fetch)ํ•ด ์˜ค์ง€ ์•Š๋Š” ์–ผ์–ด๋ถ™์€ UI ์ƒํƒœ ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ์—๊ฒŒ ๊ฑฐ์ง“๋ง์„ ํ•œ ๋Œ€๊ฐ€์ฃ .

โœ… ์˜ํ˜ธ์˜ ๋ฆฌํŒฉํ† ๋ง: ์ปดํฌ๋„ŒํŠธ ๋ฐ–์œผ๋กœ ์น˜์šฐ๊ฑฐ๋‚˜ ํ’€์–ดํ—ค์ณ๋ผ

๋ฐฉ๋ฒ• 1. ์–ด์ฐจํ”ผ ๊ณ ์ •์ธ ๋ฐ์ดํ„ฐ๋ฉด ์ปดํฌ๋„ŒํŠธ ์Šค์ฝ”ํ”„ ๋ฐ–์œผ๋กœ ํƒˆ์˜ฅ์‹œ์ผœ๋ผ!
์˜์กด์„ฑ์— ๊ฑธ๋ฆฌ์ ๊ฑฐ๋ฆฌ๋Š” ๊ฐ์ฒด๊ฐ€ ์ƒํƒœ(State)์™€ ์ „ํ˜€ ๋ฌด๊ด€ํ•œ ์ •์ (Static) ํ™˜๊ฒฝ ์„ค์ •์ด๋ผ๋ฉด, ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋ฐ–(๋ชจ๋“ˆ ์Šค์ฝ”ํ”„)์œผ๋กœ ์˜ฌ๋ฆฌ๋ฉด ๋๋‚ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ํ‰์ƒ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ ์ฐธ์กฐ ์ฃผ์†Œ๊ฐ€ ๋ฐ”๋€” ์ผ์ด ์—†์ฃ .

// โœ… ์šฐ์•„ํ•œ ํƒˆ์ถœ (Pro Approach 1)
const options = { limit: 10, sort: 'desc' }; // ๋ฐ–์œผ๋กœ ๋นผ๋ฒ„๋ฆฌ๊ธฐ!
 
function ProductList() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    // ์ด์ œ options๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ๊ฐ์‹œ๋Œ€์ƒ์—์„œ ์™„๋ฒฝํžˆ ๋ฉด์ œ๋ฉ๋‹ˆ๋‹ค.
    fetchData('/api/products', options).then((res) => setData(res));
  }, []); 
}

๋ฐฉ๋ฒ• 2. ๊ฐ์ฒด ๋ง๊ณ  ์›์‹œ๊ฐ’(Primitive) ๊ทธ ์ž์ฒด๋กœ ๋ถ„ํ•ดํ•ด์„œ ๊ฐ์‹œํ•ด๋ผ!
๋‚˜์ค‘์— ๋ฒ„ํŠผ์œผ๋กœ limit์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋Š” (State์™€ ์—ฐ๋™๋˜๋Š”) ์œ ๋™์  ๊ฐ์ฒด๋ผ๋ฉด? ๊ป๋ฐ๊ธฐ(๊ฐ์ฒด ์ฐธ์กฐ)๋ฅผ ํŒŒ๊ดดํ•˜๊ณ  ์†์„ฑ ์•Œ๋งน์ด(์ˆซ์ž, ๋ฌธ์ž์—ด ๋“ฑ ์›์‹œ ํƒ€์ž…)๋กœ ์ง์ ‘ ํ’€์–ดํ—ค์ณ์„œ ๊ฐ์‹œ๋ง์— ๋„ฃ์œผ์„ธ์š”.

// โœ… ์›์‹œ๊ฐ’ ์ถ”์ถœ๋ฒ• (Pro Approach 2)
function ProductList({ limit }) { // limit๋Š” props (์ˆซ์ž ํƒ€์ž…)
  const [data, setData] = useState(null);
  
  // ๊ฐ์ฒด๋ฅผ ๋ Œ๋”๋ง ๋‚ด๋ถ€์— ์„ ์–ธํ•˜์ง€ ๋ง๊ณ  ๋ฆฌํ„ฐ๋Ÿด๋กœ ์งํˆฌ์ž…
  useEffect(() => {
    fetchData('/api/products', { limit: limit, sort: 'desc' })
      .then((res) => setData(res));
  // ๐ŸŽฏ ๊ฐ์ฒด ๊ป๋ฐ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์›์‹œ๊ฐ’(์ˆซ์ž ํ˜•์˜ limit)์„ ์˜์กด์„ฑ์œผ๋กœ!
  }, [limit]); 
}

ํ•จ์ˆ˜(Functions)๋ฅผ Effect ์œ„์—์„œ ๋ถ€๋ฅผ ๋•Œ์˜ ๊ทœ์น™

ํ•จ์ˆ˜๋„ ๊ฐ์ฒด์™€ 100% ๋™์ผํ•˜๊ฒŒ ์ทจ๊ธ‰๋˜์–ด, ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ๊ป๋ฐ๊ธฐ(์ฃผ์†Œ)๊ฐ€ ๋ฐ”๋€๋‹ˆ๋‹ค. fetchProduct() ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ๋ฌด์‹ฌ์ฝ” Effect ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๊ณ  ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์œผ๋ฉด ๋ฌดํ•œ๋ฃจํ”„๋ฅผ ํƒ€์ฃ .
์ด ๋”œ๋ ˆ๋งˆ๋ฅผ ํ‘ธ๋Š” ๊ธฐ๋ฒ•์€ ์•„์ฃผ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ตœ๊ณ ์˜ ๋ฐฉ๋ฒ•: ์ •๋ง useEffect ์•ˆ์—์„œ๋งŒ ์“ฐ๋Š” ํ•จ์ˆ˜๋ผ๋ฉด, ํ•จ์ˆ˜ ์ •์˜ ์ž์ฒด๋ฅผ ์•„์˜ˆ useEffect ๊ด„ํ˜ธ ์•ˆ์ชฝ ๋ฐฉ๊ตฌ์„์œผ๋กœ ๋ฐ€์–ด ๋„ฃ์œผ์„ธ์š”. ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๊ทธ ํ•จ์ˆ˜ ์ด๋ฆ„์„ ํ†ต์งธ๋กœ ์ ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.
  2. ๋ถ€๋“์ดํ•˜๊ฒŒ ์ปดํฌ๋„ŒํŠธ ๋‹ค๋ฅธ ๊ณณ(๋ฒ„ํŠผ ํด๋ฆญ ๋“ฑ)์—์„œ๋„ ๊ทธ ๋ฐ”๊นฅ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค๋ฉด? ๊ทธ๋•Œ๋งŒ ์–ด์ฉ” ์ˆ˜ ์—†์ด useCallback์œผ๋กœ ๊ป๋ฐ๊ธฐ(์ฐธ์กฐ)๋ฅผ ์–ผ๋ ค๋ฒ„๋ฆฌ๊ณ  ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

์ƒํ™ฉ (๋ Œ๋”๋ง ๊ฐฑ์‹  ์‹œ์ )โŒ ๋‚˜์œ ๋Œ€์‘โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฉ˜ํƒˆ ๋ชจ๋ธ (ํ•ด๊ฒฐ์ฑ…)
์ƒํƒœ ๊ฐฑ์‹  ์—†๋Š” ์ •์  ๊ฐ์ฒด/ํ•จ์ˆ˜๋ Œ๋”๋ง ๋‚ด๋ถ€์— ์„ ์–ธ + ๋ฌด์‹œ ์ฃผ์„์ปดํฌ๋„ŒํŠธ ๋ฐ”๊นฅ ๊ผญ๋Œ€๊ธฐ(Module Scope)๋กœ ์ด์‚ฌ
props ์˜ํ–ฅ๋ฐ›๋Š” ๋™์  ๊ฐ์ฒด๊ฐ์ฒด ๊ป๋ฐ๊ธฐ ์ฑ„๋กœ []์— ๋ฐ•์Œ๋ถ„ํ•ด(destructuring)ํ•˜์—ฌ ๊ธฐ๋ณธ ํƒ€์ž…(string, number ๋“ฑ ์›์‹œ๊ฐ’) ์†์„ฑ์„ ์ถ”์ถœํ•ด []์— ๋ฐ•์Œ
๋”ฑ ํ•œ ๊ณณ์—์„œ๋งŒ ์“ฐ๋Š” ํ•จ์ˆ˜ํ•จ์ˆ˜ ๋ถ„๋ฆฌ ํ›„ useCallback ๋‚จ์šฉํ•„์š”๋กœ ํ•˜๋Š” useEffect ๋‚ด๋ถ€ ๋ฐฉ๊ตฌ์„์œผ๋กœ ํ•จ์ˆ˜ ์ •์˜๋ฅผ ๋ฐ€์–ด๋„ฃ์Œ

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
๋‹จ์–ธ์ปจ๋Œ€, ๋…ธ๋ž€์ƒ‰ lint ๊ฒฝ๊ณ (exhaustive-deps)๊ฐ€ ๋œจ๋ฉด ๋ฌด์‹œ ์ฃผ์„์„ ์น˜๋Š” ๋Œ€์‹  ๋‹น์‹ ์˜ ์ปดํฌ๋„ŒํŠธ ๊ฐ์ฒด/ํ•จ์ˆ˜ ๋ถ„๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ๋”๋Ÿฝ๋‹ค๋Š” ๊ฒƒ์„ ์‹œ์ธํ•˜๋ผ. ๋ฆฌ์•กํŠธ์˜ ๊ฐ์‹œ๋ด‡์€ ๊ฐ์ฒด์™€ ํ•จ์ˆ˜์˜ ๋‚ด์šฉ๋ฌผ์€ ๊นŒ๋ณด์ง€๋„ ์•Š์€ ์ฑ„, ๊ทธ์ € '๊ป๋ฐ๊ธฐ(ํฌ์žฅ์ง€)๊ฐ€ ์ƒˆ๋กœ ๋‚˜์™”๊ตฐ' ํ•˜๋ฉฐ ๋ฌด์กฐ๊ฑด ์‚ฌ์ด๋ Œ์„ ์šธ๋ฆด ๋ฟ์ด๋‹ค.


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์ง„์งœ ์—ฌํƒœ๊ป ๋‚ด๊ฐ€ ์งฐ๋˜ ์ฝ”๋“œ ์ค‘์— ์กฐ์šฉํžˆ ์ข€๋น„์ฒ˜๋Ÿผ ๋ฉˆ์ถฐ์žˆ๋˜ ๋ฒ„๊ทธ๋“ค์ด ๋‹ค ์˜์กด์„ฑ ๋ฐฐ์—ด ๊ฑฐ์ง“๋ง ๋•Œ๋ฌธ์ด์—ˆ๋‹ค๋‹ˆ. ๋…ธ๋ž€ ์ค„ ๋„์šฐ๋Š” lint๊ฐ€ ๋‚˜๋ฅผ ๊ดด๋กญํžˆ๋Š” ์•…๋งˆ์ธ ์ค„ ์•Œ์•˜๋Š”๋ฐ ์‚ฌ์‹ค ๋‚ด ์ƒ๋ช…์ค„์„ ์žก์•„์ฃผ๊ณ  ์žˆ์—ˆ๋˜ ๊ฑฐ๋‹ค.

๐Ÿ’ก "๋ฆฌ์•กํŠธ์—๊ฒŒ ๊ฑฐ์ง“๋ง ์น˜์ง€ ๋ง์ž! ๊ฐ์ฒด ๊ป๋ฐ๊ธฐ์™€ ํ•จ์ˆ˜ ์ฐธ์กฐ๊ฐ’์ด ๋งค๋ฒˆ ์ƒˆ๋กœ ๋ถ•์–ด๋นต ์ฐํžˆ๋“ฏ ๋งŒ๋“ค์–ด์ง€๋Š” ์›๋ฆฌ๋ฅผ ์•Œ๋ฉด, ์˜์กด์„ฑ ๋ฐฐ์—ด์€ ๋ฌด์„ญ์ง€ ์•Š๋‹ค."

๊ธฐํš์ด ๋ฐ”๋€Œ๋ฉด ์–ธ์ œ๋“ ์ง€ ํ„ฐ์งˆ ์‹œํ•œํญํƒ„์„ ๋งŒ๋“ค๊ณ  ์žˆ์—ˆ๋‹ค๋‹ˆ ์‹์€๋•€์ด ๋‚œ๋‹ค. ๊ทธ๋ž˜๋„ ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ์–ด๋–ป๊ฒŒ๋“  ์›์‹œ๊ฐ’ ๋ฝ‘์•„๋‚ด๊ฑฐ๋‚˜ ํ•จ์ˆ˜ ์œ„์น˜๋ฅผ ๊นŠ์ˆ™์ด ๋„ฃ์–ด์„œ ํ•ด๊ฒฐํ•˜๋Š” ๊ฟ€ํŒ์„ ๋‹ค ์ „์ˆ˜ํ•ด ์ฃผ์…จ๋‹ค. ์˜ค๋Š˜์€ ๋ญ”๊ฐ€ ๊นจ๋‹ฌ์Œ์˜ ๋ฐ€๋„๊ฐ€ ๋Œ€๋‹จํžˆ ๋†’์•˜๋‹ค. ๊ธฐ๋ถ„ ์ข‹๊ฒŒ ํ‡ด๊ทผํ•ด์„œ ๋„ทํ”Œ๋ฆญ์Šค ํ•œ ํŽธ ๋•Œ๋ฆฌ๊ณ  ๊ฟ€์ž  ๊ฐ€์•ผ๊ฒ ๋‹ค!


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์•„๋ž˜ ์˜์ฒ ์ด๊ฐ€ ๋ฌดํ•œ ๋ Œ๋”๋ง ํญํƒ„์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์ง  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. lint์—์„œ fetchUserData ํ•จ์ˆ˜๋ฅผ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ์œผ๋ผ๊ณ  ๊ฒฝ๊ณ ํ•˜์ง€๋งŒ, ์˜์ฒ ์ด๋Š” ๊ทธ๋ƒฅ ์ฃผ์„์œผ๋กœ ๋ฌด์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์˜ฌ๋ฐ”๋ฅด๊ณ  ๊น”๋”ํ•œ ๋ฆฌํŒฉํ† ๋ง ํ•ด๊ฒฐ์ฑ…์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

function UserCard({ userId }) {
  const [user, setUser] = useState();
 
  // ๋ฐ–์—์„œ ์„ ์–ธํ•ด๋ฒ„๋ฆผ!
  const fetchUserData = async () => {
    const res = await fetch(`/api/user/${userId}`);
    setUser(res);
  };
 
  useEffect(() => {
    fetchUserData();
    // eslint-disable-next-line
  }, [userId]); 
 
  // ...
}
  • A) fetchUserData ํ•จ์ˆ˜๋ฅผ useCallback์œผ๋กœ ๋ฌด์กฐ๊ฑด ๊ฐ์‹ธ๊ณ  ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋„ฃ๋Š”๋‹ค.
  • B) fetchUserData ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ์•„์˜ˆ useEffect ๊ด„ํ˜ธ ๋‚ด๋ถ€ ๊นŠ์ˆ™์ด(์ฝœ๋ฐฑ ์•ˆ์ชฝ์œผ๋กœ) ์ด๋™์‹œํ‚จ๋‹ค. (๋‹ค๋ฅธ ๋ฒ„ํŠผ์—์„œ ์ด ํ•จ์ˆ˜๋ฅผ ๋ˆ„๋ฅผ ์ผ์ด ์—†์œผ๋ฏ€๋กœ)
  • C) ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋Œ๋”๋ผ๋„ ๊ฒฝ๊ณ  ์ฃผ์„์„ ํฌ๊ธฐํ•˜๊ณ  ์˜์กด์„ฑ์— [userId, fetchUserData]๋ฅผ ๋ชฝ๋•… ๋„ฃ๋Š”๋‹ค.
  • D) useRef๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•จ์ˆ˜๋ฅผ ๊ทธ ์•ˆ์— ์ˆจ๊ฒจ ๋„ฃ๋Š”๋‹ค.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ๊ฐ€์žฅ ํ”ํ•˜๋ฉด์„œ ๊ณ ๊ท€ํ•œ ํ•ด๊ฒฐ์ฑ…์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ์™ธ๋ถ€ ํด๋ฆญ ์ด๋ฒคํŠธ ๋“ฑ ์–ด๋А ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์“ฐ์ด์ง€ ์•Š๊ณ  ์˜ค๋กœ์ง€ ๋งˆ์šดํŠธ/๋™๊ธฐํ™” ์‹œ์ ์— ๋”ฑ ํ•œ ๋ฒˆ ์ € Effect ์•ˆ์—์„œ๋งŒ ๋ฐœ๋™๋˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด, ๋ฐ–์—์„œ ์“ธ๋ฐ์—†์ด ๋ฉ”๋ชจ๋ฆฌ ๊ป๋ฐ๊ธฐ(์ฐธ์กฐ)๋ฅผ ์ƒˆ๋กœ ๋ถ•์–ด๋นต ์ฐ์–ด๋‚ผ ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. useEffect(() => { const fetchUserData = async () => { ... }; fetchUserData(); }, [userId]); ์ฒ˜๋Ÿผ ์ด์‚ฌ์‹œํ‚ค๋ฉด, lint ๊ฒฝ๊ณ ๋„ ์™„๋ฒฝํžˆ ์‚ฌ๋ผ์ง€๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋„ ๊น”๋”ํ•ด์ง€๋ฉฐ '๋ถˆํ•„์š”ํ•œ useCallback ์—ฐ์‚ฐ'๋งˆ์ € ์•„๋‚„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

Q2. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ Primitive(์›์‹œ) ํƒ€์ž…๊ณผ Reference(์ฐธ์กฐ) ํƒ€์ž…์„ Effect๊ฐ€ ๊ฐ์‹œํ•  ๋•Œ ๋‚˜ํƒ€๋‚˜๋Š” ์ฐจ์ด๋ฅผ ๋ฌป๋Š” ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์ค‘ useEffect์˜ ๊ด„ํ˜ธ [ ] ๋ฐฐ์—ด์— ๋‹จ๋…์œผ๋กœ ๋ฐฐ์น˜๋˜์—ˆ์„ ๋•Œ, ๋งค ๋ Œ๋”๋ง ์‹œ๋งˆ๋‹ค ๋‚ด์šฉ๋ฌผ์ด ๊ฐ™๋”๋ผ๋„ "๋ฐ”๋€Œ์—ˆ๋‹ค!"๊ณ  ๋ฆฌ์•กํŠธ๊ฐ€ 100% ์ฐฉ๊ฐํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํƒ€์ž…๋“ค๋งŒ ๊ณ ๋ฅธ ๊ฒƒ์€?

  • A) 200, 'success', false (์ˆซ์ž, ๋ฌธ์ž, ๋ถˆ๋ฆฌ์–ธ)
  • B) {}, [], () => {} (๋นˆ ๊ฐ์ฒด, ๋นˆ ๋ฐฐ์—ด, ํ™”์‚ดํ‘œ ํ•จ์ˆ˜)
  • C) null, undefined, NaN
  • D) ์ด ์ค‘ ์ •๋‹ต ์—†์Œ (๋ฆฌ์•กํŠธ๋Š” ์™„๋ฒฝํ•ด์„œ ์ฐฉ๊ฐํ•˜์ง€ ์•Š์Œ)

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ์•„์ฃผ ์ค‘์š”ํ•œ 0.1์ดˆ์˜ ๋™๋“ฑ์„ฑ ํŒ๋ณ„ ๋กœ์ง(Object.is)์ž…๋‹ˆ๋‹ค. A๋‚˜ C ๊ฐ™์€ ์›์‹œ(Primitive) ํƒ€์ž…๋“ค์€ 200 === 200 ์ฒ˜๋Ÿผ ๊ฐ’ ์ž์ฒด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๊ทธ ์ž์ฒด์ด๋ฏ€๋กœ ๋ฆฌ์•กํŠธ๊ฐ€ ๋ Œ๋”๋ง ์Šค๋ƒ…์ƒท ๊ฐ„์— ์ „ํ˜€ ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ B์™€ ๊ฐ™์ด ๋ฆฌํ„ฐ๋Ÿด๋กœ ์„ ์–ธ๋˜๋Š” ์ฐธ์กฐ(Reference) ํƒ€์ž…๋“ค์€ ๋ Œ๋”๋งํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํž™(Heap) ๊ณต๊ฐ„์— ๋ฐฉ์ด ํŒŒ์ง€๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ(๊ป๋ฐ๊ธฐ)๊ฐ€ ๊ต์ฒด๋˜๋ฏ€๋กœ, ๋‚ด์šฉ๋ฌผ์ด ๋น„์–ด ๋˜‘๊ฐ™๋”๋ผ๋„ ๋ฆฌ์•กํŠธ๋Š” ๋ฌด์กฐ๊ฑด false(๋‹ค๋ฅด๋‹ค) ํŒ์ •์„ ๋‚ด๋ ค๋ฒ„๋ ค Effect ๋ถ€์ž‘์šฉ์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

Q3. ์˜์ฒ ์ด๊ฐ€ "์–ด์ฐจํ”ผ ์ œ๊ฐ€ ์ง  ๊ฑฐ๋‹ˆ๊นŒ ์ œ๊ฐ€ ์ œ์ผ ์ž˜ ์•Œ์•„์š”. ์ด ๊ฒฝ๊ณ  ์ฃผ์„(eslint-disable) ๋ฌด์‹œํ•ด๋„ ์•ฑ ์•ˆ ํ„ฐ์ง€๊ณ  ์ž˜ ๋Œ์•„๊ฐ€๋Š”๋ฐ์š”?" ๋ผ๊ณ  ์šฐ๊ธธ ๋•Œ, ์˜ํ˜ธ๊ฐ€ ์กฐ์šฉํžˆ [categoryId] ์˜์กด์„ฑ์„ ์†์œผ๋กœ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ ์งš์–ด์ค„ ์ˆ˜ ์žˆ๋Š” '๋ฏธ๋ž˜์— ๋‹ฅ์น  ์ง„์งœ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ ์‹œ๋‚˜๋ฆฌ์˜ค'๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

โœ… ์ •๋‹ต ๋ฐ ์ฃผ๊ด€์‹ ํ•ด์„ค:

"์˜์ฒ  ๋‹˜, ๋‹น์žฅ์€ ์ปดํฌ๋„ŒํŠธ ์ตœ์ดˆ ๋งˆ์šดํŠธ ์‹œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜์–ด์„œ ์ž˜ ๋œ๋‹ค๊ณ  ์ฐฉ๊ฐํ•˜์‹œ๊ฒ ์ฃ . ํ•˜์ง€๋งŒ ๋ฏธ๋ž˜์— ๊ธฐํš์ด ๋ณ€๊ฒฝ๋˜์–ด ํ™”๋ฉด ์ขŒ์ธก์— ์‚ฌ์ด๋“œ๋ฐ” ์นดํ…Œ๊ณ ๋ฆฌ ๋ฒ„ํŠผ๋“ค(categoryId=1, 2, 3... ๋ณ€๊ฒฝ)์ด ์ƒ๊ธด๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”?
์˜์กด์„ฑ ๋ฐฐ์—ด์„ []๋กœ ๊ฐ•์ œ๋กœ ๋„๋ ค๋‚ด๊ณ  ์ฃผ์„์„ ์นœ ์ˆœ๊ฐ„, ์šฐ๋ฆฌ๋Š” ๋ฆฌ์•กํŠธ์—๊ฒŒ '์ด Effect ๋ฉ์–ด๋ฆฌ๋Š” ์™ธ๋ถ€ ์„ธ๊ณ„์˜ ์–ด๋– ํ•œ ํ™˜๊ฒฝ(ํ”„๋กœํผํ‹ฐ ๋“ฑ)์ด ๋ณ€ํ•ด๋„ ์ ˆ๋Œ€ ๋‹ค์‹œ ๋™๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค!' ๋ผ๊ณ  ๊ฑฐ๋Œ€ํ•œ ์„œ์•ฝ์„ ๋งบ์€ ์…ˆ์ž…๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฑ๋‚  ๋‹ค๋ฅธ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ categoryId ๊ฐ’(State)์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„, ์ด๋ฏธ ๊ท€๋ฅผ ๋‹ซ์•„๋ฒ„๋ฆฐ Effect๋Š” ๊ฒฐ์ฝ” ์ƒˆ API ํ˜ธ์ถœ์„ ๋•Œ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๋™๊ธฐํ™” ๊ฑฐ๋ถ€). ๊ป๋ฐ๊ธฐ UI๋Š” ์ „์ž๊ธฐ๊ธฐ ์นดํ…Œ๊ณ ๋ฆฌ(3)๋ฅผ ๋ˆ„๋ฅด๊ณ  ์žˆ๋Š”๋ฐ, ์ƒํ’ˆ ๋ชฉ๋ก ์€ ์˜์›ํžˆ ์˜ท ์นดํ…Œ๊ณ ๋ฆฌ(1)์— ์–ผ์–ด๋ถ™์–ด ์žˆ๋Š” '์น˜๋ช…์ ์ธ ์นจ๋ฌต(Stale Data ๋ Œ๋”๋ง) ๋ฒ„๊ทธ'๊ฐ€ ํƒ„์ƒํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค."