혼자 이것저것 공부해보는/TodoList

[TodoList] React 성능 최적화

silver님 2025. 2. 4. 18:41

React 성능 최적화: 효율적인 렌더링을 위한 실전 가이드 🚀

React 애플리케이션을 개발할 때 성능 최적화는 매우 중요한 요소다. 불필요한 연산을 줄이고, 불필요한 리렌더링을 방지하며, 메모이제이션 기법을 적절히 활용하면 보다 빠르고 효율적인 UI를 제공할 수 있다. 이번 글에서는 React에서 최적화하는 방법을 살펴본다. ✨

 

📝 하지만, 최적화는 기능을 완전히 구현한 후에 하는 것을 권장한다.

먼저 기능을 다 만든 후에 어떤 연산이나 함수, 컴포넌트가 최적화가 필요한지 판단한 후 적용하는 것이 좋다.

본문에서는 학습을 위해 일부러 더 많은 최적화를 적용했으므로 이 점을 감안하여 보면 좋겠다. 😉


1. 컴포넌트 내부 불필요한 연산 방지 ⚡

컴포넌트 내부에서 연산량이 많은 함수가 있을 경우, 매번 렌더링될 때마다 함수가 실행되어 성능이 저하될 수 있다. 예를 들어, 할 일 목록(Todo List)에서 완료된 할 일을 계산하는 경우를 생각해보자.

❌ Before: 불필요한 연산 발생

const getAnalyzeData = () => {
  const totalCount = todos.length;
  const doneCount = todos.filter(todo => todo.isDone).length;
  const notDoneCount = totalCount - doneCount;

  return {
    totalCount,
    doneCount,
    notDoneCount,
  };
};

const { totalCount, doneCount, notDoneCount } = getAnalyzeData();

위 코드에서는 todos의 상태가 변경될 때마다 getAnalyzeData가 실행된다. 하지만 todos가 변경되지 않았다면 다시 계산할 필요가 없다.

✅ After: useMemo를 활용한 최적화

const { totalCount, doneCount, notDoneCount } = useMemo(() => {
  const totalCount = todos.length;
  const doneCount = todos.filter(todo => todo.isDone).length;
  const notDoneCount = totalCount - doneCount;

  return {
    totalCount,
    doneCount,
    notDoneCount,
  };
}, [todos]);

이제 todos가 변경될 때만 다시 계산이 수행되고, 불필요한 연산이 방지된다. 🎯


2. 불필요한 함수 재생성 방지 🔄

함수 컴포넌트 내부에서 이벤트 핸들러나 특정 함수를 선언할 경우, 컴포넌트가 렌더링될 때마다 새로운 함수가 생성된다. 이 문제를 해결하려면 useCallback을 사용하면 된다.

❌ Before: 매 렌더링마다 함수 재생성

const onChangeSearch = (e) => {
  setSearch(e.target.value);
};

위 코드는 List 컴포넌트가 렌더링될 때마다 onChangeSearch 함수가 새롭게 생성된다.

✅ After: useCallback을 활용한 최적화

const onChangeSearch = useCallback((e) => {
  setSearch(e.target.value);
}, []);

이제 onChangeSearch는 초기 렌더링 시 한 번만 생성되고, 이후로는 동일한 함수 참조를 유지한다. 💡


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

React에서 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 함께 리렌더링되는 경우가 많다. React.memo를 활용하면 props가 변경되지 않는 한 불필요한 리렌더링을 방지할 수 있다.

❌ Before: 불필요한 리렌더링 발생

const Header = () => {
  return (
    <div className="Header">
      <h3>오늘은 👀</h3>
      <h1>{new Date().toDateString()}</h1>
    </div>
  );
};

이 코드에서는 부모 컴포넌트가 리렌더링될 때마다 Header 컴포넌트도 함께 리렌더링된다.

✅ After: React.memo를 활용한 최적화

const Header = memo(() => {
  return (
    <div className="Header">
      <h3>오늘은 👀</h3>
      <h1>{new Date().toDateString()}</h1>
    </div>
  );
});

이제 Header 컴포넌트는 props가 변경되지 않는 한 리렌더링되지 않는다. 🎉

🛠 추가적인 최적화: Props 비교를 활용한 memoization

TodoItem 컴포넌트가 동일한 내용을 렌더링할 경우에도 불필요한 리렌더링을 방지할 수 있다.

export default memo(TodoItem, (prevProps, nextProps) => {
  if (prevProps.id !== nextProps.id) return false;
  if (prevProps.isDone !== nextProps.isDone) return false;
  if (prevProps.content !== nextProps.content) return false;
  if (prevProps.date !== nextProps.date) return false;
  return true;
});

이제 TodoItem은 props가 변경되지 않는 한 다시 렌더링되지 않는다. 🚀


결론 🎯

React 애플리케이션에서 성능 최적화를 위해 다음과 같은 기법을 적용할 수 있다.

  1. 불필요한 연산 방지 → useMemo를 활용해 불필요한 재계산을 막기
  2. 불필요한 함수 재생성 방지 → useCallback을 활용해 이벤트 핸들러 최적화
  3. 불필요한 리렌더링 방지 → React.memo와 props 비교를 활용해 렌더링 최적화

💡 하지만 최적화는 기능을 다 구현한 후에 적용하는 것이 가장 좋다.

어떤 연산, 함수, 컴포넌트가 최적화가 필요한지 먼저 판단한 후 적용해야 한다.

본문에서는 학습을 위해 일부러 더 많은 최적화를 적용했으므로 참고하면 좋겠다. 😉

728x90
반응형