2026-01-08
리액트 성능 최적화 방법
리액트 성능 이슈의 본질
리렌더링이 불필요하게 많이 발생하는 것
- 상태 변경 → 컴포넌트 리렌더링
- 부모 리렌더링 → 자식도 함께 리렌더링
- 계산, 렌더, DOM 업데이트 비용 누적
React.memo – 컴포넌트 리렌더링 방지
컴포넌트 다시 그릴까 말까?
부모가 리렌더링돼도 자식에게 내려준 props가 같으면 자식을 다시 안 그림
컴포넌트의 props가 변경되지 않았을 때, 리렌더링을 방지하여 성능을 최적화
const Child = React.memo(({ value }) => {
console.log('Child 렌더');
return <div>{value}</div>;
});
- value가 이전과 같으면
- Child는 리렌더링 안 됨
- 얕은 비교(shallow compare): 객체/배열/함수 -> 참조 비교 (React.memo는 props참조가 중요)
useMemo – 값(계산 결과) 메모이제이션
계산 결과 다시 구할까 말까?
렌더링 중에 하는 계산 결과를 기억해두고 deps가 안 바뀌면 다시 계산 안 함
값의 재계산을 방지하여 성능을 최적화
const filteredList = useMemo(() => {
return list.filter(item => item.active);
}, [list]);
- list가 바뀔 때만 다시 filter 실행
useCallback – 함수 메모이제이션
함수 새로 만들까 말까?
리렌더링돼도 함수의 참조값을 유지
함수를 메모이제이션하여 불필요한 함수 재생성을 방지
```javascript const onClick = useCallback(() => { setCount(c => c + 1); }, []);
``` - 매 렌더마다 새 함수 생성 X - 같은 함수 재사용 O
이를 통해 자식 컴포넌트로 전달되는 함수나 값이 변경되지 않으면 리렌더링을 피할 수 있음
key 최적화 – 리스트 렌더링의 핵심
index는 아이템의 위치값일 뿐 고유한 식별자가 아님
-> 리스트에 추가, 삭제, 정렬이 발생하면 index가 변경됨 (잘못된 컴포넌트 재사용, 불필요한 리렌더링)
-> 데이터 자체가 가진 고유한 값(id) 사용
-> 렌더링 순서가 바뀌어도 동일한 컴포넌트로 인식 가능
{list.map((item, index) => (
<Item key={index} />
))}
아래처럼 바꾸기
{list.map(item => (
<Item key={item.id} />
))}
- key는 리액트가 컴포넌트를 식별하는 기준
- index 사용 시 DOM 재사용 꼬임 → 성능 + 버그
상태 위치 최적화
- 상위 컴포넌트에 모든 상태 몰아넣기 → 전체 리렌더링
- 상태를 사용하는 컴포넌트 근처로 분리
- 지역 상태(Local State) 활용
렌더링 최적화 패턴
- 조건부 렌더링
{isOpen && <Modal />} - 컴포넌트 분리 (작은 단위로 쪼개기): 리렌더링 범위 축소
비동기 & 이벤트 최적화
- debounce / throttle: 스크롤, 입력 이벤트에서 필수
const onScroll = useCallback( throttle(() => { // 처리 }, 200), [] );
React.lazy & Suspense – 코드 스플리팅
큰 애플리케이션을 여러 개의 작은 코드 파일로 나누고,사용자가 실제로 필요한 화면에 들어왔을 때 해당 코드만 불러와 초기 로딩 시간을 줄임
페이지별로 코드를 분리해, 처음에는 필요한 코드만 로드하고 이후에 필요한 코드를 추가로 불러오는 방식
초기 로딩 시간이 길어지는 경우,라우트별 코드 분할이 필요한 경우 사용