한국어

전역 애플리케이션의 성능 최적화를 위한 React 메모이제이션 고급 기법을 알아보세요. React.memo, useCallback, useMemo 등을 언제, 어떻게 사용하여 효율적인 UI를 구축하는지 배웁니다.

React Memo: 전역 애플리케이션 최적화 기법 심층 분석

React는 사용자 인터페이스를 구축하기 위한 강력한 JavaScript 라이브러리이지만, 애플리케이션이 복잡해질수록 성능 최적화는 매우 중요해집니다. React 최적화 도구 중 하나는 React.memo입니다. 이 블로그 게시물은 전 세계 사용자를 위한 고성능 React 애플리케이션을 구축하기 위해 React.memo와 관련 기술을 이해하고 효과적으로 사용하는 방법에 대한 포괄적인 가이드를 제공합니다.

React.memo란 무엇인가요?

React.memo는 함수형 컴포넌트를 메모이제이션하는 고차 컴포넌트(HOC)입니다. 간단히 말해, props가 변경되지 않았다면 컴포넌트가 다시 렌더링되는 것을 방지합니다. 기본적으로 props에 대한 얕은 비교를 수행합니다. 이는 렌더링 비용이 많이 드는 컴포넌트나 props가 동일한데도 자주 다시 렌더링되는 컴포넌트의 경우 성능을 크게 향상시킬 수 있습니다.

사용자 프로필을 표시하는 컴포넌트를 상상해 보세요. 사용자 정보(예: 이름, 아바타)가 변경되지 않았다면, 컴포넌트를 다시 렌더링할 필요가 없습니다. React.memo를 사용하면 이러한 불필요한 다시 렌더링을 건너뛰어 귀중한 처리 시간을 절약할 수 있습니다.

React.memo를 사용하는 이유

React.memo를 사용하면 다음과 같은 주요 이점이 있습니다:

React.memo의 기본 사용법

React.memo를 사용하는 것은 간단합니다. 함수형 컴포넌트를 이로 감싸기만 하면 됩니다:

import React from 'react';

const MyComponent = (props) => {
 console.log('MyComponent rendered');
 return (
 
{props.data}
); }; export default React.memo(MyComponent);

이 예시에서 MyComponentdata prop이 변경될 경우에만 다시 렌더링됩니다. console.log 문을 통해 컴포넌트가 실제로 언제 다시 렌더링되는지 확인할 수 있습니다.

얕은 비교 이해하기

기본적으로 React.memo는 props에 대한 얕은 비교를 수행합니다. 이는 props의 참조가 변경되었는지 확인하며, 값 자체를 확인하지 않습니다. 객체 및 배열을 다룰 때 이 점을 이해하는 것이 중요합니다.

다음 예시를 살펴보세요:

import React, { useState } from 'react';

const MyComponent = (props) => {
 console.log('MyComponent rendered');
 return (
 
{props.data.name}
); }; const MemoizedComponent = React.memo(MyComponent); const App = () => { const [user, setUser] = useState({ name: 'John', age: 30 }); const handleClick = () => { setUser({ ...user }); // Creating a new object with the same values }; return (
); }; export default App;

이 경우, user 객체의 값(nameage)은 동일하게 유지되지만, handleClick 함수는 호출될 때마다 새로운 객체 참조를 생성합니다. 따라서 React.memodata prop이 변경되었다고(객체 참조가 다르기 때문에) 판단하여 MyComponent를 다시 렌더링합니다.

사용자 정의 비교 함수

객체 및 배열의 얕은 비교 문제를 해결하기 위해 React.memo는 두 번째 인수로 사용자 정의 비교 함수를 제공할 수 있도록 합니다. 이 함수는 prevPropsnextProps 두 인수를 받습니다. 컴포넌트가 다시 렌더링되지 않아야 하는 경우(즉, props가 실질적으로 동일한 경우)에는 true를 반환하고, 다시 렌더링해야 하는 경우에는 false를 반환해야 합니다.

이전 예시에서 사용자 정의 비교 함수를 사용하는 방법은 다음과 같습니다:

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

const MyComponent = (props) => {
 console.log('MyComponent rendered');
 return (
 
{props.data.name}
); }; const areEqual = (prevProps, nextProps) => { return prevProps.data.name === nextProps.data.name && prevProps.data.age === nextProps.data.age; }; const MemoizedComponent = memo(MyComponent, areEqual); const App = () => { const [user, setUser] = useState({ name: 'John', age: 30 }); const handleClick = () => { setUser({ ...user }); }; return (
); }; export default App;

이 업데이트된 예시에서 areEqual 함수는 user 객체의 nameage 속성을 비교합니다. 이제 MemoizedComponentname이나 age가 변경될 경우에만 다시 렌더링됩니다.

React.memo 사용 시기

React.memo는 다음 시나리오에서 가장 효과적입니다:

그러나 React.memo가 만능 해결책은 아니라는 점을 아는 것이 중요합니다. 무분별하게 사용하면 얕은 비교 자체에 비용이 발생하여 오히려 성능을 저하시킬 수 있습니다. 따라서 애플리케이션을 프로파일링하고 메모이제이션을 통해 가장 큰 이점을 얻을 수 있는 컴포넌트를 식별하는 것이 중요합니다.

React.memo의 대안

React.memo는 강력한 도구이지만, React 컴포넌트 성능을 최적화하는 유일한 옵션은 아닙니다. 다음은 몇 가지 대안 및 보완 기술입니다:

1. PureComponent

클래스 컴포넌트의 경우 PureComponentReact.memo와 유사한 기능을 제공합니다. 이는 props와 state를 모두 얕게 비교하며, 변경 사항이 있을 경우에만 다시 렌더링합니다.

import React from 'react';

class MyComponent extends React.PureComponent {
 render() {
 console.log('MyComponent rendered');
 return (
 
{this.props.data}
); } } export default MyComponent;

PureComponent는 클래스 컴포넌트에서 불필요한 다시 렌더링을 방지하는 전통적인 방법이었던 shouldComponentUpdate를 수동으로 구현하는 것에 대한 편리한 대안입니다.

2. shouldComponentUpdate

shouldComponentUpdate는 클래스 컴포넌트의 생명주기 메서드로, 컴포넌트가 다시 렌더링되어야 하는지 여부를 결정하기 위한 사용자 정의 로직을 정의할 수 있도록 합니다. 가장 큰 유연성을 제공하지만, 더 많은 수동 작업이 필요합니다.

import React from 'react';

class MyComponent extends React.Component {
 shouldComponentUpdate(nextProps, nextState) {
 return nextProps.data !== this.props.data;
 }

 render() {
 console.log('MyComponent rendered');
 return (
 
{this.props.data}
); } } export default MyComponent;

shouldComponentUpdate는 여전히 사용 가능하지만, PureComponentReact.memo는 일반적으로 단순성과 사용 편의성 때문에 선호됩니다.

3. useCallback

useCallback은 함수를 메모이제이션하는 React 훅입니다. 이는 의존성 중 하나가 변경된 경우에만 변경되는 메모이제이션된 버전의 함수를 반환합니다. 이는 콜백을 메모이제이션된 컴포넌트에 props로 전달할 때 특히 유용합니다.

다음 예시를 살펴보세요:

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

const MyComponent = (props) => {
 console.log('MyComponent rendered');
 return (
 
 );
};

const MemoizedComponent = memo(MyComponent);

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

 const handleClick = useCallback(() => {
 setCount(count + 1);
 }, [count]);

 return (
 

Count: {count}

); }; export default App;

이 예시에서 useCallbackhandleClick 함수가 count 상태가 변경될 때만 변경되도록 보장합니다. useCallback이 없으면 App이 렌더링될 때마다 새로운 함수가 생성되어 MemoizedComponent가 불필요하게 다시 렌더링됩니다.

4. useMemo

useMemo는 값을 메모이제이션하는 React 훅입니다. 이는 의존성 중 하나가 변경된 경우에만 변경되는 메모이제이션된 값을 반환합니다. 이는 모든 렌더링에서 다시 실행할 필요가 없는 비용이 많이 드는 계산을 피하는 데 유용합니다.

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

const App = () => {
 const [input, setInput] = useState('');

 const expensiveCalculation = (str) => {
 console.log('Calculating...');
 let result = 0;
 for (let i = 0; i < str.length * 1000000; i++) {
 result++;
 }
 return result;
 };

 const memoizedResult = useMemo(() => expensiveCalculation(input), [input]);

 return (
 
setInput(e.target.value)} />

Result: {memoizedResult}

); }; export default App;

이 예시에서 useMemoexpensiveCalculation 함수가 input 상태가 변경될 때만 호출되도록 보장합니다. 이는 모든 렌더링에서 계산이 다시 실행되는 것을 방지하여 성능을 크게 향상시킬 수 있습니다.

전역 애플리케이션을 위한 실제 예시

React.memo와 관련 기술이 전역 애플리케이션에 어떻게 적용될 수 있는지 몇 가지 실제 사례를 살펴보겠습니다:

1. 언어 선택기

언어 선택기 컴포넌트는 종종 사용 가능한 언어 목록을 렌더링합니다. 이 목록은 상대적으로 정적일 수 있으며, 자주 변경되지 않습니다. React.memo를 사용하면 애플리케이션의 다른 부분이 업데이트될 때 언어 선택기가 불필요하게 다시 렌더링되는 것을 방지할 수 있습니다.

import React, { memo } from 'react';

const LanguageItem = ({ language, onSelect }) => {
 console.log(`LanguageItem ${language} rendered`);
 return (
 
  • onSelect(language)}>{language}
  • ); }; const MemoizedLanguageItem = memo(LanguageItem); const LanguageSelector = ({ languages, onSelect }) => { return (
      {languages.map((language) => ( ))}
    ); }; export default LanguageSelector;

    이 예시에서 MemoizedLanguageItemlanguage 또는 onSelect prop이 변경될 경우에만 다시 렌더링됩니다. 이는 언어 목록이 길거나 onSelect 핸들러가 복잡할 경우 특히 유용할 수 있습니다.

    2. 통화 변환기

    통화 변환기 컴포넌트는 통화 목록과 환율을 표시할 수 있습니다. 환율은 주기적으로 업데이트될 수 있지만, 통화 목록은 상대적으로 안정적일 수 있습니다. React.memo를 사용하면 환율이 업데이트될 때 통화 목록이 불필요하게 다시 렌더링되는 것을 방지할 수 있습니다.

    import React, { memo } from 'react';
    
    const CurrencyItem = ({ currency, rate, onSelect }) => {
     console.log(`CurrencyItem ${currency} rendered`);
     return (
     
  • onSelect(currency)}>{currency} - {rate}
  • ); }; const MemoizedCurrencyItem = memo(CurrencyItem); const CurrencyConverter = ({ currencies, onSelect }) => { return (
      {Object.entries(currencies).map(([currency, rate]) => ( ))}
    ); }; export default CurrencyConverter;

    이 예시에서 MemoizedCurrencyItemcurrency, rate 또는 onSelect prop이 변경될 경우에만 다시 렌더링됩니다. 이는 통화 목록이 길거나 환율 업데이트가 빈번할 경우 성능을 향상시킬 수 있습니다.

    3. 사용자 프로필 표시

    사용자 프로필을 표시하는 것은 이름, 프로필 사진, 그리고 바이오와 같은 정적인 정보를 보여주는 것을 포함합니다. \`React.memo\`를 사용하면 사용자 데이터가 실제로 변경될 때만 컴포넌트가 다시 렌더링되고, 부모 컴포넌트가 업데이트될 때마다 렌더링되지 않도록 합니다.

    import React, { memo } from 'react';
    
    const UserProfile = ({ user }) => {
     console.log('UserProfile rendered');
     return (
     

    {user.name}

    Profile

    {user.bio}

    ); }; export default memo(UserProfile);

    이는 \`UserProfile\`이 사용자 데이터 자체가 자주 변경되지 않는 크고 자주 업데이트되는 대시보드나 애플리케이션의 일부인 경우 특히 유용합니다.

    일반적인 함정과 피하는 방법

    React.memo는 귀중한 최적화 도구이지만, 일반적인 함정과 이를 피하는 방법을 아는 것이 중요합니다:

    애플리케이션 프로파일링

    React.memo가 실제로 성능을 향상시키는지 여부를 판단하는 가장 좋은 방법은 애플리케이션을 프로파일링하는 것입니다. React는 React DevTools Profiler 및 React.Profiler API를 포함하여 프로파일링을 위한 여러 도구를 제공합니다.

    React DevTools Profiler를 사용하면 애플리케이션의 성능 추적을 기록하고 자주 다시 렌더링되는 컴포넌트를 식별할 수 있습니다. React.Profiler API를 사용하면 특정 컴포넌트의 렌더링 시간을 프로그래밍 방식으로 측정할 수 있습니다.

    애플리케이션을 프로파일링함으로써 메모이제이션으로부터 가장 큰 이점을 얻을 수 있는 컴포넌트를 식별하고 React.memo가 실제로 성능을 향상시키고 있는지 확인할 수 있습니다.

    결론

    React.memo는 React 컴포넌트 성능을 최적화하는 강력한 도구입니다. 불필요한 다시 렌더링을 방지하여 애플리케이션의 속도와 반응성을 향상시키고 더 나은 사용자 경험을 제공할 수 있습니다. 그러나 React.memo를 신중하게 사용하고, 실제로 성능을 향상시키는지 확인하기 위해 애플리케이션을 프로파일링하는 것이 중요합니다.

    이 블로그 게시물에서 논의된 개념과 기술을 이해함으로써 React.memo 및 관련 기술을 효과적으로 사용하여 전 세계 사용자를 위한 고성능 React 애플리케이션을 구축하고, 애플리케이션이 전 세계 사용자에게 빠르고 반응성이 뛰어나도록 보장할 수 있습니다.

    React 애플리케이션을 최적화할 때 네트워크 지연 시간 및 장치 기능과 같은 전역 요소를 고려해야 함을 기억하십시오. 성능과 접근성에 중점을 둠으로써 위치나 장치에 관계없이 모든 사용자에게 훌륭한 경험을 제공하는 애플리케이션을 만들 수 있습니다.

    추가 자료 및 리소스