와탭랩스 블로그 오픈 이벤트 😃
자세히 보기
테크
2025-01-22
React Native 성능 개선: JSI와 새로운 아키텍처가 가져온 변화

React Native는 크로스 플랫폼 앱 개발의 선두 주자였지만, 기존의 브릿지 기반 설계는 성능과 확장성에서 한계를 보였습니다. 이러한 문제를 해결하기 위해 React Native는 JSI(JavaScript Interface)를중심으로 새로운 아키텍처를 도입했습니다.

TurboModules, Fabric, Hermes와 같은 기술이 JSI와 결합하여 실제로 어떤 성능적인 변화를 가져왔는지, 그리고 이를 실무에서 어떻게 활용할 수 있는지 살펴보겠습니다.

Bridge와 JSI

기존 React Native는 Javascript에서 Native을 호출하기 위해 Bridge를 사용했습니다. Bridge는 Javascript와 Native라는 다른 환경을 이어주는 역할을 하였는데요. 이렇게 다른 환경을 연결하기에 Bridge는 느린 이슈가 있었습니다

Bridge를 사용하면 왜 느려졌으며, JSI를 사용하면 빨라진 이유가 무엇을까요?

  • 데이터 직렬화
  • Javascript에서 처리한 데이터를 브릿지를 통해 네이티브 코드로 전달하려면 데이터를 직렬화 해야 합니다. 또한 반대로 네이티브에서 Javascript로 전달할 때, 데이터를 다시 원래 형태로 역직렬화를 해야 합니다. 이때 이러한 작업은 CPU를 많이 사용하게 되기 때문에 빈번하게 bridge를 사용하는 경우 성능 저하가 발생하게 되죠.
  • 스레드 컨텍스트 전환
  • Bridge는 Javascript 스레드와 네이티브 스레드 간의 데이터를 교환하는데, 이때 스레드간 컨텍스트 전환(Context Switching)이 발생하게 되어 성능 저하가 발생합니다.
  • Bridge Queue 대기열
  • Bridge를 사용하면 호출 요청이 Queue에 쌓이게 되는데요. 대기열이 길어지면 요청이 순차적으로 진행돼야 해서 응답시간이 느리게 될 수 있습니다. Bridge를 많이 사용하는 큰 애플리케이션의 경우는 이러한 문제가 많이 발생하게 되죠.

이러한 문제는 Javascript와 네이티브라는 다른 환경을 연결하기 위해 발생하던 문제입니다. JSI는 다이렉트로 Javascript와 Native가 연결되기 때문에 위와 같은 문제가 모두 사라지게 되는 원리입니다. 이러한 Bridge와 JSI의 원리를 이해하고 React Native의 변화를 살펴보죠.

TurboModules: 네이티브 모듈 호출 최적화

기존 React Native에서는 네이티브 모듈이 앱 초기화 단계에서 한꺼번에 로드되었습니다.

  • 문제점:
    • 사용하지 않는 모듈까지 메모리에 로드되어 메모리 낭비가 발생
    • 초기화 시간이 길어져 앱 시작 속도가 느려짐
    • 대규모 앱에서 네이티브 모듈이 많을수록 초기화의 비효율성이 심각해짐

TurboModules는 React Native에서 네이티브 모듈의 성능을 개선하기 위해 도입된 새로운 아키텍처입니다. 기존의 네이티브 모듈 방식은 JavaScript와 네이티브 코드 간의 호출에 Bridge를 사용하여 데이터를 전달하고, 이를 직렬화/역직렬화하는 과정에서 성능이 저하되는 문제가 있었는데 TurboModules은 이를 해결하고 성능을 개선합니다.

  1. Lazy Loading
  2. TurboModules는 네이티브 모듈을 필요한 시점에만 동적으로 로드합니다. 이를 통해 초기화 속도와 메모리 사용량이 크게 개선됩니다.
  1. 모듈 호출 최적화
  2. 기존의 네이티브 모듈은 JavaScript와 네이티브 간의 호출을 할 때 Bridge를 통해 데이터를 전달해야 했기 때문에 직렬화/역직렬화와 같은 과정에서 성능 저하가 있었습니다. TurboModules는 이를 개선하여 즉시 호출 가능한 네이티브 모듈을 제공하고, 더 효율적인 통신 방식을 사용합니다.

TurboModule을 사용하려면, 먼저 네이티브 모듈을 새로 구현해야 합니다. 이 모듈은 TurboModule 아키텍처를 사용하여 JavaScript와 네이티브 코드 간의 인터페이스를 정의합니다.

package com.example;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.TurboModule;
import com.facebook.react.bridge.Callback;

public class MyTurboModule implements TurboModule {

    private final ReactApplicationContext reactContext;

    public MyTurboModule(ReactApplicationContext reactContext) {
        this.reactContext = reactContext;
    }

    @ReactMethod
    public void myFunction(String message, Callback successCallback) {
        successCallback.invoke("Received: " + message);
    }
}

Javascript에서는 다음과 같이 사용할 수 있게 됩니다.

import { NativeModules } from 'react-native';

const { MyTurboModule } = NativeModules;

MyTurboModule.myFunction('Hello from JS', (response) => {
  console.log(response); // "Received: Hello from JS"
});

Fabric: 새로운 UI 렌더링 엔진

기존 React Native의 UI 렌더링 엔진은 Yoga였습니다. Yoga는 Facebook에서 개발한 레이아웃 엔진으로, React Native에서 UI의 레이아웃을 계산하는 데 사용되었습니다. Yoga는 크로스 플랫폼에서 동일한 레이아웃을 제공하기 위해 설계되었고, 주로 JavaScript에서 계산한 레이아웃 데이터를 네이티브 코드로 전달하는 방식으로 동작했습니다.

하지만, 기존 React Native의 UI 렌더링 엔진에서는 다음과 같은 문제가 있었습니다.

  • 레이아웃 계산의 비효율성: JavaScript에서 레이아웃 계산을 하고, 그 결과를 네이티브 코드로 전달하는 과정에서 성능 저하가 발생할 수 있었습니다.
  • Bridge 성능: Yoga를 사용하는 기존의 방식에서는 JavaScript와 네이티브 간의 데이터를 전달하기 위해 Bridge를 사용해야 했고, 이로 인해 직렬화/역직렬화나 스레드 간 컨텍스트 전환 등이 성능 저하를 초래했습니다.

Fabric은 이러한 문제를 해결하기 위해 새롭게 도입된 UI 엔진으로, JSI(JavaScript Interface)를 활용하여 JavaScript와 네이티브 코드 간의 UI 상태를 실시간으로 동기화하고, 레이아웃 계산을 더 효율적으로 처리할 수 있게 만들었습니다.

Fabric의 개선점

  1. JSI와 통합된 렌더링
  2. Fabric은 JSI를 활용하여 JavaScript와 네이티브 간 UI 상태를 실시간으로 동기화합니다. 이를 통해 기존의 Bridge 방식에서 발생했던 데이터 직렬화/역직렬화 문제와 성능 저하를 해결할 수 있습니다.
  1. React Concurrent Mode 지원
  2. Fabric은 React의 최신 Concurrent Mode를 지원합니다. 이를 통해 UI 업데이트가 더욱 부드럽고 효율적으로 이루어지며, 애니메이션이나 UI 반응 속도에서 끊김 현상이 사라집니다.

Fabric 사용 예시

리스트 렌더링

Fabric은 대규모 리스트를 렌더링할 때도 부드러운 사용자 경험을 제공합니다. 예를 들어, 수천 개의 항목을 포함한 스크롤 리스트에서도 스크롤이 부드럽게 유지됩니다.

  • 기존 방식:

<FlatList
  data={largeDataset}
  renderItem={({ item }) => <ListItem item={item} />}
/>

Fabric을 사용하면 위 코드는 내부적으로 더 최적화되어 작동하며, 스크롤 지연 없이 더 많은 데이터를 처리할 수 있습니다.

Hermes: 모바일에 최적화된 JavaScript 엔진

Hermes의 등장 배경

기존 React Native는 JSC(JavaScriptCore)나 V8 같은 일반적인 JavaScript 엔진을 사용했지만, 모바일 환경에 최적화되지 않은 부분이 많았습니다.

  • 앱 실행 시 JavaScript 파일을 해석하는 데 시간이 걸려 앱 시작 속도가 느려짐
  • 메모리 사용량이 높아 저사양 기기에서 성능 문제가 발생

이를 해결하기 위해 Hermes는 기준문제를 다음과 같이 해결하려고 했습니다.

  • JavaScript 코드를 사전에 바이트코드로 컴파일하여 앱 초기화 속도를 단축합니다.
  • 모바일 환경에 최적화된 가비지 컬렉션과 메모리 관리 방식으로 불필요한 메모리 소비를 줄입니다.

Hermes 사용 예시

1. 빠른 앱 시작

Hermes는 앱 초기화 속도를 최적화하여 사용자가 앱을 더 빨리 사용할 수 있게 합니다.

  • 예시: 기존 JSC를 사용할 때 초기화 시간이 3초 걸리던 앱이 Hermes로 실행되면서 1초로 단축

2. 저사양 기기 지원

낮은 메모리 사용량 덕분에 저사양 Android 기기에서도 원활히 실행됩니다. 예를 들어, 동남아 시장을 타겟으로 하는 앱에서 Hermes를 사용하면 시장 점유율을 높이는 데 기여할 수 있습니다.

React Native 최신 아키텍처의 효과

React Native의 새로운 아키텍처는 JSI와 TurboModules, Fabric, Hermes가 유기적으로 결합하여 다음과 같은 효과를 제공합니다:

  • 더 빠른 앱 초기화 : Lazy Loading과 Hermes를 통해 초기화 시간을 대폭 단축
  • 효율적인 자원 사용 : 필요할 때만 모듈을 로드하고, 메모리를 최적화하여 리소스 낭비를 방지
  • 부드러운 사용자 경험 : Fabric과 Concurrent Mode 덕분에 대규모 리스트나 애니메이션에서도 끊김 없는 UI 제공
  • 유연한 데이터 처리 : JSI로 데이터 직렬화/역직렬화 과정을 제거하여 대규모 데이터 처리에도 유리

React Native는 이제 대규모 애플리케이션에서도 성능 한계를 극복할 수 있는 플랫폼으로 진화했습니다. React Native 뿐만 아니라 다른 하이브리드 프레임워크들도 Bridge를 사용하고 있지만 점차 다른 방법들을 적용해 가고 있는데요. 하이브리드 프레임워크로 만든 앱이 Native만큼의 성능을 뽑아내기 위해 노력하고 있으며, 이번 React Native의 변화도 성능적인 측면에서 주목해야 합니다.

와탭 모니터링을 무료로 체험해보세요!