Hermes는 React Native를 위해 특별히 개발되고 모바일 기기에 최적화된 오픈 소스 JavaScript 엔진입니다. JavaScriptCore와 달리, Hermes는 빌드 단계에서 JS 코드를 사전에 바이트코드로 컴파일하여 애플리케이션의 빠른 시작을 보장합니다. Meta, Hermes Docs, 2024에 따르면, Hermes는 React Native 버전 0.70부터 기본으로 사용됩니다.
주요 포인트
Hermes는 Meta 팀이 React Native를 위해 특별히 만든 컴팩트한 JavaScript 엔진입니다. 2019년에 React Native에서 기본으로 사용되던 JavaScriptCore(JSC)의 대체제로 발표되었습니다. Hermes의 주요 목표는 모바일 애플리케이션, 특히 리소스가 제한된 Android에서 가장 빠른 시작을 제공하는 것입니다.
Hermes의 주요 기능은 사전 컴파일 (AOT — Ahead-of-Time)입니다. 실행 시에 코드를 컴파일하는 전통적인 JS 엔진(JIT — Just-in-Time)과 달리, Hermes는 앱 빌드 단계에서 JavaScript를 바이트코드로 컴파일합니다. 이는 장치에서 컴파일이 일어나지 않음을 의미합니다 — 바이트코드는 Hermes 가상 머신에 의해 즉시 실행됩니다.
Hermes는 보편적인 JS 엔진이 아닙니다 — 오직 React Native 시나리오에만 최적화되었습니다. 모바일 개발에 필요하지 않은 기능(eval, 생성자, 일부 ES6 기능)이 없습니다. 이를 통해 엔진이 더 컴팩트하고 빨라졌습니다. Meta에 따르면, Hermes의 크기는 압축 상태에서 약 200–300 KB로, JSC(약 2–3 MB)보다 훨씬 작습니다.
Hermes의 아키텍처는 JIT 컴파일러가 없다는 점에서 전통적인 JS 엔진과 다릅니다. 그 대신, Hermes는 사전 컴파일된 바이트코드를 실행하는 효율적인 가상 머신(VM)을 사용합니다. JIT를 포기하면 예측 가능한 성능, 더 적은 메모리 소비, 그리고 시작 시간 단축이라는 세 가지 주요 이점이 있습니다.
Hermes 바이트코드는 빠른 실행을 위해 최적화된 JavaScript 코드의 컴팩트 표현입니다. 빌드 단계(Metro 통해)에서 JavaScript 파일은 .hbc(Hermes Bytecode) 파일로 컴파일됩니다. 앱이 이 파일들을 로드하고, Hermes VM이 추가 컴파일 없이 실행합니다. 빌드 과정은 hermes-transformer 플러그인을 통해 Metro에 통합되어 있습니다.
Hermes에서의 JavaScript 처리 흐름은 세 단계로 구성됩니다: Parse — 소스 코드의 구문 분석, Compile — 바이트코드로 번역, Execute — 가상 머신에 의한 바이트코드 실행. 처음 두 단계는 빌드 단계에서 실행되고, 세 번째는 장치에서 실행됩니다. 이는 세 단계 모두가 장치에서 실행되는 JSC와 근본적으로 다릅니다.
# Hermes 바이트코드 컴파일 (빌드 시)
hermes -emit-binary -out index.hbc index.js
# 생성된 바이트코드 크기 확인
ls -lh index.hbc
# 바이트코드 통계 표시
hermes -dump-bytecode index.hbc
# 프로덕션용 바이트코드 최적화
hermes -O index.js -emit-binary -O target.hbc
컴파일 후, 모든 .hbc 파일은 네이티브 코드와 함께 APK 또는 IPA에 패키징됩니다. 앱이 시작되면, Hermes VM은 바이트코드를 로드하고 거의 즉시 실행을 시작합니다 — V8과 JSC에 특징적인 JIT 워머업 시간이 필요 없습니다.
Hermes와 JavaScriptCore(JSC)의 비교는 언제 어떤 엔진을 선택할지 이해하는 데 도움이 됩니다. JSC는 Apple의 완전한 JS 엔진으로, Safari에서 사용됩니다. Hermes는 React Native를 위한 전문 엔진입니다. 둘 사이에서 선택하는 것은 프로젝트의 우선순위에 따릅니다.
| 특성 | Hermes | JavaScriptCore |
|---|---|---|
| 컴파일 | AOT (빌드 시) | JIT (장치에서) |
| 시작 시간 | 50–100 ms | 200–400 ms |
| 메모리 사용량 | 30–40% 더 적음 | 기준 |
| 크기 | ~250 KB (압축) | ~2 MB (압축) |
| ES6+ 지원 | 제한적 | 완전 |
Hermes는 시작 속도와 메모리 소비에서 이점이 있지만, 복잡한 계산 성능에서는 JSC에 뒤집니다. JSC의 JIT 컴파일은 실행 중 핫 코드 경로를 최적화하여 CPU 집중적인 작업에서 유리합니다. UI 업데이트와 내비게이션이 주요 작업 부하를 구성하는 일반적인 React Native 앱에서, Hermes는 빠른 시작을 통해 더 나은 사용자 경험을 제공합니다.
Hermes에는 React Native에 특정적인 여러 최적화가 포함되어 있습니다. 가장 중요한 것은 JSI(JavaScript Interface)와의 통합입니다. Hermes는 JSI를 네이티브로 지원하여, 추가 계층 없이 새로운 React Native 아키텍처(Fabric + Turbo Modules)를 사용할 수 있습니다. 이로써 Hermes와 새 아키텍처는 최대 성능을 위한 이상적인 페어가 됩니다.
Hermes는 모바일 기기에 최적화된 세대별 가비지 컬렉터(generational GC)를 사용합니다. 가비지 컬렉션이 UI에서 눈에 띄는 일시 정지(jank)를 유발할 수 있는 JSC와 달리, Hermes는 프레임 사이에 작은 분량으로 GC를 수행하여 사용자 경험에 미치는 영향을 최소화합니다. Meta에 따르면, 일반적인 React Native 시나리오에서 Hermes의 GC 일시 정지는 JSC보다 2–3배 짧습니다.
Hermes는 레이지 모듈 로딩을 지원합니다 — JavaScript 모듈은 실제로 필요할 때만 컴파일됩니다. 이를 통해 바이트코드 크기가 줄어들고 시작이 빨라집니다. 또한, Hermes는 컴파일 시점에 상수 표현식을 평가하여 가능한 경우 함수 호출을 상수값으로 대체합니다.
// Hermes 특정 최적화 예
// 1. Hermes가 상수 표현식을 인라인
const TAX_RATE = 0.07;
const price = 100;
// Hermes가 컴파일 시 TAX_RATE * price를 미리 계산
const totalWithTax = price * (1 + TAX_RATE);
// 2. Hermes가 속성 액세스 체인을 최적화
const config = { api: { timeout: 5000 } };
// Hermes가 내부적으로 config.api 액세스를 캐시
const timeout = config.api.timeout;
fetch(url, { timeout });
Hermes가 Chrome DevTools를 통한 디버깅(Hermes 0.12부터)을 지원한다는 점은 중요합니다. 개발자는 익숙한 도구(브레이크포인트, 메모리 프로파일링, 변수 검사)를 사용할 수 있습니다. 이로써 디버깅이 제한되었던 이전 버전에서의 Hermes에 대한 주요 반대 사유 중 하나가 해결됩니다.
Hermes의 설정은 React Native 버전에 따라 다릅니다. React Native 0.70부터, Hermes는 새 프로젝트에서 기본으로 활성화됩니다. 이전에 만들어진 프로젝트의 경우나 엔진을 전환할 때는 빌드 설정을 통해 구성합니다.
Android의 경우, Hermes는 android/app/build.gradle 파일에서 hermesEnabled 플래그를 통해 활성화됩니다. iOS의 경우, Podfile에서 :hermes_enabled 변수를 통해 구성합니다. 구성을 변경한 후에는 팟스를 재설치하고 프로젝트를 재빌드해야 합니다.
// android/app/build.gradle — Hermes 활성화
project.ext.react = [
enableHermes: true
]
// iOS Podfile — Hermes 활성화
hermes_enabled = true
pod 'hermes-engine'
앱에서 Hermes가 실행 중인지 확인하려면 Android에서 adb logcat | grep Hermes를 실행하거나 iOS에서 Xcode 콘솔을 확인하세요. 앱이 시작되면, Hermes는 “Hermes VM initialized”와 같은 메시지를 출력합니다. JavaScript 코드에서 globalThis.HermesInternal을 확인할 수도 있습니다 — Hermes가 활성화되어 있으면 이 속성을 사용할 수 있습니다.
// Hermes 실행 중인지 확인
if (typeof globalThis.HermesInternal !== 'undefined') {
console.log('✅ Hermes 엔진 활성');
console.log(
'버전:',
globalThis.HermesInternal.getEngineProperties()
);
} else {
console.log('❌ Hermes 비활성 (JSC로 폴백)');
}
자주 묻는 질문
네, Hermes는 새로운 React Native 아키텍처(Fabric + Turbo Modules)와 완전히 호환됩니다. 또한, Hermes와 JSI는 함께 개발되었으며 페어로 최고의 성능을 제공합니다.
네, Hermes는 iOS와 Android 두 플랫폼 모두를 지원합니다. iOS에서는 표준 JavaScriptCore를 Hermes가 대체하여 동일한 이점(빠른 시작과 더 적은 메모리 소비)을 제공합니다.
Hermes는 대부분의 ES6+ 기능을 지원하지만 제한이 있습니다. eval, 생성자, Proxy는 사용할 수 없습니다. 호환성을 위해 일반적인 React Native와 마찬가지로 Babel 트랜스파일을 사용하는 것을 권장합니다.
Hermes는 컴팩트한 바이트코드와 더 작은 엔진 크기를 통해 앱 크기를 줄입니다. 바이트코드는 일반적으로 동등한 JS 코드보다 20–30% 더 컴팩트하며, 엔진 자체는 JSC의 ~2MB에 비해 ~250KB입니다.
대부분의 라이브러리는 Hermes와 호환됩니다. 라이브러리가 eval, Proxy 또는 비표준 기능을 사용하는 경우, Hermes 호환 버전이 있는지 확인하세요. Babel 플러그인을 사용하여 지원되지 않는 구문을 대체할 수도 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.