React Native 성능 최적화 기초: 리렌더링과 useMemo, useCallback 정리
상태 관리까지 정리했다면
이제 앱은 “기능적으로는” 꽤 그럴듯해진다.
그다음에 체감되는 문제가 하나 있다.
- 화면 전환이 묘하게 버벅인다
- 입력할 때 전체 화면이 다시 그려지는 느낌
- 리스트가 많아지면 확 느려진다
이건 대부분
렌더링 구조를 제대로 이해하지 못한 상태에서
컴포넌트가 불필요하게 다시 그려지기 때문이다.
이 글에서는
React Native에서 성능 최적화를 시작할 때
가장 먼저 이해해야 할 포인트를 정리한다.
이 글이 필요한 사람
- 앱이 커질수록 점점 느려지는 느낌이 드는 경우
- useMemo, useCallback을 왜 쓰는지 감이 안 오는 경우
- “일단 다 감싸면 되지 않나?”라고 생각한 적 있는 경우
React Native 성능의 핵심은 렌더링이다
React Native 앱에서
성능 이슈의 대부분은 이 한 문장으로 정리된다.
“필요 없는 리렌더링이 너무 많다”
React는 기본적으로
state나 props가 바뀌면
해당 컴포넌트를 다시 렌더링한다.
문제는
안 바뀌어도 되는 것까지 같이 렌더링되는 경우다.
리렌더링은 왜 문제가 될까
리렌더링 자체가 나쁜 건 아니다.
하지만 이런 상황에서는 문제가 된다.
- 리스트 아이템이 수십 개 이상
- 각 아이템에 이미지, 계산 로직 포함
- 입력할 때마다 전체 리스트가 다시 렌더링됨
특히 모바일에서는
이게 바로 버벅임으로 체감된다.
[이미지: React Native 리렌더링 흐름 개념도]
먼저 체크해야 할 기본 원칙
useMemo, useCallback을 쓰기 전에
이 원칙부터 점검하는 게 맞다.
1️⃣ 컴포넌트 분리
// ❌ 모든 걸 한 컴포넌트에서 처리
// ✅ 역할별로 컴포넌트 분리
- 화면 컴포넌트
- 리스트 아이템 컴포넌트
- 버튼 컴포넌트
성능 최적화의 절반은
컴포넌트 분리로 해결된다.
2️⃣ state 위치 점검
state가 너무 위에 있으면
불필요한 리렌더링 범위가 커진다.
- 정말 전역 상태인가?
- 이 화면에서만 써도 되는 상태 아닌가?
이걸 먼저 정리하지 않으면
hook으로 감싸도 근본 해결이 안 된다.
useMemo란 무엇인가
useMemo는
계산 결과를 기억해두는 hook이다.
const value = useMemo(() => {
return expensiveCalculation(data);
}, [data]);
- data가 바뀌지 않으면
- 이전 계산 결과를 그대로 사용
useMemo를 언제 써야 할까
useMemo는
모든 곳에 쓰는 도구가 아니다.
다음 조건에 해당할 때 고려한다.
- 계산 비용이 있는 로직
- 렌더링마다 다시 계산할 필요 없는 값
- 리스트 아이템 내부 계산
예시
const totalPrice = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
단순한 값에는
useMemo를 쓰지 않는 게 오히려 낫다.
useCallback이 필요한 이유
React에서는
함수도 객체다.
즉, 렌더링될 때마다
함수가 새로 생성된다.
이게 왜 문제냐면
props로 함수를 넘길 때다.
useCallback 기본 개념
const handlePress = useCallback(() => {
console.log('pressed');
}, []);
- 의존성이 바뀌지 않으면
- 같은 함수 참조를 유지
useCallback을 써야 하는 대표적인 경우
1️⃣ 자식 컴포넌트에 함수 전달
<Item onPress={handlePress} />
이때 Item이 memo로 감싸져 있다면
함수 참조가 바뀌는 순간
리렌더링이 발생한다.
2️⃣ FlatList renderItem 내부 함수
리스트 성능 최적화에서는
useCallback이 꽤 중요한 역할을 한다.
React.memo와 함께 생각해야 한다
useCallback은
혼자 쓰는 도구가 아니다.
보통은
React.memo와 함께 사용된다.
const Item = React.memo(({ onPress }) => {
return <Pressable onPress={onPress} />;
});
이 조합이 있어야
**“진짜로 렌더링을 막는 효과”**가 난다.
흔한 오해들
❌ 모든 함수에 useCallback
→ 코드만 복잡해지고 효과 없음
❌ 모든 값에 useMemo
→ 오히려 성능 손해 가능
❌ 성능 문제 없는데 미리 최적화
→ 유지보수 난이도만 상승
성능 최적화는
문제가 보일 때 하는 게 맞다.
실무 기준으로 추천하는 접근 순서
- 컴포넌트 분리
- state 위치 재정리
- FlatList 구조 점검
- React.memo 적용
- 필요할 때 useMemo / useCallback
이 순서를 지키면
과한 최적화를 피할 수 있다.
정리
- 성능 문제의 대부분은 리렌더링 때문이다
- 컴포넌트 분리가 최우선이다
- useMemo는 계산 최적화용이다
- useCallback은 함수 참조 유지용이다
- 문제를 확인한 뒤 적용하는 게 가장 좋다
다음 글에서는
FlatList 성능 최적화를 중심으로
- keyExtractor
- getItemLayout
- windowSize
같은 실무 옵션들을 정리해보려고 한다.