ํ”„๋กœ๊ทธ๋ž˜๋ฐ/React JS

[React] ์„ฑ๋Šฅ ์ตœ์ ํ™” React.memo์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ

์šฉ๋‡ฝ 2023. 7. 20. 19:10
๋ฐ˜์‘ํ˜•

[React] ์„ฑ๋Šฅ ์ตœ์ ํ™” React.memo์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ

๐Ÿ“– ๋“ค์–ด๊ฐ€๋ฉฐ

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

React.memo๋ž€ โ“

memo๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฆฌ๋ Œ๋”๋ง์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, ๋ฉ”๋ชจํ™”๋œ ๋ฒ„์ „์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”๋ง์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปค๋‹ค๋ž€ ํ๋ชจ์˜ ์ปดํฌ๋„ŒํŠธ๋‚˜ ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ง‰์•„ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋™์ž‘ ์›๋ฆฌ ๐Ÿ’ก

React.memo๋Š” ์ธ์ž๋กœ ๋“ค์–ด๊ฐ„ ์ปดํฌ๋„ŒํŠธ์— ์–ด๋–ค props๊ฐ€ ์ž…๋ ฅ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์ž…๋ ฅ๋˜๋Š” ๋ชจ๋“  props์˜ ์‹ ๊ทœ ๊ฐ’์„ ํ™•์ธํ•œ ๋’ค ์ด๋ฅผ ๊ธฐ์กด์˜ props์˜ ๊ฐ’๊ณผ ๋น„๊ตํ•˜๋„๋ก ๋ฆฌ์•กํŠธ์—๊ฒŒ ์ „๋‹ฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  props์˜ ๊ฐ’์ด ๋ฐ”๋€ ๊ฒฝ์šฐ์—๋งŒ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ props ๊ฐ’์ด ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋‹ค๋ฉด ๋ฆฌ๋ Œ๋”๋ง์€ ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์˜ˆ์‹œ

import React, { memo } from 'react';

const ExpensiveComponent = ({ show }) => {
  console.log('ExpensiveComponent๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.');
  return (
    <div>
      {show ? '์•ˆ๋…•' : ''}
    </div>
  );
};

export default memo(ExpensiveComponent);

์ด๋ ‡๊ฒŒ ๊ฐ์‹ธ์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”๋ง์„ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‹ธ์ค€ ์ปดํฌ๋„ŒํŠธ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ์˜๋ฌธ์ ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

"๊ทธ๋Ÿผ ์™œ ์ด๊ฑธ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฑธ๊นŒ?"

์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต์€ ์ตœ์ ํ™”์—๋Š” ๋น„์šฉ์ด ๋”ฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

์ด memo ๋ฉ”์„œ๋“œ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋กœ ์ด๋™ํ•˜์—ฌ ๊ธฐ์กด props ๊ฐ’๊ณผ ์ƒˆ๋กœ์šด props์˜ ๊ฐ’์„ ๋น„๊ตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ๋ฆฌ์•กํŠธ๋Š” ๋‘ ๊ฐ€์ง€ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ๊ธฐ์กด์˜ props ๊ฐ’์„ ์ €์žฅํ•  ๊ณต๊ฐ„์ด ํ•„์š”ํ•˜๊ณ  ๋น„๊ตํ•˜๋Š” ์ž‘์—…๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ์ด ํšจ์œจ์€ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ‰๊ฐ€ํ•˜๋Š” ๋ฐ์— ํ•„์š”ํ•œ ์„ฑ๋Šฅ ๋น„์šฉ๊ณผ props๋ฅผ ๋น„๊ตํ•˜๋Š” ์„ฑ๋Šฅ ๋น„์šฉ์„ ์„œ๋กœ ๋งž๋ฐ”๊พธ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ props์˜ ๊ฐœ์ˆ˜์™€ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณต์žก๋„, ๊ทธ๋ฆฌ๊ณ  ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์ˆ˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋ฏ€๋กœ ์–ด๋Š ์ชฝ์˜ ๋น„์šฉ์ด ๋” ๋†’๋‹ค๊ณ  ํ™•์ • ์ง“๊ณ  ๋งํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ๋ถ€ํ„ฐ props๋กœ ํ•จ์ˆ˜๋ฅผ ๋ฐ›๋Š” ์ƒํ™ฉ โ“

// Button.js

const Button = ({ onClick }) => {
  console.log('Button RUNNING');

  return <button onClick={onClick}>ํด๋ฆญ</button>;
};

export default React.memo(Button);

// App.js
import React, { useState } from 'react';

function App() {
  const [toggleState, setToggleState] = useState(false);
  console.log('APP RUNNING');

  const toggleStateHandler = () => setToggleState((prev) => !prev);

  return (
    <div className="app">
      <Button onClick={toggleStateHandler}>Toggle State</Button>
    </div>
  );
}

export default App;

์œ„์—์„œ ๋งํ•œ ๋‚ด์šฉ๋Œ€๋กœ๋ผ๋ฉด Button ์ปดํฌ๋„ŒํŠธ์˜ props๋Š” ๋ฐ”๋€Œ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ดˆ ๋ Œ๋”๋ง ํ›„ ๋ฆฌ๋ Œ๋”๋ง์ด ๋  ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ถ€๋ชจ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ๊ฐ™์ด ๋ Œ๋”๋ง์ด ๋˜๋Š” ์ƒํ™ฉ์„ ๊ฒช์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์™œ ๊ทธ๋Ÿด๊นŒ์š”?

 

React.memo๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ ํ•˜๋Š” ์ผ์€ props์˜ ๊ฐ’์„ ํ™•์ธํ•˜๊ณ  ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

props.show === props.previous.show

์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋น„๊ต๋ฅผ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. (์‹ค์ œ๋กœ ์ด๋ ‡๊ฒŒ ๋น„๊ต๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.)

์—ฌ๊ธฐ์„œ ์›์‹œ๊ฐ’๊ณผ ์ฐธ์กฐ๊ฐ’์— ์ดํ•ด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

cosnt str1 = 'hi';
const str2 = 'hi';
console.log(str1 === str2) // true

const arr1 = [1,2,3];
const arr2 = [1,2,3];
console.log(arr1 === arr2) // false

์œ„์™€ ๊ฐ™์ด ๋ฐฐ์—ด์ด๋‚˜ ๊ฐ์ฒด, ํ•จ์ˆ˜๋Š” ์ฐธ์กฐ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ž ๋‹ค๋ฅธ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ๋ณด๊ธฐ์— ๋‚ด์šฉ์€ ๊ฐ™๋”๋ผ๋„ ๋น„๊ต ์‹œ์—๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

 

๋‹ค์‹œ ๋Œ์•„์™€์„œ ํ•จ์ˆ˜๋Š” ํ•˜๋‚˜์˜ ๊ฐ์ฒด์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค.

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ(App)๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ์ด ๋˜๊ณ  ์ด ํ•จ์ˆ˜ ๊ฐ์ฒด๊ฐ€ onClick props์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋ฒ„ํŠผ์€ props.onClick๊ณผ props.previous.onClick๋ฅผ ๋น„๊ตํ•˜๋Š” ์…ˆ์ธ๋ฐ ์ฐธ์กฐ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ ๋™์ผํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

์ฆ‰, React.memo๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ด๋Ÿฌํ•œ ๋™์ž‘ ๋•Œ๋ฌธ์— ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๊ณ  ์ธ์‹ํ•˜๊ณ  ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ด๋Ÿฐ ์ƒํ™ฉ์€ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ• ๊นŒ์š”?

 

๋ฐ”๋กœ useCallback์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ ๊ธ€์€ useCallback์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•