
최근 마이페이지에서 리스트 및 카드 컴포넌트의 리렌더링을 최적화하기 위해
React.memo, useMemo, useCallback을 적극 활용하여 최적화를 진행했다.
하지만 적용 후 코드가 더 복잡해지고,
⚠️ 정말 필요한 곳에만 최적화를 적용한 것이 맞는지?
⚠️ 오히려 과한 최적화로 인해 성능이 저하될 수도 있는지?
라는 고민이 생겼다. 이에 대해 트러블슈팅을 진행하고, 어떤 기준으로 적용해야 하는지 정리해봤다.
앞서, 메모이제이션이란?
메모이제이션(memoization)은 이전에 계산한 값을 저장함으로써 동일한 계산을 반복하지 않도록 하는 기술입니다. 메모이제이션을 사용하면 이전에 계산한 값을 재사용하여 성능을 향상시킬 수 있습니다.
useCallback 남발 문제: 이벤트 핸들러 최적화가 정말 필요할까?useCallback을 사용하면 이벤트 핸들러가 리렌더링될 때마다 새로 생성되지 않도록 최적화할 수 있다.useCallback을 적용했다.tsx
복사편집
const handleClick = useCallback(() => {
console.log("버튼 클릭!");
}, []);
하지만, 이렇게 모든 핸들러에 useCallback을 적용하는 것이 오히려 성능을 저하시킬 수 있음을 알게 됐다.
useCallback을 남발하면 안 되는 이유useCallback이 오히려 메모리를 추가적으로 사용하며, 함수를 캐싱하는 과정에서 불필요한 오버헤드가 발생할 수 있다.setState 변경 함수는 useCallback이 없어도 무방하다.❌ 불필요한 useCallback 적용 예제
const handleToggle = useCallback(() => {
setIsOpen(prev => !prev);
}, []);
✔ 위 코드는 useCallback 없이도 성능 저하 없이 동작 가능
const handleToggle = () => {
setIsOpen(prev => !prev);
};
useCallback을 언제 써야 할까?✔ 부모 컴포넌트가 자주 리렌더링되며, 자식 컴포넌트로 함수를 props로 넘길 때만 useCallback 사용
✔ 이벤트 핸들러가 한 번만 실행되거나, 자식에게 전달되지 않는다면 useCallback을 사용하지 않기
const handleClick = () => {
console.log("버튼 클릭!");
};
📌이렇게 하면 불필요한 메모리 사용을 줄이고 코드도 간결해진다.
useMemo를 모든 데이터에 적용하는 문제: 정말 최적화가 되는가?useMemo로 감싸서 불필요한 렌더링을 줄이고자 했다.useMemo를 적용하는 것이 적절한지 확신이 없었다.const memoizedList = useMemo(() => guestbooksData.content, [guestbooksData]);
📌 문제점:
guestbooksData.content 자체가 이미 서버에서 받아온 값이므로 불변 객체이다.
즉, useMemo를 사용하지 않아도 불필요한 재계산이 발생하지 않는다.
불필요한 useMemo 사용은 오히려 메모리 사용량을 증가시킬 수 있다.
useMemo를 언제 써야 할까?✔ 리스트 데이터가 filter, map, sort 등 연산을 수행할 경우만 useMemo를 사용
✔ 가공이 필요 없는 원본 데이터에는 useMemo를 사용하지 않기
❌ 불필요한 useMemo 적용 예제
tsx
복사편집
const memoizedList = useMemo(() => guestbooksData.content, [guestbooksData]);
최적화된 코드
const guestbookList = guestbooksData.content; // 그대로 사용해도 무방
📌 불필요한 메모리 사용을 줄이고, 코드 가독성도 향상됨
React.memo 적용 범위 문제: 작은 컴포넌트에도 적용해야 할까?React.memo를 적용하여 리스트 아이템(GatheringItem, MainCard)의 불필요한 리렌더링을 방지했다.React.memo를 적용하는 것이 과연 적절한가?React.memo를 적용하면, 컴포넌트의 props가 변경되지 않으면 리렌더링을 방지할 수 있다.React.memo를 적용해도 리렌더링이 계속 발생하므로 의미가 없다.❌ 불필요한 React.memo 적용 예제
const SmallComponent = React.memo(({ text }) => {
return <p>{text}</p>;
});
최적화된 코드
const SmallComponent = ({ text }) => {
return <p>{text}</p>;
};
📌 작은 컴포넌트에는 React.memo를 적용하지 않는 것이 더 효율적! 그치만 대부분은 작은경우에도 많이 쓴다. 이는 브라우저 성능차이가 있음을 확인하고 느려졌으면 사용해보자~!
React.memo, useMemo, useCallback은 언제 써야 할까?| 언제 사용해야 하나? | 언제 사용하지 말아야 하나? | |
|---|---|---|
useCallback | 부모에서 자식으로 props로 전달될 때 (이벤트 핸들러) | setState만 변경하는 단순한 함수 |
useMemo | 비싼 연산(필터링, 정렬 등)을 캐싱할 때 | 단순한 값(숫자, 문자열)에는 필요 없음 |
React.memo | 부모가 자주 리렌더링되지만, 자식은 변경되지 않는 경우 | 작은 UI 컴포넌트 또는 props가 자주 변경되는 경우 |
✅ CSR 환경에서 React.memo, useMemo, useCallback을 어떻게 활용해야 할까?
useMemo를 통해 가공된 데이터 캐싱props로 전달되는 함수만 useCallback 사용React.memo 적용 ✅ 단순한 setState 변경 함수에는 useCallback 사용하지 말 것
✅ 가공이 필요 없는 데이터에는 useMemo 사용하지 말 것
✅ 작은 UI 컴포넌트에는 React.memo를 적용하지 말 것
✅ 렌더링 비용이 높은 경우(리스트, 차트, 복잡한 UI)에만 최적화 적용
🎯 즉, React.memo, useMemo, useCallback은 남발하지 말고 "필요할 때만" 사용해야 최적화 효과를 제대로 볼 수 있다!