useMemo는 React에서 **성능 최적화(Performance Optimization)**를 위해 사용하는 훅(Hook) 중 하나이다. 이 훅의 핵심 기능은 메모이제이션(Memoization) 기법을 사용하여, 복잡한 연산의 결과값을 기억해 두었다가, 해당 값이 필요할 때 다시 계산하는 대신 기억해 둔 값을 재사용하는 것이다.
이를 통해 컴포넌트가 리렌더링(Re-rendering)될 때마다 불필요하게 반복될 수 있는 무거운 계산 작업을 방지하여 애플리케이션의 성능을 향상시킬 수 있다.
1. 📌 핵심 개념: 메모이제이션 (Core Concept: Memoization)
메모이제이션은 컴퓨터 과학에서 사용되는 최적화 기법으로, 함수의 반환값을 캐싱(Caching)하는 것을 의미한다. 특정 입력값에 대한 함수 호출 결과를 저장해두고, 이후 동일한 입력으로 함수가 다시 호출되면 실제 함수를 실행하지 않고 캐싱된 결과를 즉시 반환한다. useMemo는 바로 이 메모이제이션 개념을 React 컴포넌트 내의 값 계산에 적용한 것이다.
2. 🤔 useMemo 는 언제 사용할까? (When to Use useMemo?)
useMemo는 주로 다음과 같은 상황에서 유용하게 사용된다.
- 비용이 큰 계산(Expensive Calculations) 방지:
- 매우 큰 배열을 필터링, 정렬, 또는 변환하는 작업.
- 복잡한 수학적 연산이나 데이터 처리 로직.
- 렌더링 중에 수행하기에는 시간이 오래 걸리는 모든 종류의 계산.
- 목적: 이러한 계산들이 매 렌더링마다 반복 실행되는 것을 막아 성능 저하를 방지한다.
- 참조 동등성(Referential Equality) 유지:
- 컴포넌트가 렌더링될 때마다 객체나 배열이 새로 생성되면, 내용이 같더라도 이전 렌더링에서의 객체/배열과는 다른 참조(주소값)를 갖게 된다.
- 만약 이 객체나 배열이 자식 컴포넌트의 prop으로 전달되고, 해당 자식 컴포넌트가 React.memo 등으로 최적화되어 있다면, 부모가 리렌더링될 때마다 불필요하게 자식 컴포넌트도 리렌더링될 수 있다.
- useMemo를 사용하여 객체나 배열 생성을 감싸면, **의존성(Dependency)**이 변경되지 않는 한 동일한 참조를 가진 객체/배열을 반환하여 불필요한 자식 컴포넌트의 리렌더링을 방지할 수 있다.
- 목적: React.memo와 함께 사용하여 자식 컴포넌트의 불필요한 리렌더링을 최적화한다.
3. 💾 사용법 (Syntax)
import React, { useMemo, useState } from 'react';
function MyComponent({ data }) {
const [filter, setFilter] = useState('');
// data 배열을 필터링하는 복잡한 계산이라고 가정
const filteredData = useMemo(() => {
console.log('Filtering data...'); // 이 로그는 filter나 data가 변경될 때만 출력됨
return data.filter(item => item.name.includes(filter));
}, [data, filter]); // 의존성 배열: data 또는 filter가 변경될 때만 함수가 재실행됨
// ... 컴포넌트 렌더링 로직 ...
return (
<div>
<input
type="text"
value={filter}
onChange={e => setFilter(e.target.value)}
placeholder="Filter by name"
/>
<ul>
{filteredData.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
클릭하여 코드 복사
- useMemo는 두 개의 인자를 받는다.
- 계산 함수 (Create Function): 메모이제이션할 값을 계산하는 함수. 이 함수는 순수 함수(Pure Function)여야 하며, 동일한 입력에 대해 항상 동일한 출력을 반환해야 한다.
- 의존성 배열 (Dependency Array): 배열 안에 포함된 값들이 변경될 때만 첫 번째 인자로 전달된 계산 함수를 재실행한다. 만약 배열이 비어있다면([]), 컴포넌트가 처음 마운트될 때만 함수가 실행되고 그 이후에는 항상 메모이즈된 값을 반환한다.
4. ⚙️ 작동 방식 (How it Works)
- 최초 렌더링: useMemo의 계산 함수가 실행되고, 그 결과값이 반환되며 동시에 캐싱된다.
- 이후 리렌더링:
- React는 useMemo의 의존성 배열에 있는 값들을 이전 렌더링 때의 값들과 비교한다.
- 변경 없음: 배열 내의 모든 값이 이전과 동일하다면, 계산 함수를 실행하지 않고 캐싱된(메모이즈된) 값을 즉시 반환한다.
- 변경 있음: 배열 내의 값 중 하나라도 변경되었다면, 계산 함수를 다시 실행하고, 새로운 결과값을 반환하며 이 값을 새롭게 캐싱한다.
5. ✅ 장점 (Advantages)
- 성능 향상: 불필요한 계산을 건너뛰어 렌더링 속도를 개선하고 CPU 사용량을 줄인다.
- 렌더링 최적화: 참조 동등성을 유지하여 React.memo와 함께 사용할 때 자식 컴포넌트의 불필요한 리렌더링을 효과적으로 방지한다.
6. ⚠️ 주의사항 (Considerations)
- 모든 곳에 사용하지 말 것: useMemo 자체도 값을 비교하고 저장하는 약간의 비용이 발생한다. 매우 간단한 계산이나 비용이 크지 않은 작업에 useMemo를 남용하면 오히려 코드를 복잡하게 만들고 미미한 성능 저하를 유발할 수 있다. 항상 성능 측정을 통해 필요한 곳에만 적용하는 것이 좋다.
- 최적화 도구일 뿐: useMemo는 성능 최적화를 위한 도구이지, 코드의 의미론적 동작을 보장하는 도구가 아니다. React는 특정 상황(예: 메모리 부족)에서 메모이즈된 값을 버리고 다음 렌더링 시 다시 계산할 수도 있다 (매우 드문 경우). 따라서 useMemo 없이도 코드가 정상적으로 동작해야 한다.
- 의존성 배열 관리: 의존성 배열을 정확하게 명시하는 것이 중요하다. 누락하면 값이 갱신되어야 할 때 갱신되지 않는 버그가 발생할 수 있고, 불필요한 값을 포함하면 최적화 효과가 떨어질 수 있다. ESLint의 react-hooks/exhaustive-deps 규칙이 이를 도와줄 수 있다.
7. 💡 비교: useCallback 과의 차이점 (Comparison: Difference from useCallback)
useMemo와 useCallback은 모두 메모이제이션을 사용하지만, 대상이 다르다.
- useMemo(() => fn, deps): 함수 fn을 실행하고 그 반환값을 메모이제이션한다. 값 자체를 재사용하고 싶을 때 사용한다.
- useCallback(fn, deps): 함수 fn 자체를 메모이제이션한다. 함수를 자식 컴포넌트에 prop으로 넘겨줄 때, 불필요한 리렌더링을 방지하기 위해 주로 사용한다. useCallback(fn, deps)는 useMemo(() => fn, deps)와 동일하게 동작한다.
간단히 말해, 값을 메모이제이션하려면 useMemo를, 함수를 메모이제이션하려면 useCallback을 사용한다.
'React > 기초' 카테고리의 다른 글
✨ React Context : createContext, Provider, useContext란? (0) | 2025.04.20 |
---|---|
React Context API 사용 가이드 (0) | 2025.04.09 |
💡 useEffect 실무 활용 예시 (0) | 2025.04.07 |
⚙️ useState 주요 활용 사례 (0) | 2025.04.07 |
리액트 기초 01 - 핵심 요소 3 - Hooks : useState란 무엇인가? (0) | 2025.04.07 |