Skip to main content

빌드 단계 속도 향상

React Native 앱을 빌드하는 과정은 시간이 많이 소요될 수 있으며, 개발자들의 시간을 여러 분 단위로 잡아먹을 수 있다. 프로젝트가 커지고, 여러 React Native 개발자가 함께 작업하는 대규모 조직에서는 이 문제가 더욱 두드러질 수 있다.

이러한 성능 저하를 완화하기 위해, 이 페이지에서는 빌드 시간을 단축할 수 있는 몇 가지 방법을 제안한다.

개발 중에는 하나의 ABI만 빌드하기 (Android 전용)

로컬에서 안드로이드 앱을 빌드할 때 기본적으로 4가지 Application Binary Interface(ABI)를 모두 빌드한다: armeabi-v7a, arm64-v8a, x86, x86_64.

하지만 로컬에서 빌드하고 에뮬레이터나 실제 기기에서 테스트하는 경우 모든 ABI를 빌드할 필요는 없다. 이렇게 하면 네이티브 빌드 시간을 약 75% 단축할 수 있다.

React Native CLI를 사용한다면 run-android 명령어에 --active-arch-only 플래그를 추가할 수 있다. 이 플래그는 실행 중인 에뮬레이터나 연결된 폰에서 올바른 ABI를 자동으로 선택한다. 이 방법이 잘 동작하는지 확인하려면 콘솔에 info Detected architectures arm64-v8a와 같은 메시지가 출력되는지 확인하면 된다.

$ yarn react-native run-android --active-arch-only

[ ... ]
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
Jetifier found 1037 file(s) to forward-jetify. Using 32 workers...
info JS server already running.
info Detected architectures arm64-v8a
info Installing the app...

이 메커니즘은 reactNativeArchitectures Gradle 프로퍼티에 의존한다.

따라서 CLI 없이 커맨드라인에서 직접 Gradle로 빌드한다면 다음과 같이 원하는 ABI를 지정할 수 있다:

$ ./gradlew :app:assembleDebug -PreactNativeArchitectures=x86,x86_64

이 방법은 CI에서 안드로이드 앱을 빌드하고 다른 아키텍처의 빌드를 병렬로 처리할 때 유용하다.

원한다면 프로젝트의 최상위 폴더에 있는 gradle.properties 파일을 사용해 이 값을 로컬에서 오버라이드할 수도 있다:

# 이 프로퍼티를 사용해 빌드할 아키텍처를 지정할 수 있다.
# CLI에서 다음과 같이 오버라이드할 수도 있다.
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64

앱의 릴리스 버전을 빌드할 때는 이 플래그를 제거해야 한다. 일상적인 개발 워크플로에서 사용하는 아키텍처뿐만 아니라 모든 ABI에서 동작하는 apk/app 번들을 빌드해야 하기 때문이다.

컴파일러 캐시 활용

C++나 Objective-C로 빈번하게 네이티브 빌드를 수행한다면, 컴파일러 캐시를 사용하는 것이 도움이 될 수 있다.

컴파일러 캐시는 크게 두 가지 타입으로 나뉜다: 로컬 컴파일러 캐시와 분산형 컴파일러 캐시다.

로컬 캐시 설정

info

아래 지침은 안드로이드와 iOS 모두에 적용된다. 안드로이드 앱만 개발한다면, 바로 진행해도 된다. iOS 앱도 함께 개발한다면, 아래 XCode 특별 설정 섹션의 지침을 따르길 바란다.

네이티브 빌드의 컴파일 시간을 단축하기 위해 ccache 사용을 권장한다.
ccache는 C++ 컴파일러를 감싸고, 컴파일 결과를 저장하며, 이전에 저장된 중간 컴파일 결과가 있으면 컴파일 과정을 건너뛴다.

ccache는 대부분의 운영체제에서 패키지 매니저를 통해 설치할 수 있다. macOS에서는 brew install ccache 명령어로 설치할 수 있다.
또는 공식 설치 가이드를 참고해 소스 코드로부터 직접 설치할 수도 있다.

설치 후 두 번의 클린 빌드를 수행한다. 예를 들어, 안드로이드에서 yarn react-native run-android를 실행한 뒤, android/app/build 폴더를 삭제하고 동일한 명령어를 다시 실행해 보자.
두 번째 빌드가 첫 번째보다 훨씬 빠르게 완료될 것이다(몇 분이 아닌 몇 초 내에 완료됨).
빌드 중에 ccache -s 명령어를 실행해 캐시 적중률을 확인할 수 있다.

$ ccache -s
Summary:
Hits: 196 / 3068 (6.39 %)
Direct: 0 / 3068 (0.00 %)
Preprocessed: 196 / 3068 (6.39 %)
Misses: 2872
Direct: 3068
Preprocessed: 2872
Uncacheable: 1
Primary storage:
Hits: 196 / 6136 (3.19 %)
Misses: 5940
Cache size (GB): 0.60 / 20.00 (3.00 %)

ccache는 모든 빌드에 대한 통계를 누적한다. 빌드 전에 ccache --zero-stats를 실행해 통계를 초기화하면 캐시 적중률을 정확히 확인할 수 있다.
캐시를 완전히 지우려면 ccache --clear 명령어를 사용하면 된다.

XCode 특정 설정

iOS와 XCode에서 ccache가 올바르게 동작하도록 하려면, ios/Podfile에서 React Native의 ccache 지원을 활성화해야 한다.

에디터에서 ios/Podfile을 열고 ccache_enabled 라인의 주석을 해제한다.

ruby
  post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
installer,
config[:reactNativePath],
:mac_catalyst_enabled => false,
# TODO: 아래 라인의 주석을 해제
:ccache_enabled => true
)
end

CI 환경에서 이 방법 사용하기

Ccache는 macOS에서 /Users/$USER/Library/Caches/ccache 폴더를 사용해 캐시를 저장한다. 따라서 CI 환경에서도 해당 폴더를 저장하고 복원해 빌드 속도를 높일 수 있다.

하지만 주의해야 할 몇 가지 사항이 있다:

  1. CI 환경에서는 캐시 오염 문제를 피하기 위해 완전히 깨끗한 빌드를 수행하는 것을 권장한다. 앞서 언급한 방법을 따르면 네이티브 빌드를 4개의 다른 ABI에서 병렬로 실행할 수 있으며, CI 환경에서는 ccache가 필요하지 않을 가능성이 높다.

  2. ccache는 캐시 히트를 계산하기 위해 타임스탬프를 사용한다. 이 방법은 CI 환경에서 잘 작동하지 않는다. 파일이 매번 CI 실행 시 다시 다운로드되기 때문이다. 이 문제를 해결하려면 파일 내용을 해시하는 compiler_check content 옵션을 사용해야 한다.

분산 캐시

로컬 캐시와 마찬가지로, 네이티브 빌드에 분산 캐시를 사용하는 것을 고려할 수 있다. 특히 빈번한 네이티브 빌드를 수행하는 대규모 조직에서 유용할 수 있다.

이를 위해 sccache를 사용할 것을 권장한다. sccache의 분산 컴파일 빠른 시작 가이드를 참고해 이 도구를 설정하고 사용하는 방법을 확인할 수 있다.