오늘 수업에서는 ReactNative의 개발 과정에서 발생하는 최적화 문제와 이런 최적화 문제를 해결하는 과정에 대해서 공부해봤습니다. 렌더링에 대한 내용으로 시작해 React의 최적화 도구와 FlastList를 사용하면서 생기는 대표적인 최적화 문제 그리고 해당 문제를 해결하는 방법을 위 블로그에서 정리하였습니다.
1. Rendering과 참조동일성
느린 화면을 고치는 순서
렌더링은 화면을 다시 설계도를 계산하는 것에 가깝습니다.
리렌더링 확인 → 원린 찾기 → 필요 도구 적용 → 구조 정리
- 리렌더링 확인 :
- 원인 찾기
- 필요 도구 적용
- 구조 정리 :
Re-recersing 발생시점
리렌더링은 주로 다음 3가지 이유로 인해서 리렌더링된다.
- state가 바뀔때
- props가 바뀔때
- 부모가 다시 렌더링될 때
function FeedScreen() {
const [posts, setPosts] = useState(initialPosts)
return (
<FlatList
data={posts}
renderItem=
)
}위 코드를 보면 함수가 매번 새로 만들어진다는 문제점을 가지고 있습니다. 함수 안의 내용이 같아도 매번 새로 만들어지면 React는 다른 값으로 인식합니다.
참조 동일성(reference equality)
객체, 배열, 함수는 내용이 같더라도 다른 객체이다.
{} === {}
[] === []
우리가 종이에 같은 내용을 적더라도 A종이와 B종이는 다른 종이이다.
2. React 최적화 도구
React.memo
같은 props면 다시 실행을 줄여줌.
const PostCard = React.memo(function PostCard({
post,
onLike,
}: Props ) {
console.log('PostCard render:', post.id);
return(
<Pressable >
</Pressable>
)
})하지만 React.memo는 효과가 적어지는 경우 또한 존재한다.
- onLike 함수 생성
- 렌더링할 떄마다 새 함수가 만들어지기 때문.
- props 변경으로 판단
- 함수 참조가 달라지면
- 다시 렌더링
- 함수를 다시 기억한다.
useMemo
계산 결과, 배열, 객체를 기억한다. 즉, 비싼 계산혹은 배열을 기억해준다.
const filteredPosts = useMemo(() => {
return posts.filter(post =>
post.title)})
사용하면 좋은 상황
- 필터링, 정렬, 그룹핑
- 바복 비용이 커질 수 있는 계산
TIP
> #### useMemo를 남발하면 안되는 이유
useMemo의 경우 dependency 비교라는 비용을 가진다. 현재 dependency와 이전 dependency를 비교해서 같으면 이전 값 사용하는 원리도 동작합니다.
useCallback
함수 참조를 기억한다. (함수를 기억하는게 아닌 함수의 참조를 기억하는 것이다.)
const handleLike = useCallback(((id: stirng) => {
setPosts(prev =>
prev.map(post =>
? {...post}))
}))
useCallback은 다음과 같은 상황에서 강점을 가지고 있습니다.
- React.memo 자식에게 함수를 넘길 때
- FlastList renderItem을 안정화할 때
- 함수 재생성이 실제 문제일 때
도구 선택 최종 정리

3. FlatList 최적화
FlastList는 리스트인데 같은 값을 계속 등장할 경우 해당 값을 렌더링할때 새로 값을 계산하므로, 많은 비용을 가지게된다.
NOTE
여기서 FlastList는 Reactnative에서 데이터를 효율적으로 렌더링하기 위해서 사용하는 컴포넌트입니다. 기존 scrollview와 달리 화면에 보이는 아이템만 렌더링하기 때문에 성능이 좋아집니다.
<FlatList
data={posts}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>getItemLayout
const ITEM_HEIGHT = 96;
<FlastList
data={posts}
keyExtractor={(item) => item.id}
renderer
getItemLayout을 사용할 경우 직접 값을 계산하지 말고 우리가 실질적인 데이터를 제공하므로 빠르게 렌더링을 할 수 있게 된다.
사용하면 좋은 경우 카드 높이가 거의 고정된 리스트
- 채팅 목록
- 상품 목록
- 알림 목록

FlatList의 문제 해결 절차 : 렌더링 양 조절
보이는 화면보다 조금 더 렌더링하되, 너무 많이 하지는 않게 맞춘다.

Trade-Off
값을 줄이면 JS 작업은 줄 수 있지만, 스크롤 중 빈 영역이 보일 수 있다. 값을 키우면 빈 영역은 줄어들지만, 한 번에 처리할 렌더링 작업이 늘어날 수 있다.
state 위치가 리렌더링 범위를 결정한다.
function FeedScreen() {
return (
<>
<CommentInput />
<PostList />
</>
);
}
function CommentInput() {
const [text, setText] = useState('');
return
<TextInput value={text} onChangeText={setText} />;
}4. 최적화
우리가 최적화를 하기 위해서는 일단 성능 테스트를 진행해야합니다. 이 경우 주로 다음과 같은 순서를 거칩니다.
- 재현
- 로그
- 프로파일링
- 작게 수정
- 재측정
이때 어떤 도구를 이용해서 성능을 측정하냐에 따라 다른 기준으로 측적하게 됩니다.

React Profiler
어떤 컴포넌트가 언제, 얼마나 오래 렌더링됐는지 확인합니다.
<Profiler id="Feed" onRender={onRender}>
<FeedScreen />
</Profiler>- 어떤 컴포넌트가 렌더링 됐는가
- 어떤 Commit이 오래 걸렸는가
- 같은 props인데 반복되는가
- 수정 전/후를 비교했는가?
아이템 경량화
const styles = StyleSheet.create({
card: {padding:16, borderRadius: 12 },
});useEffect 의존성
useEffect(() => {
fetchFeed();
}, []);useEffect(() => {
fetchFeed();
}) Custom Hook : 복잡도 분리
최적화 코드를 계속 짜다보면 코드가 늘어나기 마련입니다. 이런 문제를 해결하기 위해서 Custom Hook을 설정하는 것이 중요합니다.
정리
성능 최적화 체크리스트
-
성능 속도 측정
-
State가 리렌더링을 만드는가?
-
새 객체, 배열, 함수를 props로 넘기지 않는가?
-
React.memo가 필요한 컴포넌트인가?
-
Profiler로 전/후를 비교했는가?
-
useMemo/useCallback dependency가 정확한가?
-
Flat:list 옵션과 item component가 적절한가?
-
상태 구독 범위가 너무 넓지 않는가?
-
useEffect가 불필요한가?
그리고 모든 개발에 있어서 “좋은 코드는 빠르기 전에 설명 가능해야 한다.”라는 것이 가장 중요하다. 코드 읽는 사람이 봤을때도 어떻게 동작하는지 바로 알수 있도록 만드는 것이라고 할 수 있다.