[TodoList] React 성능 최적화
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 애플리케이션에서 성능 최적화를 위해 다음과 같은 기법을 적용할 수 있다.
- 불필요한 연산 방지 → useMemo를 활용해 불필요한 재계산을 막기
- 불필요한 함수 재생성 방지 → useCallback을 활용해 이벤트 핸들러 최적화
- 불필요한 리렌더링 방지 → React.memo와 props 비교를 활용해 렌더링 최적화
💡 하지만 최적화는 기능을 다 구현한 후에 적용하는 것이 가장 좋다.
어떤 연산, 함수, 컴포넌트가 최적화가 필요한지 먼저 판단한 후 적용해야 한다.
본문에서는 학습을 위해 일부러 더 많은 최적화를 적용했으므로 참고하면 좋겠다. 😉