이 글은 누구를 위한 것인가
- React Native 앱의 시작 시간이 느려서 고민인 팀
- Hermes와 JSC 중 어떤 엔진을 써야 할지 모르는 개발자
- React Native New Architecture로 마이그레이션을 고려하는 팀
들어가며
React Native 앱이 느리다는 인식이 있지만, 올바른 엔진 설정과 최적화로 네이티브 앱에 가까운 성능을 낼 수 있다. Hermes는 React Native 공식 권장 엔진이며 시작 시간을 크게 줄인다.
이 글은 bluefoxdev.kr의 React Native 성능 최적화 를 참고하여 작성했습니다.
1. Hermes vs JSC 비교
[엔진 비교]
JSC (JavaScriptCore):
iOS 내장, Android는 번들
JIT 컴파일 (실행 중 최적화)
시작 시 JS 파일 파싱 필요
메모리 사용량 높음
적합: 복잡한 계산, JS 특수 기능
Hermes:
Facebook 개발, React Native 공식 권장
AOT 바이트코드 컴파일 (빌드 시 미리 컴파일)
시작 시 파싱 없음 → 빠른 시작
메모리 사용량 낮음
적합: 대부분의 RN 앱 (RN 0.64+)
[Hermes 성능 이점]
앱 시작 시간: 20-30% 단축
메모리 사용: 20-30% 감소
APK 크기: Hermes 엔진 자체 크기 있음
배터리: 개선 (낮은 메모리 사용)
[Hermes 제한사항]
일부 JS API 미지원 (Proxy, eval 등)
디버깅: Flipper, Chrome DevTools 사용
JSC 전용 라이브러리 일부 호환 안 됨
[New Architecture (Fabric + JSI)]
기존: Bridge (비동기, 직렬화)
새로운: JSI (동기, 직접 호출)
성능: 애니메이션, 제스처 대폭 개선
마이그레이션: 라이브러리 지원 확인 필요
2. Hermes 설정 및 최적화
// react-native.config.js - Hermes 활성화
// package.json에서 이미 설정되어 있는 경우가 많음
// android/gradle.properties
// hermesEnabled=true (이미 기본값)
// ios/Podfile
// use_hermes!(true) (기본값)
// 성능 모니터링
import { InteractionManager } from 'react-native';
// 무거운 작업을 인터랙션 후로 지연
function HeavyListScreen() {
const [dataLoaded, setDataLoaded] = React.useState(false);
React.useEffect(() => {
// 화면 전환 애니메이션 완료 후 데이터 로드
InteractionManager.runAfterInteractions(() => {
loadHeavyData().then(() => setDataLoaded(true));
});
}, []);
return dataLoaded ? <DataList /> : <LoadingView />;
}
// 메모이제이션으로 불필요한 리렌더링 방지
const ProductCard = React.memo(({ product, onPress }) => {
return (
<TouchableOpacity onPress={onPress}>
<Image source={{ uri: product.imageUrl }} />
<Text>{product.name}</Text>
</TouchableOpacity>
);
}, (prevProps, nextProps) => {
// shallow 비교 커스터마이징
return prevProps.product.id === nextProps.product.id &&
prevProps.product.price === nextProps.product.price;
});
// FlatList 최적화
function ProductList({ products }) {
const renderItem = React.useCallback(({ item }) => (
<ProductCard product={item} onPress={() => handlePress(item)} />
), []);
const keyExtractor = React.useCallback((item) => item.id, []);
const getItemLayout = React.useCallback((data, index) => ({
length: 120, // 고정 높이
offset: 120 * index,
index,
}), []);
return (
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout} // 가상화 최적화
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
initialNumToRender={8}
/>
);
}
// 번들 크기 최적화
// metro.config.js
const { getDefaultConfig } = require('@react-native/metro-config');
const config = getDefaultConfig(__dirname);
config.transformer.minifierConfig = {
compress: {
drop_console: true, // 프로덕션에서 console.log 제거
},
};
module.exports = config;
// 코드 스플리팅 (re-pack 또는 동적 import)
const LazyScreen = React.lazy(() => import('./screens/LazyScreen'));
// Hermes 디버깅 설정
// launch.json (VS Code)
// {
// "type": "reactnative",
// "request": "attach",
// "name": "Attach to Hermes",
// "cwd": "${workspaceFolder}",
// "port": 8081
// }
// New Architecture 활성화
// android/gradle.properties
// newArchEnabled=true
// ios/Podfile
// ENV['RCT_NEW_ARCH_ENABLED'] = '1'
// JSI 모듈 (Native Module 직접 호출)
// 기존 NativeModule 방식보다 빠름
import { NativeModules } from 'react-native';
// 기존 방식 (Bridge, 비동기)
NativeModules.CalendarManager.createEvent('My Event', (eventId) => {
console.log(eventId);
});
// JSI 방식 (New Architecture, 동기 가능)
// global.CalendarManager.createEvent('My Event');
// 성능 측정
import { Performance } from 'react-native';
function measureScreenLoad() {
const start = Performance.now();
return () => {
const duration = Performance.now() - start;
console.log(`Screen load: ${duration}ms`);
};
}
function DataList() { return null; }
function LoadingView() { return null; }
async function loadHeavyData() {}
function handlePress(item) {}
마무리
React Native 성능 최적화 우선순위: ①Hermes 활성화(시작 시간 20-30% 개선) → ②FlatList getItemLayout으로 가상화 → ③React.memo + useCallback으로 리렌더링 방지 → ④InteractionManager로 애니메이션 중 무거운 작업 지연. New Architecture는 라이브러리 호환성을 먼저 확인하고 점진적으로 마이그레이션하라.