๐Ÿ’ป๐Ÿ’€/๊ฐœ๋ฐœ

[React] ๋˜‘๋˜‘ํ•˜๊ฒŒ useCallback ์‚ฌ์šฉํ•˜๊ธฐ

db2 2022. 10. 2. 19:07

์ด ๊ธ€์€ When to useCallback & useMemo hooks ? ์˜ ๋ฒˆ์—ญ๊ธ€์ž…๋‹ˆ๋‹ค. (์ด๋ฏธ์ง€ ํฌํ•จ)

๊ฐœ๋ฐœ์ž์˜ ์ˆ™๋ช…, ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”

์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ˆ„๊ตฌ๋“  ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นŒ๋“œ ์ดํ›„ ๋งˆ์ฃผํ•˜๊ฒŒ ๋˜๋Š” ํ”ํ•œ ์ด์Šˆ์ด๋‹ค. ํ•˜์ง€๋งŒ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ๋™์ž‘ํ–ˆ๋˜ ์‹œ์Šคํ…œ์ด ๋ฐฐํฌ ํ›„ ์šด์˜ ์‹œ์— ๋” ๋Š๋ ค์ง€๋Š” ๋Š๋‚Œ์ด ๋“œ๋Š” ๊ฑด ๋‹จ์ˆœํ•œ ์ฐฉ๊ฐ์ผ๊นŒ?

๊ฐœ๋ฐœ ๋ชจ๋“œ์ผ ๋•Œ ๋” ๋น ๋ฅด๊ฒŒ ๋Š๊ปด์ง€๋Š” ์ด์œ 

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

ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด ์ฐจ๋ผ๋ฆฌ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค

์„ฑ๋Šฅ ์ตœ์ ํ™”์—๋Š” ํ•ญ์ƒ ๋น„์šฉ์ด ๋“ค์ง€๋งŒ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ํ•ญ์ƒ ์ด์ ์ด ์žˆ๋Š” ๊ฒƒ๋„ ์•„๋‹ˆ๋‹ค.

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

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

useCallback์„ ์ž˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

useCallback์€ ํ•จ์ˆ˜๋ฅผ ๊ธฐ์–ตํ–ˆ๋‹ค๊ฐ€ ์žฌ์‚ฌ์šฉํ•˜๋„๋ก(๋ฉ”๋ชจ์ด์ œ์ด์…˜) ๋„์™€์ฃผ๋Š” ํ›…์ด๋‹ค.

๋ฆฌ์•กํŠธ์—์„œ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋˜๋ฉด ๊ทธ ์•ˆ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋„ ๋ฆฌ๋ Œ๋”๊ฐ€ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ๋ฆฌ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ๋งค๋ฒˆ ๋‚ด๋ถ€ ๋™์ž‘๋“ค์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด ๋•Œ useCallback์„ ์‚ฌ์šฉํ•ด์„œ ํ•จ์ˆ˜์˜ ๋ถˆํ•„์š”ํ•œ ์žฌ์‹คํ–‰ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๋จผ์ € useCallback์„ ์ ์šฉํ•˜์ง€ ์•Š์€ ์˜ˆ์ œ์ด๋‹ค.

import React, { useState } from "react";

const set = new Set();

export default function App() {
  const [count, setCount] = useState(0);

  const increase = () => setCount(count + 1);
  const printLog = () => console.log("re-execute"); 

  set.add(printLog);
  console.log(set); 

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increase}>Add</button>
      <button onClick={printLog}>Print</button>
    </div>
  );
}

Add ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด count๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋” ๋˜๋ฉด์„œ ๋‚ด๋ถ€์˜ increase ํ•จ์ˆ˜์™€ printLog ํ•จ์ˆ˜์˜ ์„ ์–ธ๊ณผ ํ˜ธ์ถœ๋„ ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰๋œ๋‹ค. ๋”ฐ๋ผ์„œ Add ๋ฒ„ํŠผ์„ 3๋ฒˆ ํด๋ฆญํ•ด์„œ count๊ฐ€ 0์—์„œ 2๋กœ ์ฆ๊ฐ€ํ–ˆ์„ ๋•Œ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ๋ฒ„ํŠผ์„ ํ•œ ๋ฒˆ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค printLog ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์„ ์–ธ๋˜์–ด์„œ Set์— ๋”ํ•ด์ง€๊ณ  ์žˆ๋‹ค. (StrictMode๋กœ ์ธํ•ด ๋‘ ๋ฒˆ์”ฉ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ๊ฑด ๋ฌด์‹œ ๋ฐ”๋žŒ)

increase ํ•จ์ˆ˜๋Š” ๋ณ€๊ฒฝ๋˜๋Š” state์ธ count๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ’์ด ์ฆ๊ฐ€ํ•˜๋ฉด ํ•จ๊ป˜ ์žฌ์ƒ์„ฑ ๋˜์–ด์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋งž๋‹ค. ํ•˜์ง€๋งŒ printLog ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ ์ฒซ ์‹คํ–‰ ์ดํ›„ ๋‚ด๋ถ€์ ์œผ๋กœ ๋™์ž‘์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋งค๋ฒˆ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋ฐ”๋กœ ์ด๋Ÿด ๋•Œ useCallback์„ ์‚ฌ์šฉํ•ด ์ตœ์ ํ™”๋ฅผ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค!

import React, { useState, useCallback } from "react";

const set = new Set();

export default function App() {
  const [count, setCount] = useState(0);

  const increase = () => setCount(count + 1);
  const printLog = useCallback(() => console.log('re-execute'), []); // 1๏ธโƒฃ

  set.add(printLog); // 2๏ธโƒฃ
  console.log(set); 

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increase}>Add</button>
      <button onClick={printLog}>Print</button>
    </div>
  );
}

๊ทธ๋Ÿผ ์ด์ œ count๊ฐ€ ์ฆ๊ฐ€ํ•ด๋„ ๏ธ1๏ธโƒฃ์—์„œ๋Š” printLog ํ•จ์ˆ˜๊ฐ€ ์žฌ์„ ์–ธ ๋˜์ง€ ์•Š๊ณ  ์ฒ˜์Œ ์ƒ์„ฑ๋๋˜ ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ค‘๋ณต๋œ ๊ฐ’์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” Set์˜ ์ž๋ฃŒ๊ตฌ์กฐ ํŠน์„ฑ์ƒ, ๋ฆฌ๋ Œ๋” ๋  ๋•Œ๋งˆ๋‹ค 2๏ธโƒฃ์—์„œ printLog ํ•จ์ˆ˜๋ฅผ set์— ๋”ํ•ด๋„ ํ•จ์ˆ˜์˜ ์ฐธ์กฐ๊ฐ’์ด ์ฒ˜์Œ๊ณผ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๋ณต๋˜์–ด ๋”ํ•ด์ง€์ง€ ์•Š๋Š”๋‹ค.

์ฝ˜์†”์„ ํ™•์ธํ•ด๋ณด๋ฉด ๋ช…ํ™•ํ•ด์ง„๋‹ค. App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ์ดˆ ๋งˆ์šดํŠธ ๋  ๋•Œ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜์–ด ๋”ํ•ด์ง„ ๋’ค, ๊ทธ ์ดํ›„๋ถ€ํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

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