리액트 강좌: 불필요한 컴포넌트 리렌더 방지하기

1. 들어가며

리액트는 사용자 인터페이스를 구축하기 위한 매우 강력하고 유연한 라이브러리입니다. 그러나, 각종 컴포넌트가 렌더링되는 방식 때문에 성능 문제가 발생할 수 있습니다. 특히, 불필요한 컴포넌트 리렌더링은 애플리케이션의 성능을 저하시킬 수 있습니다. 이번 강좌에서는 리액트에서 불필요한 컴포넌트 리렌더링을 방지하기 위한 다양한 기법을 살펴보겠습니다.

2. 리렌더링의 이해

리액트에서는 상태(state)나 속성(props)이 변경되면 해당 컴포넌트가 리렌더링 됩니다. 비록 이라는 기본적인 동작이 정상적이지만, 과도한 리렌더링은 성능 이슈를 초래할 수 있습니다. 따라서 리렌더링을 효과적으로 관리하는 것이 중요합니다.

2.1. 리렌더링이 발생하는 경우

리액트에서 리렌더링이 발생하는 주요 경우는 다음과 같습니다:

  • 컴포넌트의 state가 변경될 때
  • 컴포넌트의 props가 변경될 때
  • 부모 컴포넌트가 리렌더링될 때 자식 컴포넌트도 리렌더링됨

2.2. 과도한 리렌더링의 비용

불필요한 리렌더링은 다음과 같은 문제를 초래합니다:

  • 성능 저하: 렌더링이 자주 일어나면 애플리케이션 반응 속도가 느려질 수 있습니다.
  • 메모리 사용 증가: 리렌더링으로 인해 불필요한 메모리 사용이 증가할 수 있습니다.
  • 유저 경험 저하: 애플리케이션 속도가 느려지면 사용자 경험에도 부정적인 영향을 미칩니다.

3. 불필요한 리렌더링 방지하기

3.1. React.memo 사용하기

리액트는 React.memo라는 고차 컴포넌트를 제공하여 컴포넌트의 리렌더링을 방지할 수 있습니다. React.memo를 사용하면 동일한 props가 전달될 경우 컴포넌트를 리렌더링하지 않습니다.

import React, { memo } from 'react';

const MyComponent = memo(({ title }) => {
  return <h1>{title}</h1>;
});

3.2. useMemo와 useCallback

리액트의 훅 중 useMemouseCallback을 사용하면 리렌더링을 최적화할 수 있습니다. 이들은 각각 메모이제이션된 값을 반환하고, 메모이제이션된 함수를 반환하여 불필요한 렌더링을 방지합니다.

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

const MyComponent = ({ items }) => {
  const total = useMemo(() => calculateTotal(items), [items]);
  const handleClick = useCallback(() => {
    console.log('Clicked!');
  }, []);

  return (
    <div>
      <h1>Total: {total}</h1>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
};

3.3. PureComponent와 shouldComponentUpdate

클래스형 컴포넌트에서는 PureComponent를 상속받아 불필요한 리렌더링을 방지할 수 있습니다. PureComponent는 기본적으로 props와 state의 얕은 변화를 비교하여 변경이 없으면 리렌더링을 방지합니다.

import React, { PureComponent } from 'react';

class MyComponent extends PureComponent {
  render() {
    return <h1>{this.props.title}</h1>;
  }
}

3.4. 상태 관리 최적화

상태 관리 라이브러리(예: Redux, MobX)를 사용하여 전역 상태를 관리하면 불필요한 리렌더링을 줄일 수 있습니다. 상태를 적절히 분리하여 특정 컴포넌트만 리렌더링되도록 할 수 있습니다.

4. 리렌더링 최적화 방법 사례

다음은 리렌더링 최적화를 적용한 예제입니다.

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

const Item = memo(({ item }) => {
  console.log('Item rendered');
  return <div>{item.name}</div>;
});

const ItemList = ({ items }) => {
  return items.map(item => <Item key={item.id} item={item} />);
};

const MyComponent = () => {
  const [items, setItems] = useState([{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }]);
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <h1>Counter: {counter}</h1>
      <button onClick={() => setCounter(counter + 1)}>Increment Counter</button>
      <ItemList items={items} />
    </div>
  );
};

5. 결론

리액트 애플리케이션에서 성능을 최적화하기 위해서는 불필요한 컴포넌트 리렌더링을 방지하는 것이 필수적입니다. React.memo, useMemo, useCallback, PureComponent와 같은 다양한 기법을 활용하면 보다 성능이 뛰어난 애플리케이션을 만들 수 있습니다. 여러분의 리액트 프로젝트에 최적화를 적용하여 더 나은 사용자 경험을 제공해보세요!