한국어

useMemo, useCallback, React.memo를 사용하여 React 애플리케이션의 성능을 최적화하는 포괄적인 가이드입니다. 불필요한 재렌더링을 방지하고 사용자 경험을 개선하는 방법을 알아보세요.

React 성능 최적화: useMemo, useCallback, React.memo 마스터하기

사용자 인터페이스를 구축하기 위한 인기 있는 JavaScript 라이브러리인 React는 컴포넌트 기반 아키텍처와 선언적 스타일로 유명합니다. 그러나 애플리케이션의 복잡성이 증가함에 따라 성능이 문제가 될 수 있습니다. 컴포넌트의 불필요한 재렌더링은 성능 저하와 사용자 경험 저하로 이어질 수 있습니다. 다행히 React는 useMemo, useCallback, React.memo를 포함하여 성능을 최적화하는 여러 도구를 제공합니다. 이 가이드에서는 이러한 기술을 자세히 살펴보고, 고성능 React 애플리케이션을 구축하는 데 도움이 되는 실용적인 예제와 실행 가능한 통찰력을 제공합니다.

React 재렌더링 이해

최적화 기술을 자세히 살펴보기 전에 React에서 재렌더링이 발생하는 이유를 이해하는 것이 중요합니다. 컴포넌트의 상태 또는 props가 변경되면 React는 해당 컴포넌트와 잠재적으로 하위 컴포넌트의 재렌더링을 트리거합니다. React는 가상 DOM을 사용하여 실제 DOM을 효율적으로 업데이트하지만 과도한 재렌더링은 특히 복잡한 애플리케이션에서 여전히 성능에 영향을 미칠 수 있습니다. 제품 가격이 자주 업데이트되는 글로벌 전자 상거래 플랫폼을 상상해 보세요. 최적화가 없으면 작은 가격 변경조차도 전체 제품 목록에서 재렌더링을 트리거하여 사용자 검색에 영향을 줄 수 있습니다.

컴포넌트가 재렌더링되는 이유

성능 최적화의 목표는 불필요한 재렌더링을 방지하여 컴포넌트가 실제로 데이터가 변경된 경우에만 업데이트되도록 하는 것입니다. 주식 시장 분석을 위한 실시간 데이터 시각화를 포함하는 시나리오를 생각해 보세요. 차트 컴포넌트가 사소한 데이터 업데이트마다 불필요하게 재렌더링되면 애플리케이션이 응답하지 않게 됩니다. 재렌더링을 최적화하면 원활하고 반응성이 뛰어난 사용자 경험을 보장할 수 있습니다.

useMemo 소개: 비용이 많이 드는 계산 메모이제이션

useMemo는 계산 결과를 메모이제이션하는 React 훅입니다. 메모이제이션은 비용이 많이 드는 함수 호출 결과를 저장하고 동일한 입력이 다시 발생할 때 해당 결과를 재사용하는 최적화 기술입니다. 이렇게 하면 함수를 불필요하게 다시 실행할 필요가 없습니다.

useMemo 사용 시기

useMemo 작동 방식

useMemo는 두 개의 인수를 사용합니다.

  1. 계산을 수행하는 함수.
  2. 종속성 배열.

함수는 배열의 종속성 중 하나가 변경될 때만 실행됩니다. 그렇지 않으면 useMemo는 이전에 메모이제이션된 값을 반환합니다.

예: 피보나치 수열 계산

피보나치 수열은 계산 집약적인 계산의 고전적인 예입니다. useMemo를 사용하여 n번째 피보나치 수를 계산하는 컴포넌트를 만들어 보겠습니다.


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

function Fibonacci({ n }) {
  const fibonacciNumber = useMemo(() => {
    console.log('Calculating Fibonacci...'); // 계산이 실행될 때를 보여줍니다.
    function calculateFibonacci(num) {
      if (num <= 1) {
        return num;
      }
      return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
    }
    return calculateFibonacci(n);
  }, [n]);

  return 

Fibonacci({n}) = {fibonacciNumber}

; } function App() { const [number, setNumber] = useState(5); return (
setNumber(parseInt(e.target.value)) />
); } export default App;

이 예제에서 calculateFibonacci 함수는 n prop이 변경될 때만 실행됩니다. useMemo가 없으면 Fibonacci 컴포넌트가 재렌더링될 때마다 함수가 실행되어 n이 동일하게 유지되더라도 실행됩니다. 이 계산이 글로벌 금융 대시보드에서 발생한다고 상상해 보세요. 시장의 모든 틱이 전체 재계산을 유발하여 상당한 지연이 발생합니다. useMemo는 이를 방지합니다.

useCallback 소개: 함수 메모이제이션

useCallback은 함수를 메모이제이션하는 또 다른 React 훅입니다. 매번 새로운 함수 인스턴스가 생성되는 것을 방지하므로 콜백을 자식 컴포넌트에 props로 전달할 때 특히 유용합니다.

useCallback 사용 시기

useCallback 작동 방식

useCallback은 두 개의 인수를 사용합니다.

  1. 메모이제이션할 함수.
  2. 종속성 배열.

함수는 배열의 종속성 중 하나가 변경될 때만 다시 생성됩니다. 그렇지 않으면 useCallback은 동일한 함수 인스턴스를 반환합니다.

예: 버튼 클릭 처리

콜백 함수를 트리거하는 버튼이 있는 컴포넌트를 만들어 보겠습니다. useCallback을 사용하여 콜백 함수를 메모이제이션합니다.


import React, { useState, useCallback } from 'react';

function Button({ onClick, children }) {
  console.log('Button re-rendered'); // 버튼이 재렌더링될 때를 보여줍니다.
  return ;
}

const MemoizedButton = React.memo(Button);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount((prevCount) => prevCount + 1);
  }, []); // 빈 종속성 배열은 함수가 한 번만 생성됨을 의미합니다.

  return (
    

Count: {count}

Increment
); } export default App;

이 예제에서 handleClick 함수는 종속성 배열이 비어 있으므로 한 번만 생성됩니다. count 상태 변경으로 인해 App 컴포넌트가 재렌더링될 때 handleClick 함수는 동일하게 유지됩니다. React.memo로 래핑된 MemoizedButton 컴포넌트는 props가 변경된 경우에만 재렌더링됩니다. onClick prop (handleClick)이 동일하게 유지되므로 Button 컴포넌트는 불필요하게 재렌더링되지 않습니다. 대화형 지도 애플리케이션을 상상해 보세요. 사용자가 상호 작용할 때마다 수십 개의 버튼 컴포넌트가 영향을 받을 수 있습니다. useCallback이 없으면 이러한 버튼이 불필요하게 재렌더링되어 지연된 경험을 생성합니다. useCallback을 사용하면 더 부드러운 상호 작용이 보장됩니다.

React.memo 소개: 컴포넌트 메모이제이션

React.memo는 함수형 컴포넌트를 메모이제이션하는 고차 컴포넌트(HOC)입니다. props가 변경되지 않은 경우 컴포넌트가 재렌더링되는 것을 방지합니다. 이는 클래스 컴포넌트의 PureComponent와 유사합니다.

React.memo 사용 시기

React.memo 작동 방식

React.memo는 함수형 컴포넌트를 래핑하고 이전 및 다음 props를 얕게 비교합니다. props가 동일하면 컴포넌트는 재렌더링되지 않습니다.

예: 사용자 프로필 표시

사용자 프로필을 표시하는 컴포넌트를 만들어 보겠습니다. 사용자의 데이터가 변경되지 않은 경우 불필요한 재렌더링을 방지하기 위해 React.memo를 사용합니다.


import React from 'react';

function UserProfile({ user }) {
  console.log('UserProfile re-rendered'); // 컴포넌트가 재렌더링될 때를 보여줍니다.
  return (
    

Name: {user.name}

Email: {user.email}

); } const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => { // 사용자 지정 비교 함수 (선택 사항) return prevProps.user.id === nextProps.user.id; // 사용자 ID가 변경된 경우에만 재렌더링 }); function App() { const [user, setUser] = React.useState({ id: 1, name: 'John Doe', email: 'john.doe@example.com', }); const updateUser = () => { setUser({ ...user, name: 'Jane Doe' }); // 이름 변경 }; return (
); } export default App;

이 예제에서 MemoizedUserProfile 컴포넌트는 user.id prop이 변경된 경우에만 재렌더링됩니다. user 객체의 다른 속성(예: 이름 또는 이메일)이 변경되더라도 ID가 다르면 컴포넌트가 재렌더링되지 않습니다. `React.memo` 내의 이 사용자 지정 비교 함수를 사용하면 컴포넌트가 재렌더링되는 시기를 세밀하게 제어할 수 있습니다. 소셜 미디어 플랫폼에서 지속적으로 업데이트되는 사용자 프로필을 생각해 보세요. `React.memo`가 없으면 사용자의 상태 또는 프로필 사진을 변경하면 핵심 사용자 세부 정보가 동일하게 유지되더라도 프로필 컴포넌트가 완전히 재렌더링됩니다. `React.memo`를 사용하면 대상 업데이트가 가능하고 성능이 크게 향상됩니다.

useMemo, useCallback 및 React.memo 결합

이 세 가지 기술은 함께 사용할 때 가장 효과적입니다. useMemo는 비용이 많이 드는 계산을 메모이제이션하고, useCallback은 함수를 메모이제이션하며, React.memo는 컴포넌트를 메모이제이션합니다. 이러한 기술을 결합하면 React 애플리케이션에서 불필요한 재렌더링 횟수를 크게 줄일 수 있습니다.

예: 복잡한 컴포넌트

이러한 기술을 결합하는 방법을 보여주는 더 복잡한 컴포넌트를 만들어 보겠습니다.


import React, { useState, useCallback, useMemo } from 'react';

function ListItem({ item, onUpdate, onDelete }) {
  console.log(`ListItem ${item.id} re-rendered`); // 컴포넌트가 재렌더링될 때를 보여줍니다.
  return (
    
  • {item.text}
  • ); } const MemoizedListItem = React.memo(ListItem); function List({ items, onUpdate, onDelete }) { console.log('List re-rendered'); // 컴포넌트가 재렌더링될 때를 보여줍니다. return (
      {items.map((item) => ( ))}
    ); } const MemoizedList = React.memo(List); function App() { const [items, setItems] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const handleUpdate = useCallback((id) => { setItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, text: `Updated ${item.text}` } : item ) ); }, []); const handleDelete = useCallback((id) => { setItems((prevItems) => prevItems.filter((item) => item.id !== id)); }, []); const memoizedItems = useMemo(() => items, [items]); return (
    ); } export default App;

    이 예제에서:

    이러한 기술의 조합은 컴포넌트가 필요한 경우에만 재렌더링되도록 하여 상당한 성능 향상을 가져옵니다. 대규모 프로젝트 관리 도구에서 작업 목록이 지속적으로 업데이트, 삭제 및 재정렬되는 것을 상상해 보세요. 이러한 최적화가 없으면 작업 목록에 대한 작은 변경 사항이라도 재렌더링의 연쇄 반응을 트리거하여 애플리케이션을 느리고 응답하지 않게 만듭니다. useMemo, useCallbackReact.memo를 전략적으로 사용하면 복잡한 데이터와 빈번한 업데이트에서도 애플리케이션의 성능을 유지할 수 있습니다.

    추가 최적화 기술

    useMemo, useCallbackReact.memo는 강력한 도구이지만 React 성능을 최적화하는 유일한 옵션은 아닙니다. 고려해야 할 몇 가지 추가 기술은 다음과 같습니다.

    최적화를 위한 글로벌 고려 사항

    글로벌 잠재 고객을 위해 React 애플리케이션을 최적화할 때는 네트워크 대기 시간, 장치 기능 및 지역화와 같은 요소를 고려하는 것이 중요합니다. 몇 가지 팁은 다음과 같습니다.

    결론

    원활하고 반응성이 뛰어난 사용자 경험을 제공하려면 React 애플리케이션 성능을 최적화하는 것이 중요합니다. useMemo, useCallbackReact.memo와 같은 기술을 마스터하고 글로벌 최적화 전략을 고려하여 다양한 사용자 기반의 요구 사항을 충족하도록 확장할 수 있는 고성능 React 애플리케이션을 구축할 수 있습니다. 성능 병목 현상을 식별하고 이러한 최적화 기술을 전략적으로 적용하려면 애플리케이션을 프로파일링하는 것을 잊지 마세요. 너무 일찍 최적화하지 말고 가장 큰 영향을 줄 수 있는 영역에 집중하세요.

    이 가이드는 React 성능 최적화를 이해하고 구현하기 위한 견고한 기반을 제공합니다. React 애플리케이션을 계속 개발할 때 성능을 우선시하고 사용자 경험을 개선할 수 있는 새로운 방법을 지속적으로 찾아보세요.