React 렌더링 최소화로 성능 개선하기
리액트 애플리케이션에서 불필요한 렌더링을 줄여 반응성과 자원 사용을 개선하는 방법들을 사례와 코드 중심으로 정리한 실무 체크리스트
목차
개요
리액트 앱에서 성능 문제의 상당수는 과도한 렌더링에서 시작한다. 렌더링이 잦아지면 UI 반응성이 떨어지고 메모리·CPU 비용이 커진다. 이 글은 react 렌더링 최적화 관점에서 리액트 성능 개선과 리액트 불필요한 렌더링 방지 방법을 초보자도 이해하기 쉽게 정리한다.
왜 렌더링을 줄여야 하나
렌더링 비용의 이해
렌더링은 컴포넌트 함수의 실행과 가상 DOM 비교, 실제 DOM 업데이트를 포함한다. 이 과정은 특히 리스트나 복잡한 UI에서 비용이 크다. 불필요한 렌더링은 사용자가 체감하는 지연으로 이어진다.
측정 없이 추정하지 않기
먼저 프로파일링으로 병목을 찾는다. Chrome DevTools의 React 프로파일러 또는 React DevTools를 이용하면 어떤 컴포넌트가 자주 렌더링되는지 확인할 수 있다.
기본 원칙
- 작은 컴포넌트로 분리해 재렌더링 범위를 좁힌다.
- 불변성(immutable) 패턴을 지켜 변경 감지를 단순화한다.
- 최적화는 측정 후 적용한다. Premature optimization을 피한다.
핵심 기법과 사례
1. React.memo로 함수형 컴포넌트 메모화
props가 바뀌지 않으면 재렌더링을 막는다. 단, 비교 비용을 고려해야 한다.
const Item = React.memo(function Item({ value, onClick }) {
return <div onClick={onClick}>{value}</div>
});
// 사용
// <Item value={item} onClick={handleClick} />
2. useCallback과 useMemo의 역할 구분
함수나 값이 자주 바뀌어 자식이 불필요하게 렌더링될 때 사용한다. useCallback은 함수를, useMemo는 계산된 값을 메모화한다.
const Parent = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount(c => c + 1), []);
const expensive = useMemo(() => computeHeavy(count), [count]);
return <Child onInc={increment} value={expensive} />
}
주의: 남용하면 코드 복잡도와 메모리 오버헤드가 커진다.
3. key와 리스트 렌더링 최적화
리스트 항목에 안정적인 key를 부여하면 React의 재사용 효율이 올라간다. 인덱스는 항목이 자주 추가/삭제되는 경우 피한다.
4. 불필요한 객체/함수 재생성 피하기
렌더마다 새로운 객체나 함수를 만들면 props가 바뀐 것으로 간주되어 자식이 렌더링된다. 상위에서 const handlers = useMemo(() => ({ onClick }), [onClick])처럼 묶어 전달한다.
5. 컴포넌트 경계 설정
하나의 큰 컴포넌트보다 작은 단위로 쪼개면 변경이 발생했을 때 영향 범위를 줄일 수 있다. 프레젠테이션과 상태 로직을 분리하면 재사용성과 테스트성이 좋아진다.
6. 가상화(virtualization) 적용
긴 목록은 전체를 렌더링하지 않고 화면에 보이는 부분만 렌더링하는 라이브러리를 사용한다. react-window 또는 react-virtualized가 대표적이다.
// 예: react-window의 간단한 사용
import { FixedSizeList } from 'react-window';
const Row = ({ index, style }) => (>
<div style={style}>Item {index}</div>
);
// <FixedSizeList height={500} itemSize={35} itemCount={1000}>
// {Row}
// </FixedSizeList>
7. 불필요한 리렌더링의 근본 원인 찾기
props로 넘기는 값의 참조가 자주 바뀌는지, 상위 상태가 과도하게 범용인지 확인한다. 컨텍스트는 편리하지만 자주 바뀌는 값은 분리해야 한다.
프로파일링과 체크리스트
- React DevTools 프로파일러로 렌더 빈도와 비용 확인
- 자주 렌더되는 컴포넌트에 React.memo 적용 검토
- 불필요한 객체 재생성 여부 점검
- 리스트는 key 확인, 필요 시 가상화 적용
- 컨텍스트 사용 범위 제한
- 렌더링과 연관된 비동기 작업은 분리
맺음말
리액트 성능 개선은 작은 변화의 누적이다. 먼저 측정하고, 영향이 큰 부분부터 적용한다. 위 방법들은 리액트 불필요한 렌더링 방지에 직접적인 도움을 주며, 단계적으로 적용하면 유지보수성도 함께 개선된다.