React Native 에서 useMemo , useCallback , memo 등 다양한 함수들을 써왔다. 불필요한 리렌더링을 방지하기 위한 일련의 대책으로 사용했다. 하지만 useCallback , useMemo 등 비슷한 기능들이여서 정확히 어떤 용도인지 잘 몰랐다. 그래서 이참에 한번 글을 읽어 보았다.
이 부분은 React Native 에서도 똑같이 적용된다. 크로스 플랫폼에서 처음 접했으며 이걸 토대로 공부해 나아가서 React 로 넘어가게 된 것이다.
React Native 는 크로스 플랫폼으로 간단한 앱을 2가지 OS 로 하고 싶을땐 정말 좋은 언어이지만 깊이 들어가면 어쩔 수 없이 네이티브 코드를 건드려야 한다
그래서 나는 React Native를 하되 AOS, SWIFT 도 같이 공부하고 있당
그럼 우선 React useMemo / useCallback / memo 에 대해 알아보도록 하자
1️⃣ useMemo
계산량이 많은 연산을 캐싱하여 불필요한 연산을 방지
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
인자로 함수와 Dependencies를 넘겨 받는다. 이 때, 2번째 인자로 넘겨준 의존 인자 중에 하나라도 값이 변경되면 1번째 인자의 함수를 재실행한다. 이를 통해 매 렌더링 할때마다 소요되는 불필요한 계산을 피할 수 있다. 만약 Dependencies 인자를 전달하지 않는다면 매번 새롭게 계산하여 return 한다.
✅ useMemo 사용법
import { useMemo, useState } from "react";
const ExpensiveComponent = ({ num }) => {
// ✅ 값이 변경될 때만 연산 수행 (num이 변할 때만 재계산)
const squaredValue = useMemo(() => {
console.log("🔄 연산 수행 중...");
return num * num;
}, [num]);
return <div>제곱 값: {squaredValue}</div>;
};
export default function App() {
const [count, setCount] = useState(0);
const [num, setNum] = useState(5);
return (
<div>
<button onClick={() => setCount(count + 1)}>증가: {count}</button>
<button onClick={() => setNum(num + 1)}>숫자 변경: {num}</button>
<ExpensiveComponent num={num} />
</div>
);
}
🔍 위 코드에서 useMemo가 없으면?
- count 버튼을 누를 때마다 ExpensiveComponent가 리렌더링되면서 num * num 계산이 계속 실행됨
- 하지만 num이 변경되지 않았다면 굳이 다시 계산할 필요 없음!
- useMemo를 사용하면 이전 값을 재사용하여 불필요한 연산을 막아줌!
✅ useMemo 핵심 정리
- 특정 값(예: 배열 필터링, 연산 등)을 캐싱해서 불필요한 재계산을 막음
- 의존성 배열 ([])에 있는 값이 변경될 때만 재계산
- 렌더링할 때마다 계산이 필요하지만, 값이 바뀌지 않았다면 캐싱된 값 사용
✅ 언제 사용하면 좋을까?
- 리스트 필터링, 정렬, 복잡한 계산이 들어가는 경우
- 성능이 중요한 경우 (예: 수천 개의 데이터를 처리하는 경우)
- 모든 함수를 useMemo로 감싸게 되면 이 또한 리소스 낭비가 될 수 있으므로 퍼포먼스 최적화가 필요한 연상량이 많은 곳에 사용하는 것이 좋다.
2️⃣ useCallback
이전에 만든 함수를 캐싱하여, 불필요한 함수 재생성을 방지
✅ useCallback 사용법
import { useState, useCallback } from "react";
const Child = ({ onClick }) => {
console.log("🔄 Child 컴포넌트 렌더링!");
return <button onClick={onClick}>클릭</button>;
};
const MemoizedChild = React.memo(Child);
export default function App() {
const [count, setCount] = useState(0);
// ✅ `useCallback`으로 함수 메모이제이션 (count가 바뀌지 않으면 동일한 함수 유지)
const handleClick = useCallback(() => {
console.log("버튼 클릭!");
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>증가: {count}</button>
<MemoizedChild onClick={handleClick} />
</div>
);
}
🔍 useCallback이 없으면?
- count가 변경될 때마다 handleClick 함수가 새로 생성됨
- Child 컴포넌트는 onClick이 변경된 것으로 인식하고 불필요한 리렌더링 발생
- useCallback을 사용하면 이전 함수를 재사용하여 리렌더링 방지
✅ useCallback 핵심 정리
- 함수를 메모이제이션(캐싱)하여, 불필요한 함수 재생성을 방지
- 컴포넌트가 리렌더링될 때도 같은 함수 객체를 유지
- useMemo(() => fn, deps)와 비슷하지만, useCallback은 함수 자체를 반환
✅ 언제 사용하면 좋을까?
- 자식 컴포넌트에 함수를 props로 전달할 때
- 컴포넌트가 자주 리렌더링되지만, 함수 내용이 변하지 않는 경우
3️⃣ memo
컴포넌트가 같은 props를 받으면, 리렌더링을 막고 이전 결과를 재사용
하나의 컴포넌트가 똑같은 props를 넘겨 받았을 때 같은 결과를 렌더링 하고 있다면 React.memo를 사용하여 불필요한 컴포넌트 렌더링을 방지 할 수 있다.
React.memo를 사용할 경우 이전과 같은 props가 들어올때는 렌더링 과정을 스킵하고 가장 최근에 렌더링된 결과를 재사용 한다.React.memo는 넘겨받은 props의 변경 여부만을 체크한다. 하지만 컴포넌트 내부에서 useState같은 훅을 사용 하고 있는 경우에는 상태가 변경 되면 리렌더링 된다.
넘겨받은 props의 변경 여부는 shallow compare로 비교 되므로, object의 경우 같은 값을 참조 하고 있는지를 비교한다.
(불변성을 고려해야함)이 비교방식을 커스텀 하고 싶은 경우 아래와 같이 React.memo의 두번째 인자로 넣어주면 된다.
function MyComponent(props) { /* 컴포넌트 로직 */ } function areEqual(prevProps, nextProps) { /* 전달되는 nextProps가 prevProps와 같다면 true를 반환, 같지 않다면 false를 반환해 준다. */ } export default React.memo(MyComponent, areEqual);
✅ memo 사용법
import React, { useState, memo } from "react";
const Child = memo(({ name }) => {
console.log("🔄 자식 컴포넌트 렌더링!");
return <div>이름: {name}</div>;
});
export default function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>증가: {count}</button>
<Child name="John" />
</div>
);
}
🔍 memo가 없으면?
- count가 변경될 때마다 Child도 불필요한 리렌더링 발생
- memo를 사용하면 같은 props를 받으면 렌더링하지 않음
✅ memo 핵심 정리
- 같은 props를 받으면, 리렌더링을 방지하고 이전 결과를 사용
- 내부적으로 React.memo()를 사용하여 최적화
- useMemo나 useCallback과 함께 사용하면 더욱 효과적
✅ 언제 사용하면 좋을까?
- 부모 컴포넌트가 리렌더링될 때, 불필요한 자식 컴포넌트의 리렌더링을 막고 싶을 때
- 변하지 않는 props를 자식 컴포넌트에 전달할 때
🛠 useMemo, useCallback, memo 비교 정리
Hook역할사용 목적
useMemo | 값 캐싱 | 계산량이 많은 연산 최적화 (ex. 배열 필터링, 정렬) |
useCallback | 함수 캐싱 | 불필요한 함수 생성 방지 (ex. props로 함수 전달 시) |
memo | 컴포넌트 캐싱 | 같은 props일 경우 리렌더링 방지 |
- React.memo는 HOC이고, useMemo와 useCallback은 hook이다.
- React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo는 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.
- useMemo는 함수의 연산량이 많을때 이전 결과값을 재사용하는 목적이고, useCallback은 함수가 재생성 되는것을 방지하기 위한 목적이다.
이렇게 세 가지를 잘 활용하면 성능 최적화를 할 수 있어!
다만, 무조건 쓰는 게 아니라 렌더링 성능이 문제 될 때만 최적화하는 게 좋아! 🚀
🚀 정리: "언제 써야 할까?"
❗ 하지만 무조건 사용하면 성능이 더 나빠질 수도 있음!
최적화가 필요한 곳에만 선택적으로 사용하자!
- useMemo → 복잡한 연산 결과를 캐싱해서 불필요한 연산을 줄일 때
- useCallback → 같은 함수 객체를 유지해서 불필요한 리렌더링을 막을 때
- memo → 같은 props라면 리렌더링을 방지하고 싶을 때
✅ 가장 좋은 방법
- 먼저 코드를 작성한 후, 성능 문제(리렌더링, 연산량 증가)가 생기면 최적화!
- React DevTools로 불필요한 리렌더링이 발생하는 컴포넌트를 체크
- 필요한 곳에만 useMemo, useCallback, memo 적용
이젠 확실하게 해놓자 !!

'개발 > React' 카테고리의 다른 글
React_ Semantic 구조 (2) | 2024.08.07 |
---|---|
React_ npm module @types 의 의미 (0) | 2024.07.29 |
React 프로젝트 생성 feat. Typescript (0) | 2024.06.13 |
React 무한 스크롤 (0) | 2024.06.10 |
React 새로고침 데이터 저장 (1) | 2024.06.09 |