목차
1. React.memo 메서드
- 1-1. 커스텀 비교함수
2. useCallback
💡1. React.memo 메서드
저번에 useMemo 훅으로 불필요한 연산을 막았다면,
React.memo는 불필요한 리렌더링을 막아주는 내장함수이다.
: 함수형 컴포넌트를 메모이제이션(memoization) 하여, props가 변경되지 않으면 컴포넌트를 리렌더링하지 않는 기능을 수행한다.
기본형태는 아래와 같음.
import { memo } from 'react';
const MyComponent = (props) => {
// ...
};
export default memo(MyComponent);
props가 얕은 비교(shallow comparison)로 동일한 경우 해당 컴포넌트는 리렌더링되지 않는다.
최적화된 컴포넌트를 export 해서 부모로 보내주면 된다.
그럼 부모는 memo로 최적화된 컴포넌트를 리렌더링 하지 않게 되는 것이다.
ex)
todo List의 헤더 컴포넌트는 아래의 List 컴포넌트가 수정될 때마다 같이 리렌더링 되고 있었다.
Header 컴포넌트를 최적화 해보자.
const Header = () => {
return (
<div className="Header">
<h3>오늘은 🗓️</h3>
<h1>{new Date().toDateString()}</h1>
</div>
);
};
const memoizedHeader = memo(Header);
export default memoizedHeader;
Header 컴포넌트는 날짜만 출력하는 정적인 UI이다.
상위 컴포넌트인 App이 상태를 변경해도 Header는 변경될 이유가 없다.
-> React.memo()로 감싸면 Header의 리렌더링이 방지되어 성능을 향상시킬 수 있다.
💡1-1. 커스텀 비교 함수 사용
위에서 React.memo()는 기본적으로 얕은비교(==)를 수행한다고 했다.
하지만 더 정밀한 최적화가 필요한 경우에는 커스텀 비교 함수를 만들어 사용할 리렌더링을 막을 수 있다.
TodoList 에서 하나의 todoitem을 수정하면, 다른 todoitem들까지 함께 리렌더링 되는 상황이 발생하는데,
커스텀 비교 함수를 사용해서 리렌더링을 막아보자☺️
Q. memo()를 통해 감싸 export 했는데 왜?
App 컴포넌트가 렌더링 될 때마다 그 내부의 함수들이 모두 다시 생성되기 때문에, todoItem이 props로 받는 함수들이(onDelete(), onUpdate())이 새로운 함수로서 전달되기 때문이다.. 따라서 리렌더링이 발생한다.
* 새로운 함수와 같은 객체 값을 전달하는 경우 주소값이 달라짐. 즉, 얕은비교(==)로 비교 불가
해결방법
-> 커스텀 비교함수 (콜백함수로 구현) 사용하여 리렌더링 막아줘야함
// TodoItem.jsx
import "./TodoItem.css";
import {memo} from 'react';
const TodoItem = ({ id, isDone, content, date, onUpdate, onDelete }) => {
const onChangeCheckbox =()=>{
onUpdate(id);
};
const onClickDeleteButton=()=>{
onDelete(id);
};
return (
<div className="TodoItem">
<input onChange={onChangeCheckbox} readOnly checked={isDone} type="checkbox" />
<div className="content">{content}</div>
<div className="date">
{new Date(date).toLocaleDateString()}
</div>
<button onClick={onClickDeleteButton}>삭제</button>
</div>
);
};
// 고차 컴포넌트 (HOC)
export default memo(TodoItem,(prevProps, nextProps)=>{
if(prevProps.id !== nextProps.id) return false;
if(prevProps.isDone !== nextProps.isDone) return false;
if(prevProps.content !== nextProps.content) return false;
if(prevProps.date !== nextProps.date) return false;
return true;
});
export 에서 callback 함수를 사용해서 커스텀 비교 함수를 만들어준다.
prevProps, nextProps 로 참조 자체를 분리
- prevProps: 이전 렌더링에서 사용된 props
- nextProps: 새롭게 들어온 props
Q. prevProps, nextProps 변수안에 자동으로 값이 넣어지는 건가?
prevProps와 nextProps은 내가 만든 변수이름이지만,
React가 값(객체)을 자동으로 넣어주는 것이기 때문에 마치 내장 객체처럼 사용할 수 있는 것이다.
결론적으로 반환값에 따라, props가 바뀌었는지 안바뀌었는지 판단하도록 작동함!
- TodoItem이 리렌더링 될 상황이 되면,
- React는 prevProps와 nextProps를 이 비교 함수에 전달한다.
- 비교 함수가 true를 반환하면 ➝ props가 같다고 간주하고 리렌더링 X
- false를 반환하면 ➝ props가 바뀌었다고 판단하고 리렌더링 0
💡2. useCallback
: 렌더링될 때마다 함수가 새로 생성되는 것을 방지하여 함수의 재생성을 막아주는 React 훅
의존성 배열이 바뀌지 않는 한, 같은 함수 객체를 재사용 !
함수는 컴포넌트가 렌더링될 때마다 새로운 함수 객체로 다시 생성된다.
그래서 위에 커스텀 훅을 사용해서 함수 객체의 리렌더링을 막아줬었다.
➝ useCallback을 사용하면 동일한 함수 객체를 유지할 수 있다.
아래 처럼 onCreate, onUpdate, onDelete 함수에 useCallback을 적용해보자.
const onCreate = useCallback((content) => {
dispatch({
type: "CREATE",
data: {
id: idRef.current++,
isDone: false,
content: content,
data: new Date().getTime(),
},
});
}, []);
- []를 의존성으로 주었기 때문에, 이 함수는 컴포넌트가 최초로 마운트될 때 한 번만 생성됨.
- 이후 리렌더링이 되더라도 동일한 함수 참조를 유지함.
- Editor 컴포넌트에 전달되는 onCreate가 매번 새로 만들어지지 않기 때문에, Editor가 불필요하게 리렌더링되지 않음.
const onUpdate = useCallback((targetId) => {
dispatch({
type: "UPDATE",
targetId: targetId,
});
}, []);
const onDelete = useCallback((targetId) => {
dispatch({
type: "DELETE",
targetId: targetId,
});
}, []);
- List 컴포넌트에 전달되는 콜백 함수도 매번 새로운 함수로 전달되지 않음.
- 따라서 memo로 감싼 TodoItem들도 불필요한 리렌더링을 방지할 수 있음.
💡 의존성 배열 [] 주의할 점
현재는 모든 useCallback 훅이 [] (빈 배열)을 사용하고 있다.
이는 내부에서 사용하는 값들이 불변이거나 변하지 않을 것임이 확실할 때만 가능하다.
하지만 만약 idRef.current나 dispatch가 바뀌게 된다면,
→ 의존성 배열에 반드시 포함시켜야 함!
💡정리
- React.memo()는 불필요한 렌더링이 실제로 발생하는 경우에만 적용
- React.memo()는 함수형 컴포넌트에서만 사용할 수 있다.
- 부모 컴포넌트 자체가 리렌더링되며, React.memo()의 얕은 비교만으로 리렌더링을 막을 수 없을때는 '커스텀 비교 함수'를 사용한다.
- 너무 많은 컴포넌트에 무분별하게 memo()를 사용하는 것은 오히려 복잡도만 증가시키고 성능 이점이 적을 수 있다.
- useCallback 은 함수 props의 참조를 유지해준다 ! ( 리렌더링 X)
*Ref
https://ko.react.dev/reference/react/memo
memo – React
The library for web and native user interfaces
ko.react.dev
https://ko.react.dev/reference/react/useCallback
useCallback – React
The library for web and native user interfaces
ko.react.dev
설명이 부족한 부분이나 피드백은 댓글로 남겨주세요!
감사합니다 ☺️
'⚙️Frontend > REACT' 카테고리의 다른 글
[React] useMemo (ft. 재연산 최적화) (1) | 2025.05.28 |
---|---|
[React] useReducer (0) | 2025.05.26 |
[React] useEffect (라이프사이클, Dependency array) (2) | 2025.05.22 |
[React] Props 전달 시 {} 사용하는 이유 / map() 함수 (5) | 2025.05.19 |
[React] React Hook과 Custom Hook (4) | 2025.05.14 |