👽
memoization의 개념은 왜 등장했나? 최적화 관점에서 살펴보기 렛츠고

리랜더링이 언제 발생한다고 생각하나?
state가 바뀌었을 때props가 변경되었을 때부모 컴포넌트가 리랜더링 된 경우 자식 컴포넌트 모두
재사용`하는 것
불필요한 랜더링을 줄여주고
최적화(Optimization)하는 방법이 바로바로~
memo(React.memo) : 컴포넌트를 캐싱
useCallback : 함수를 캐싱
useMemo : 값을 캐싱
캐싱이란?
저장해 두고, 필요할 때마다 다시 계산하거나 생성하지 않고 저장된 값을 재사용하는 것
React.memo를 통해 컴포넌트를memoization하는 방법을 알아보자.
리랜더링 조건 중 하나, 부모가 리랜더링되면 자식도 리랜더링 된다. 이 때 자식은 '난 한 게 없는데 왜 ㅠㅠ' 라는 생각이 들 것이다. 이 때 자식 편을 들어주는 것이
React.memo
예제 코드와 함께 레츠고

App.jsx
import React, { useState } from "react";
import Box1 from "./components/Box1";
import Box2 from "./components/Box2";
import Box3 from "./components/Box3";
const boxesStyle = {
display: "flex",
marginTop: "10px",
};
function App() {
console.log("App 컴포넌트가 렌더링되었습니다!");
const [count, setCount] = useState(0);
// 1을 증가시키는 함수
const onPlusButtonClickHandler = () => {
setCount(count + 1);
};
// 1을 감소시키는 함수
const onMinusButtonClickHandler = () => {
setCount(count - 1);
};
return (
<>
<h3>카운트 예제입니다!</h3>
<p>현재 카운트 : {count}</p>
<button onClick={onPlusButtonClickHandler}>+</button>
<button onClick={onMinusButtonClickHandler}>-</button>
<div style={boxesStyle}>
<Box1 />
<Box2 />
<Box3 />
</div>
</>
);
}
export default App;
Box1.jsx
import React from "react";
const boxStyle = {
width: "100px",
height: "100px",
backgroundColor: "#91c49f",
color: "white",
// 가운데 정렬 3종세트
display: "flex",
justifyContent: "center",
alignItems: "center",
};
function Box1() {
console.log("Box1이 렌더링되었습니다.");
return <div style={boxStyle}>Box1</div>;
}
export default Box1;
Box2.jsx
import React from "react";
const boxStyle = {
width: "100px",
height: "100px",
backgroundColor: "#4e93ed",
color: "white",
// 가운데 정렬 3종세트
display: "flex",
justifyContent: "center",
alignItems: "center",
};
function Box2() {
console.log("Box2가 렌더링되었습니다.");
return <div style={boxStyle}>Box2</div>;
}
export default Box2;
Box3.jsx
import React from "react";
const boxStyle = {
width: "100px",
height: "100px",
backgroundColor: "#c491be",
color: "white",
// 가운데 정렬 3종세트
display: "flex",
justifyContent: "center",
alignItems: "center",
};
function Box3() {
console.log("Box3가 렌더링되었습니다.");
return <div style={boxStyle}>Box3</div>;
}
export default Box3;



실제로 변한 것은 부모 컴포넌트인 App.jsx 뿐인데 나머지 자식들까지 리랜더링 되고 난리가 났다. 💥
memo를 통해 해결 할 수 있다!! 레츠고
React.memo를 이용해서 컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓴다.
📌 이것이 바로컴포넌트 memoization
한 번 해보자 레츠고
Box1.jsx, Box2.jsx , Box.jsx 전부 동일
export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);


📌 최초 랜더링 이외에는 App.jsx 컴포넌트의 state가 변경되더라도 자식 컴포넌트들은 랜더링이 되지 않는다! (아이들이 울지도 않고 잘 있네요. 👶)
useCallbackhook을 통해 함수를 memoization 하는 방법을 알아보자 레츠고
React.memo는 컴포넌트를 메모이제이션(기억) 했다면,
useCallback은 인자로 들어오는 함수 자체를 메모이제이션(기억) 한다.
Box1이 count를 초기화 해주는 코드라고 가정한다.
App.jsx
...
// count를 초기화해주는 함수
const initCount = () => {
setCount(0);
};
return (
<>
<h3>카운트 예제입니다!</h3>
<p>현재 카운트 : {count}</p>
<button onClick={onPlusButtonClickHandler}>+</button>
<button onClick={onMinusButtonClickHandler}>-</button>
<div style={boxesStyle}>
<Box1 initCount={initCount} />
<Box2 />
<Box3 />
</div>
</>
);
}
...
Box1.jsx
...
function Box1({ initCount }) {
console.log("Box1이 렌더링되었습니다.");
const onInitButtonClickHandler = () => {
initCount();
};
return (
<div style={boxStyle}>
<button onClick={onInitButtonClickHandler}>초기화</button>
</div>
);
}
...
+ 버튼이나, - 버튼을 누를 때 그리고 초기화 버튼을 누를 때 모두
App 컴포넌트와 Box1 컴포넌트가 리렌더링 되는 것이 확인된다.
?? :
React.memo를 통해서 Box1.jsx는 메모이제이션을 했는데도 리랜더링이 되는데 이게 말이 되나요?
🧐 네, 아가씨. 그것은 우리가 함수형 컴포넌트를 사용하기 때문이고App.jsx가 리랜더링 되면서 코드가 다시 만들어지기 때문이죠.const onInitButtonClickHandler = () => { initCount(); };
📌 자바스크립트에서 함수도 객체의 한 종류다.
따라서 모양은 같더라도 다시 만들어지면 그 주소값이 달라지고,
이에 따라 하위 컴포넌트인Box1.jsx는 props가 변경됐다고 인식 한다.
💥 그래서 우린 useCallback이 필요하다. 그를 불러보자 레츠고.
const onInitButtonClickHandler = () => {
initCount();
};
이 문제의 함수를 메모리 공간에 가둬 저장해놓고 특정 조건이 아닌 경우엔 변경되지 않도록 한다.
App.jsx
// 변경 전
const initCount = () => {
setCount(0);
};
// 변경 후
const initCount = useCallback(() => {
setCount(0);
}, []);
이렇게 하니 다시 자식이 조용해졌다. 🤗
감사합니다 ! 이해가 쏘옥 되었듭니당