useMemo: 개념 이해, 계산 캐싱 및 React Native

저자: IT Sectr 게시일: 2026-07-05 읽는 시간: 10 분

React Native의 useMemo 훅은 컴포넌트 렌더링 간 계산 결과를 메모이제이션하여 비용이 많이 드는 작업의 반복 실행을 방지합니다. 함수를 메모이제이션하는 useCallback과 달리, useMemo는 반환된 값을 보존하고 지정된 의존성이 변경된 경우에만 다시 계산합니다. React Docs, 2024에 따르면, 메모이제이션은 대규모 배열 및 복잡한 데이터 변환과 관련된 계산에 특히 효과적입니다. React Native에서는 불필요한 리렌더링이 발생할 때마다 JavaScript와 네이티브 스레드 간의 브리지 연결에 추가 부하가 발생하므로, useMemo를 올바르게 사용하면 애니메이션의 부드러움과 인터페이스 응답 속도에 직접적인 영향을 미칩니다.

핵심 요점

  • useMemo — 값을 메모이제이션하기 위한 React 훅으로, 렌더링 간 함수의 결과를 캐싱합니다
  • 훅은 두 개의 인수를 받습니다: 계산 함수와 의존성 배열
  • 값은 하나 이상의 의존성이 변경된 경우에만 다시 계산됩니다
  • React Native에서 useMemo는 목록 및 애니메이션의 성능 최적화에 중요합니다
  • useMemo의 과도한 사용은 의존성 비교 비용으로 인해 성능을 저하시킬 수 있습니다

React Native에서 useMemo란

useMemo — 는 추가 패키지 없이 React Native에서 사용할 수 있는 React 표준 라이브러리의 훅입니다. 전달된 함수가 반환하는 값을 메모이제이션하고, 배열에 지정된 의존성이 변경될 때까지 후속 렌더링에서 이 값을 재사용합니다.

훅의 이름은 memoization에서 유래되었습니다 — 비용이 많이 드는 함수의 결과를 캐시에 저장하는 최적화 기법입니다. 동일한 인수로 후속 호출할 때는 다시 계산하는 대신 캐시된 값이 반환됩니다.

React Native의 맥락에서 메모이제이션은 아키텍처적 특성으로 인해 특히 중요합니다. 컴포넌트가 리렌더링될 때마다 JavaScript — 네이티브 브리지를 통해 데이터가 전송되며 시간이 소요됩니다. 컴포넌트에 비용이 많이 드는 계산(목록 필터링, 정렬, 포맷팅)이 포함된 경우, 리렌더링할 때마다 해당 계산이 반복되어 JS 스레드를 차단하고 프레임 드롭을 유발합니다.

React Native Performance Docs, 2024에 따르면, 병목 현상은 종종 네이티브 컴포넌트의 렌더링이 아니라 렌더링 간 JavaScript 로직의 실행입니다. useMemo는 바로 이 문제를 해결합니다.

값 메모이제이션의 작동 방식

useMemo는 함수와 의존성 배열을 받아 메모이제이션된 값을 반환합니다. React는 이전 값과 의존성 배열을 저장합니다. 각 렌더링에서 React는 Object.is를 사용하여 현재 의존성을 이전 의존성과 비교합니다. 하나 이상의 의존성이 변경된 경우 함수가 다시 실행되고 결과가 저장됩니다.

구문 및 인수 전달

useMemo의 시그니처는 웹과 React Native에서 완전히 동일합니다. 첫 번째 인수는 메모이제이션할 값을 반환하는 매개변수 없는 함수입니다. 두 번째는 의존성 배열로, 이 배열이 변경되면 값이 다시 계산됩니다.

js
import React, { useMemo } from 'react';

const sortedList = useMemo(() => {
    return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

이 예제에서 sortedList는 items 배열에 대한 참조가 변경된 경우에만 다시 계산됩니다. 배열의 내용이 변경되었지만 참조가 동일하게 유지된 경우(변이), useMemo는 변경 사항을 감지하지 못합니다 — 이것은 중요한 제한 사항입니다.

Object.is를 통한 의존성 비교

React는 의존성 비교를 위해 Object.is 알고리즘을 사용합니다. 이는 엄격한 동등성 ===과 유사하게 작동하지만 NaN을 올바르게 처리합니다(+0과 -0은 다른 것으로 간주). 즉, 원시 타입(문자열, 숫자, 불리언)의 경우 값으로 비교가 작동하고, 객체와 배열의 경우 참조로 비교가 작동합니다.

js
// Object.is({'a': 1}, {'a': 1}) -> false (different refs)
// Object.is(42, 42) -> true (primitive by value)
// Object.is(NaN, NaN) -> true (correct handling of NaN)

이 메커니즘을 이해하는 것이 중요합니다: 렌더링할 때마다 의존성에 새 객체를 전달하면, 의존성이 매번 변경된 것으로 간주되어 메모이제이션이 무용해집니다.

useMemo 사용 시기

useMemo는 세 가지 시나리오에서 정당화됩니다: 비용이 많이 드는 계산, 자식 컴포넌트에 안정적인 props 전달, 객체의 참조 동일성 유지. React Native에서는 모바일 애플리케이션의 아키텍처로 인해 이 세 가지 시나리오가 정기적으로 발생합니다.

비용이 많이 드는 계산

컴포넌트가 대규모 데이터 배열(필터링, 정렬, 그룹화) 또는 리소스 집약적인 수학적 연산을 처리하는 경우, useMemo는 리렌더링할 때마다 이러한 계산이 반복되는 것을 방지합니다. 모바일 애플리케이션에서 일반적인 예는 목록에 표시하기 전에 API의 데이터를 포맷하는 것입니다.

자식 컴포넌트를 위한 안정적인 Props

자식 컴포넌트가 React.memo로 래핑된 경우, props가 변경될 때만 리렌더링됩니다. 부모 내부에서 useMemo 없이 prop 객체가 생성되면, 부모가 렌더링될 때마다 새 참조가 생성되고 자식 컴포넌트가 불필요하게 리렌더링되어 React.memo의 이점이 무효화됩니다.

스타일 계산의 메모이제이션

React Native에서 스타일은 종종 props에 의존합니다 — 예를 들어, 요소의 너비는 화면 크기를 기반으로 계산됩니다. useMemo를 사용하면 입력 매개변수가 변경된 경우에만 이러한 동적 스타일을 계산할 수 있습니다.

useMemo가 무용한 경우

useMemo는 만능 최적화 도구가 아닙니다. 여러 시나리오에서 이점이 없으며 의존성 저장 및 비교의 오버헤드로 인해 성능을 저하시킬 수도 있습니다. 메모리의 모든 바이트가 중요한 React Native에서, 효과를 측정하지 않고 useMemo를 맹목적으로 적용하는 것은 안티패턴입니다.

훅은 세 가지 주요 경우에 이점이 없습니다. 첫째, 계산이 단순한 경우(단순 덧셈, 문자열 연결), useMemo의 비용이 계산 자체의 비용을 초과합니다. 둘째, 의존성이 매 렌더링마다 변경되는 경우 — 함수는 어차피 실행되며, 메모이제이션은 추가 작업만 더합니다. 셋째, 컴포넌트가 드물게 렌더링되는 경우, 캐시 저장의 오버헤드가 정당화되지 않습니다.

Kent C. Dodds, 2023에 따르면, useMemo를 추가하기 전에 React DevTools Profiler 또는 React Native에 내장된 Performance Monitor를 사용하여 문제를 측정해야 합니다. 컴포넌트 리렌더링에 1ms 미만이 소요되는 경우, 메모이제이션이 필요하지 않습니다.

  • 단순한 계산 — 덧셈, 연결, 단순 삼항 연산
  • 드문 리렌더링 — 한 번만 렌더링되는 정적 화면
  • 변경되는 의존성 — 의존성이 매 렌더링마다 새로운 경우 메모이제이션은 무용함
  • 원시 값 — 숫자와 문자열은 메모이제이션이 필요 없으며, 어차피 가벼움

useMemo와 useCallback 비교

useMemo와 useCallback — 자주 혼동되는 React의 두 메모이제이션 훅입니다. 차이점은 보존하는 대상에 있습니다: useMemo는 함수 실행의 결과(모든 값)를 반환하고, useCallback은 함수 자체를 반환합니다. 둘 다 의존성 배열을 받지만 구문이 다릅니다.

특성useMemouseCallback
반환값함수 실행 결과함수에 대한 참조
첫 번째 인수값을 반환하는 함수메모이제이션할 함수
일반적인 용도계산 캐싱자식 컴포넌트용 안정적인 콜백
동등성useMemo(() => fn, deps)useCallback(fn, deps) === useMemo(() => fn, deps)

실제로 useCallback은 함수를 메모이제이션해야 하는 경우 useMemo에 대한 문법적 설탕입니다. React Native에서는 두 훅 모두 비슷하게 자주 사용됩니다: useMemo — 데이터(포맷된 텍스트, 계산된 스타일), useCallback — 이벤트 핸들러(onPress, onChangeText).

useMemo 실전 예제

React Native 애플리케이션에서 useMemo를 사용하는 두 가지 실제 시나리오를 살펴보겠습니다. 첫 번째는 검색어로 연락처 목록을 필터링하는 것이고, 두 번째는 화면 크기에 따라 동적 스타일을 계산하는 것입니다.

예제 1: 목록 검색 및 필터링

이 예제에서 useMemo는 검색어와 원본 데이터가 모두 변경되지 않은 경우 대규모 연락처 목록의 매 렌더링마다 반복되는 필터링을 방지합니다.

js
const ContactList = ({ contacts, searchQuery }) => {
    const filteredContacts = useMemo(() => {
        if (!searchQuery.trim()) return contacts;
        const query = searchQuery.toLowerCase();
        return contacts.filter((c) =>
            c.name.toLowerCase().includes(query) ||
            c.phone.includes(query)
        );
    }, [contacts, searchQuery]);

    return (
        <FlatList
            data={filteredContacts}
            renderItem={renderContact}
            keyExtractor={(item) => item.id}
        />
    );
};

useMemo가 없으면 필터링이 모든 렌더링에서 실행되며, 애니메이션이나 컴포넌트의 다른 부분에서 상태 변경으로 인한 렌더링도 포함됩니다. useMemo를 사용하면 contacts 또는 searchQuery가 변경된 경우에만 필터링이 실행됩니다.

예제 2: 동적 스타일 계산

React Native에서 스타일은 종종 화면 크기나 컴포넌트 props에 의존합니다. useMemo를 사용하면 이러한 스타일 객체를 한 번 계산하고 의존성이 변경될 때까지 재사용할 수 있습니다.

js
const Card = ({ title, isSelected }) => {
    const cardStyle = useMemo(() => ({
        backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
        borderWidth: isSelected ? 2 : 1,
        padding: 16,
    }), [isSelected]);

    return (
        <View style={cardStyle}>
            <Text>{title}</Text>
        </View>
    );
};

이 접근 방식은 cardStyle 객체가 isSelected가 변경될 때까지 안정적인(동일한 참조) 상태를 유지하도록 보장합니다. Card가 React.memo로 래핑된 경우, 이 패턴은 자식 요소의 불필요한 리렌더링을 방지합니다.

자주 묻는 질문

useMemo는 무엇을 반환하나요?

useMemo는 메모이제이션된 값을 반환합니다 — 첫 번째 인수로 전달된 함수의 실행 결과입니다. 후속 렌더링에서 의존성이 변경되지 않은 경우 React는 캐시된 값을 반환합니다.

useMemo와 useCallback의 차이점은 무엇인가요?

useMemo는 함수 실행의 결과(모든 값: 숫자, 객체, JSX)를 반환하고, useCallback은 함수 자체를 반환합니다. useCallback(fn, deps)는 useMemo(() => fn, deps)와 동등합니다.

useMemo가 필요하지 않은 경우는?

useMemo는 단순한 계산(덧셈, 연결), 드문 리렌더링, 매 렌더링마다 변경되는 의존성 및 원시 타입에 대해 무용합니다. useMemo를 추가하기 전에 항상 성능을 측정하세요.

useMemo가 성능을 저하시킬 수 있나요?

네, 불필요하게 사용하는 경우 그렇습니다. React는 이전 값을 메모리에 저장하고 매 렌더링마다 의존성을 비교합니다. 단순한 계산의 경우 이러한 작업의 오버헤드가 메모이제이션의 이점을 초과합니다.

useMemo 동작을 디버그하는 방법은?

React DevTools Profiler를 사용하여 컴포넌트 렌더링 시간을 측정하세요. React Native에서는 개발자 메뉴를 통해 Performance Monitor도 사용할 수 있습니다. 리렌더링에 1ms 미만이 소요되는 경우 — useMemo가 필요하지 않습니다.

요약

  • useMemo — 값을 메모이제이션하기 위한 React 훅으로, 렌더링 간 함수의 결과를 캐싱합니다
  • 훅은 계산 함수와 의존성 배열을 받아, 이들이 변경된 경우에만 값을 다시 계산합니다
  • React Native에서 useMemo는 목록, 애니메이션 및 복잡한 계산 최적화에 특히 중요합니다
  • 주요 시나리오: 비용이 많이 드는 계산, React.memo용 안정적인 props, 동적 스타일
  • useMemo는 useCallback과 다릅니다: 전자는 값을 반환하고, 후자는 함수를 반환합니다
  • useMemo의 과도한 사용은 의존성 저장 및 비교의 오버헤드로 인해 성능을 저하시킵니다
  • useMemo를 추가하기 전에 React DevTools Profiler 또는 React Native의 Performance Monitor를 사용하여 항상 문제를 측정하세요

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기