Post

๐Ÿฅœ [React] React.memo(), useCallback(), useMemo() ๋ฅผ ํ™œ์šฉํ•œ ์ตœ์ ํ™”๋ฅผ ํ•ด๋ณด์ž

๊ฐœ์š”

์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์žฅ๋ฒฝ์ด ๋‚ฎ์•„์ง€๊ณ  ์žˆ๋Š” ์š”์ฆ˜,

์‚ฌ๋žŒ๋“ค์ด ์ž๊ธฐ ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์ตœ์ ํ™” ํ•˜๋Š” ์ผ์—๋Š” ์†Œํ™€ํžˆํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ฝ”๋“œ๋ฅผ ํ•œ๋ฒˆ ๊ฐœ์„ ํ•ด๋ณด์ž

๊ท€์ฐฎ์ง€๋งŒ ํ•ด๋ณด์ž



React Profiler ์„ค์น˜

์ผ๋‹จ ๋จผ์ € ๊ตฌ๊ธ€์—์„œ ์„ค์น˜๊ฐ€ ๊ฐ€๋Šฅํ•œ React Profiler ๋ฅผ ํ™œ์šฉํ•  ์˜ˆ์ •์ด๋‹ค.

์„ค์น˜๋ฅผ ํ•˜๋ ค๋ฉด ๋งํฌ ์ฐธ๊ณ 

image-01 (React Profiler)



React.memo()

๋จผ์ € React.memo() ๋ฅผ ํ™œ์šฉํ•ด๋ณด๊ธฐ ์ „์—

React.memo() ๋ž€ ๋ญ˜๊นŒ?

๊ทธ๊ฑธ ์•Œ๋ ค๋ฉด ์ผ๋‹จ React ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ๋˜์งš์–ด๋ณด์•„์•ผ ํ•˜๋Š”๋ฐ

React ๋Š” ๋จผ์ € ์ปดํฌ๋„ŒํŠธ๋ฅผ rendering ํ•œ ํ›„ ์ด์ „์— rendering ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋น„๊ตํ•ด

DOM ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค (Virtual DOM). ๋งŒ์•ฝ ์ด์™€ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅด๋ฉด DOM ์„ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

์ด๋•Œ React.memo() ๋กœ ์Œ“์—ฌ์žˆ์œผ๋ฉด React ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฒ˜์Œ rendering ํ•  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ์— Memoizing ํ•œ๋‹ค.

๊ทธ ํ›„ ๋‹ค์Œ rendering ๋ฐœ์ƒ ์‹œ ์ปดํฌ๋„ŒํŠธ์˜ props ๊ฐ€ ์ผ์น˜ํ•œ๋‹ค๋ฉด React ๋Š” Memoizing ํ•œ ๊ฐ’์„

์žฌ์‚ฌ์šฉํ•œ๋‹ค.

React ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๊ธฐ์–ตํ•ด ๋’€๋‹ค๊ฐ€ ๋‹ค์‹œ ์žฌํ™œ์šฉํ•˜๋Š” ๊ฒƒ

์ž ์ด์ œ ํ•จ์ˆ˜์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์œผ๋‹ˆ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž

์˜ˆ์‹œ์ฝ”๋“œ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default function A({ message, posts }) {
  return (
    <div>
      <h1>A Component</h1>
      <p>{message}</p>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <p>{post.title}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

๋ชจ๋“  ๊ฐ’์„ ํ•œ๋ฒˆ์— ๋žœ๋”๋ง ํ•˜๋Š” A-component ๊ฐ€ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from "react";

export default function B({ message, posts }) {
  return (
    <div>
      <h1>B Component</h1>
      <Message message={message} />
      <List posts={posts} />
    </div>
  );
}

const Message = ({ message }) => {
  return <p>{message}</p>;
};

const ListItem = ({ post }) => {
  return (
    <li key={post.id}>
      <p>{post.title}</p>
    </li>
  );
};

const List = ({ posts }) => {
  return (
    <ul>
      {posts.map((post) => (
        <ListItem key={post.id} post={post} />
      ))}
    </ul>
  );
};

์ด๋ฒˆ์—” ๊ฐ๊ฐ ๋™์ž‘์„ ๋ถ„๋ฆฌํ•œ B-components ๊ฐ€ ์žˆ๋‹ค.

React Profiler ๋ฅผ ํ†ตํ•ด ๋žœ๋”๋ง ์†๋„๋ฅผ ๋น„๊ตํ•ด ๋ณด์ž

image-02 (์™œ ์ด๋ ‡๊ฒŒ ์ฐจ์ด๊ฐ€ ๋‚ ๊นŒ?)

๋ฌธ์ œ ํŒŒ์•…

๋„๋Œ€์ฒด ์™œ ์ด๋Ÿฐ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๊นŒ?

์„ค๋ น ์ฐจ์ด๊ฐ€ ๋‚˜๋”๋ผ๋„ B๊ฐ€ ๋” ๋นจ๋ผ์•ผ ํ•˜๋Š” ๊ฒƒ ์•„๋‹๊นŒ?

์‹ถ์ง€๋งŒ B ๊ฐ€ ๋” ๋Š๋ฆฐ ์ด์œ ๋Š” props ๋กœ ์ด๋ฏธ ๋ฐ›์•„์˜จ ๊ฐ’์„ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—

๋ฟŒ๋ฆฌ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋žœ๋”๋ง ์‹œ ์†๋„๊ฐ€ ๋” ์ง€์—ฐ๋˜๋Š” ์ƒํ™ฉ์ด๋‹ค.

๊ทธ๋ ‡๊ธฐ์— ํ•ด๊ฒฐ๋ฐฉ์•ˆ์€ ๊ฐ„๋‹จํ•˜๋‹ค.

์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ props ๋กœ ์ฃผ๊ณ  ์žˆ๋Š” ๊ฐ’์„ react ์—์„œ ๊ธฐ์–ต ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€

๋™์ผํ•œ ๊ฐ’์ด๋ฉด ๋žœ๋”๋ง์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋œ๋‹ค.


ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ React.memo() ๋ฅผ ํ™œ์šฉํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// React.memo() ๋ฅผ ์ ์šฉํ•œ ๋ชจ์Šต
import React from "react";

export default function B({ message, posts }) {
  return (
    <div>
      <h1>B Component</h1>
      <Message message={message} />
      <List posts={posts} />
    </div>
  );
}

const Message = React.memo(({ message }) => {
  return <p>{message}</p>;
});

const ListItem = React.memo(({ post }) => {
  return (
    <li key={post.id}>
      <p>{post.title}</p>
    </li>
  );
});

const List = React.memo(({ posts }) => {
  return (
    <ul>
      {posts.map((post) => (
        <ListItem key={post.id} post={post} />
      ))}
    </ul>
  );
});

image-03 (๋žœ๋”๋ง ์†๋„๊ฐ€ ํ™•์—ฐํžˆ ์ค„์–ด๋“  ๊ฒƒ์ด ๋ณด์ธ๋‹ค)

๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ React.memo() ๋ฅผ ๋‚จ์šฉํ•ด์„œ๋„ ์•ˆ ๋œ๋‹ค !

์ง€์–‘ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ

๋žœ๋”๋ง ๋  ๋•Œ props ๊ฐ€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ผ ๊ฒฝ์šฐ์—๋Š” React.memo() ๋Š” ๋ณ„

๋„์›€์ด ๋˜์ง€ ๋ชปํ•œ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๋ชจ๋“  ํ•จ์ˆ˜์— React.memo() ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒฝ๊ณ ๊ฐ€ ์žˆ์œผ๋‹ˆ ์ ์žฌ ์ ์†Œ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

๋”ฐ๋ผ์„œ

React.memo() ๋Š” ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ ๋„๊ตฌ๋กœ ์ƒ๊ฐํ•ด์•ผํ•œ๋‹ค.



useCallback()

์›๋ž˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋žœ๋”๋ง ๋  ๋•Œ๋Š” ๊ทธ ์•ˆ์— ์žˆ๋Š” ํ•จ์ˆ˜๋„ ๋‹ค์‹œ ๋งŒ๋“ค๊ฒŒ ๋˜๋Š”๋ฐ,

๊ณ„์† ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋žœ๋”๋ง ๋  ๋•Œ ๋งˆ๋‹ค ๊ณ„์† ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ข‹์€ ํ˜„์ƒ์€ ์•„๋‹ˆ๋‹ค.

์„ค๋ น ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ props ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋„

๊ณ„์†ํ•ด์„œ re-rendering ๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•œ๋‹ค.

๊ทธ๋Ÿด ๋•Œ๋Š” useCallback() ์œผ๋กœ ๊ฐœ์„  ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { useCallback } from "react";

export default function B({ message, posts }) {
  
  /**
   * ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ List ์˜ props ๋กœ ๋“ค์–ด๊ฐ€๋Š” ํ•จ์ˆ˜
  */
  const tsetFunction = useCallback(() => {
    console.log("TEST");
  }, []);

  // useEffect() ์™€ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ด ๋น„์Šทํ•˜๋‹ค.

  return (
    <div>
      <h1>B Component</h1>
      <Message message={message} />
      <List posts={posts} tsetFunction={tsetFunction} />
    </div>
  );
}


const List = React.memo(({ posts, tsetFunction }) => {
  return (
    <ul>
      {posts.map((post) => (
        <ListItem key={post.id} post={post} />
      ))}
    </ul>
  );
});

์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•œ๋ฐ, useEffect() ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š” ๋ฐฐ์—ด์€ ์˜์กด์„ฑ์ด๋ฏ€๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ๋žœ๋”๋ง ํ•  ๋•Œ์˜ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.



useMemo()

useMemo() ๋„ ๋”ฐ์ง€๊ณ ๋ณด๋ฉด Memoization ์— ๊ด€๋ จ๋˜์–ด ์žˆ๋Š”๋ฐ,

๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด

์ด๋ฏธ ๋ถˆํ•„์š”ํ•œ ๋žœ๋”๋ง์ด ๊ณ„์† ์ง„ํ–‰ ๋  ๋•Œ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์‹์ด ์žˆ๋‹ค๋ฉด ๊ทธ ๊ฐ’์„ memoization ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์˜ˆ์‹œ์ฝ”๋“œ

1
2
3
4
5
function Component({a, b}) {
    const result = calculate(a, b);

    return <div>{result}</div>;
}

๋ผ๋Š” ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜๊ฐ€ ์žˆ์„ ๋•Œ calculate ๋ผ๋Š” ์—ฐ์‚ฐ ๊ฐ’์ด

๋ณ€ํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ ๊ณ„์†ํ•ด์„œ rendering ์ด ๋ฐœ์ƒํ•ด ๊ฐ™์€ ์—ฐ์‚ฐ์„ ๋ฐ˜๋ณตํ•œ๋‹ค๋ฉด ์ข‹์ง€ ๋ชปํ•˜๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋Ÿด ๋•Œ useMemo() ๋ฅผ ํ™œ์šฉํ•ด ์“ธ๋ฐ ์—†๋Š” ์—ฐ์‚ฐ์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

1
2
3
4
5
function Component({a, b}) {
    const result = useMemo(() => calculate(a, b), [a, b]);

    return <div>{result}</div>;
}

์•ž์„œ ์„ค๋ช…ํ•œ useCallback() ๊ณผ ์ผ๋งฅ์ƒํ†ตํ•œ๋‹ค.

useEffect() ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•˜๋‹ค. ์šฐ์ธก์˜ ๋ฐฐ์—ด์€ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.



๋งˆ์น˜๋ฉฐ

React ์˜ ๋™์ž‘๋ฐฉ์‹์— ์˜ํ•ด ๋ถˆํ•„์š”ํ•œ ๋žœ๋”๋ง์ด ๋งŽ์•„์ง€์ž ์ด์— ๋”ฐ๋ฅธ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•ด

์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด Memoization ์„ ํ™œ์šฉํ•˜๋Š”๋ฐ

useMemo()useCallback()
์—ฐ์‚ฐ์„ ๊ธฐ๋กํ•  ๋•Œprops ๋‚˜ ๋ถˆํ•„์š”ํ•œ ํ•จ์ˆ˜๋ฅผ ์ค„์ผ ๋•Œ

๋กœ ํ™œ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•  ๊ฒƒ ๊ฐ™๋‹ค.


๋‹ค์Œ์— ์žˆ์„ ํ”„๋กœ์ ํŠธ์—์„œ

ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ ๋ฐฐ์šด Memoization ์„ ํ™œ์šฉํ•ด ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ํ•ด๋ณผ ์˜ˆ์ •์ด๋‹ค.

์–ผ๋งˆ๋‚˜ ๊ฐœ์„ ์ด ๋˜๋Š”์ง€ ์•„์ง์€ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ์—์„œ๋งŒ ํ…Œ์ŠคํŠธ ํ•ด๋ดค๋Š”๋ฐ

์ถ”ํ›„ ์žˆ์„ ํฐ ํ”„๋กœ์ ํŠธ์—์„œ ํ™œ์šฉ์„ ํ•œ๋‹ค๋ฉด ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์‹œ๊ฐ„ ๋‹จ์ถ•์ด ๋ ์ง€ ํฅ๋ฏธ๋กญ๋‹ค.

์•„์ง๊นŒ์ง€ ์ฝ”๋“œ ๊ฐœ์„ ๊ฒฝํ—˜์ด ๋ชจ์ž๋ฅธ ์ง€๊ธˆ ์ค‘์š”ํ•œ ํ•™์Šต ์ค‘ ํ•˜๋‚˜์˜€๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๋˜๋ฐ ์™œ ์•ˆํ–ˆ์ง€

This post is licensed under CC BY 4.0 by the author.