๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
React-Native

react-native-performance๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๊ธฐ

by LasBe 2025. 5. 17.
๋ฐ˜์‘ํ˜•

๐Ÿ“’ react-native-performance๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๊ธฐ


์ด๋ฒˆ์— ํšŒ์‚ฌ์—์„œ ์•ฑ ์„ฑ๋Šฅ ๊ฐœ์„  ์—…๋ฌด๋ฅผ ๋งก์•„ ์ •๋ง ์—ด์‹ฌํžˆ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ๊ณผ์ •์—์„œ ์•ฑ ์‹œ์ž‘ ์†๋„๋„ ์ •๋ง ํฌ๊ฒŒ ๊ฐœ์„ ๋˜์—ˆ๋Š”๋ฐ, ์ˆ˜์น˜๋กœ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์œผ๋ฉด ๊ทธ์ € ๊ฐ์ผ๋ฟ์ด๊ฒ ์ฃ ?

 

์ˆ˜์น˜ํ™”๋ฅผ ํ•˜๋ ค๊ณ  ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋“ค์„ ๋งŽ์ด ์ฐพ์•„๋ณด๋‹ค react-native-performance๋ผ๋Š” ํŒจํ‚ค์ง€๋ฅผ ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ฃผ์š”ํ•œ ์ด์œ ๋Š” Cold Start ์‹œ ์•ฑ ๋ฒˆ๋“ค ๋กœ๋“œ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์„ธํŒ…๋ถ€ํ„ฐ ์ดˆ๊ธฐ ๋ฒˆ๋“ค ๋กœ๋“œ ์‹œ๊ฐ„ + ์ดˆ๊ธฐ ํ™”๋ฉด ๋ Œ๋”๋ง ์‹œ๊ฐ„ ํ”„๋กœํŒŒ์ผ๋ง๊นŒ์ง€ ์†Œ๊ฐœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

๐Ÿ“Œ ์„ค์น˜

https://shopify.github.io/react-native-performance/

$ yarn add @shopify/react-native-performance
$ cd ios && pod install

ํŒจํ‚ค์ง€ ์„ค์น˜ ํ›„ pod install๊นŒ์ง€ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ Native ์„ธํŒ…

๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ์— ์•ฑ ์‹œ์ž‘ ์ „ ์ธก์ • ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ดˆ๊ธฐ ๋ฒˆ๋“ค ๋กœ๋“œ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”Ž Android

// MainApplication.java

import com.shopify.reactnativeperformance.ReactNativePerformance; // ์ถ”๊ฐ€
// ...

@Override
public void onCreate() {
  ReactNativePerformance.onAppStarted(); // ์ถ”๊ฐ€
  super.onCreate();
  // ...
}
// MainApplication.kt

import com.shopify.reactnativeperformance.ReactNativePerformance; // ์ถ”๊ฐ€
// ...

public void onCreate() {
  ReactNativePerformance.onAppStarted(); // ์ถ”๊ฐ€
  super.onCreate();
  // ...
}

์•ˆ๋“œ๋กœ์ด๋“œ๋Š” Java or Kotlin์— ๋”ฐ๋ผ MainApplication์— ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ์€ onCreate() ์ „์— onAppStarted() ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด ๋ฒˆ๋“ค ๋กœ๋“œ ์‹œ๊ฐ„ ์ธก์ •์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค.

๐Ÿ”Ž IOS

// AppDelegate.m

#import <ReactNativePerformance/ReactNativePerformance.h> // ์ถ”๊ฐ€
// ...

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [ReactNativePerformance onAppStarted]; // ์ถ”๊ฐ€
  // ...
}
// AppDelegate.swift

import ReactNativePerformance // ์ถ”๊ฐ€
//...

@main
class AppDelegate: RCTAppDelegate {
  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    ReactNativePerformance.onAppStarted() // ์ถ”๊ฐ€
    // ...
}

IOS๋„ Objective-C or Swift์— ๋”ฐ๋ผ AppDelegate์— ์•ฑ ์‹œ์ž‘ ์ธก์ • ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

๐Ÿ“Œ ์•ฑ ์‹œ์ž‘ ์†๋„ ์ธก์ •

// App.tsx

import React, { useCallback } from 'react';
import { RenderPassReport, PerformanceProfiler, PerformanceMeasureView } from '@shopify/react-native-performance';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import Home from './src/pages/Home';

const RootStack = createStackNavigator();

const PerformanceHomeScreen = () => {
  return (
    <PerformanceMeasureView screenName="Home" interactive={true}>
      <Home />
    </PerformanceMeasureView>
  );
};

function App() {
  const onReportPrepared = useCallback((report: RenderPassReport) => {
    console.log({ report });
  }, []);

  return (
    <PerformanceProfiler onReportPrepared={onReportPrepared}>
      <NavigationContainer>
        <RootStack.Navigator initialRouteName="Home">
          <RootStack.Screen name="Home" component={PerformanceHomeScreen} />
        </RootStack.Navigator>
      </NavigationContainer>
    </PerformanceProfiler>
  );
}

export default App;
  • App.tsx์—์„œ <PerformanceProfiler />๋ฅผ ์„ ์–ธํ•จ์œผ๋กœ์จ ํ”„๋กœํŒŒ์ผ๋ง์„ ์ดˆ๊ธฐํ™”ํ•ด ์ฃผ๊ณ , ์ธก์ • ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • <PerformanceMeasureView />๋ฅผ ์ตœ์ดˆ ๋ Œ๋” ํŽ˜์ด์ง€ Navigator Screen์— ์ ์šฉํ•˜๋ฉฐ ํ™”๋ฉด์ด ๋ Œ๋” ๋  ๋•Œ ์ธก์ •์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž ๋ฐ์ดํ„ฐ ํ™•์ธ

{
    "reportId": "22B199F0-637F-4122-9FE1-F95A13170950",
    "resourceAcquisitionStatus": {
        "totalTimeMillis": 0,
        "components": {}
    },
    "flowInstanceId": "B02EB9F0-37F2-4EBC-94E6-5B97269E1A5E",
    "destinationScreen": "Home",
    "flowStartTimeSinceEpochMillis": 1747417857111,
    "timeToBootJsMillis": 1625.4560546875,
    "renderPassName": "interactive",
    "timeToRenderMillis": 210.14794921875,
    "interactive": true
}

PerformanceProfiler์˜ onReportPrepared์„ ํ†ตํ•ด ๋ฐ›์€ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

๊ฐ ๋ฐ์ดํ„ฐ์˜ ์˜๋ฏธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • destinationScreen
    • ํ”„๋กœํŒŒ์ผ๋ง ๋œ ๋„์ฐฉ ์Šคํฌ๋ฆฐ์ž…๋‹ˆ๋‹ค.
  • flowInstanceId
    • ํ•˜๋‚˜์˜ flow ์‹คํ–‰์—์„œ ๋ฐœ์ƒํ•œ ๋ชจ๋“  ๋ Œ๋”๋ง ๋ฆฌํฌํŠธ๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๊ณ ์œ ํ•œ UUID์ž…๋‹ˆ๋‹ค.
  • flowStartTimeSinceEpochMillis
    • flow๊ฐ€ ์‹œ์ž‘๋œ ์‹œ์ ์˜ ํƒ€์ž„์Šคํƒฌํ”„์ž…๋‹ˆ๋‹ค.
  • interactive
    • ํ•ด๋‹น ๋ Œ๋” ํŒจ์Šค๊ฐ€ ํ™”๋ฉด์„ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์ƒํƒœ๋กœ ๋งŒ๋“ค์—ˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. 
      PerformanceMeasureView์˜ interactive prop์— ์˜ํ•ด ์ œ์–ด๋ฉ๋‹ˆ๋‹ค.
  • renderPassName
    • ์™„๋ฃŒ๋œ ๋ Œ๋” ํŒจ์Šค์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ์ค‘๋‹จ๋œ ๋ Œ๋” ํŒจ์Šค์˜ ๊ฒฝ์šฐ์—๋Š” ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • reportId
    • ๊ฐ ๋ Œ๋”๋ง์— ๋Œ€ํ•ด ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„๋˜๋Š” UUID์ž…๋‹ˆ๋‹ค. 
  • sourceScreen
    • ํ”„๋กœํŒŒ์ผ๋ง๋œ flow์˜ ์‹œ์ž‘ ์Šคํฌ๋ฆฐ์ž…๋‹ˆ๋‹ค.
  • timeToBootJsMillis
    • JS ์ฝ”๋“œ๊ฐ€ ๋ถ€ํŒ…๋˜๊ธฐ๊นŒ์ง€ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์•ฑ ์‹œ์ž‘ ๋ Œ๋”๋ง ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • timeToRenderMillis
    • ๋ Œ๋” ํŒจ์Šค๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ๊นŒ์ง€ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ค‘๋‹จ๋œ ๋ Œ๋” ํŒจ์Šค์˜ ๊ฒฝ์šฐ์—๋Š” ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • timeToAbortMillis
    • ๋ Œ๋” ํŒจ์Šค๊ฐ€ ์ค‘๋‹จ๋˜๊ธฐ๊นŒ์ง€ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ค‘๋‹จ๋œ ๋ Œ๋” ํŒจ์Šค์˜ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด timeToBootJsMillis์™€ timeToRenderMillis์ž…๋‹ˆ๋‹ค.

์ด ๋‘˜์„ ์ด์šฉํ•œ๋‹ค๋ฉด "์•ฑ ์‹œ์ž‘ ํ›„ ์ฒซ ์ฝ˜ํ…์ธ  ๋ Œ๋”๋ง ์‹œ์ž‘ ์‹œ๊ฐ„"์ธ "First Contentful Paint(FCP)"๋ฅผ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

FCP = ๋ฒˆ๋“ค ๋กœ๋“œ ์‹œ๊ฐ„(timeToBootJsMillis) + ์ปจํ…์ธ  ๋ Œ๋” ์‹œ๊ฐ„(timeToRenderMillis)

 

๊ทธ๋Ÿผ ์œ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ดค์„ ๋•Œ timeToBootJsMillis๊ฐ€ 1625ms, timeToRenderMillis๊ฐ€ 210ms์ด๋‹ˆ๊นŒ ์•ฑ ์‹œ์ž‘ ์†๋„๋Š” 1.8์ดˆ ์ •๋„ ๋˜๊ฒ ์ฃ ?

 

์ €๋Š” Navigator ๋‹จ์— PerformanceMeasureView๋ฅผ ์ ์šฉํ•˜์˜€์ง€๋งŒ, ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์ง์ ‘ ์ ์šฉํ•œ ๋’ค interactive props์„ ์ด์šฉํ•œ๋‹ค๋ฉด ์ฒซ ์ƒํ˜ธ์ž‘์šฉ ์‹œ๊ฐ„ ์ง€ํ‘œ์ธ Time To Interactive(TTI)๋ฅผ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

react-native-performance ํŒจํ‚ค์ง€๊ฐ€ ์ปค๋ฎค๋‹ˆํ‹ฐ๋„ ํ˜•์„ฑ๋˜์–ด ์žˆ์ง€ ์•Š์•„ ํ™œ์šฉํ•˜๊ธฐ์— ๊นŠ๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ์—” ์–ด๋ ต์ง€๋งŒ ์•ฑ ์‹œ์ž‘ ์†๋„๋ฅผ ์ธก์ •ํ•˜๊ธฐ์—” ๊ฐ„๋‹จํ•˜๊ณ  ์‰ฌ์šด ๊ฒƒ ๊ฐ™์•„ ๋งŒ์กฑํ•ฉ๋‹ˆ๋‹ค.

 

์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ๋‹ด์€ ์˜ˆ์ œ๋Š” ๋‹ค์Œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

GitHub - devlasbe/rn-lazy-loading: React Native์—์„œ Lazy Loading์„ ํ†ตํ•ด ์•ฑ ์‹œ์ž‘ ์†๋„๋ฅผ ์ธก์ •

React Native์—์„œ Lazy Loading์„ ํ†ตํ•ด ์•ฑ ์‹œ์ž‘ ์†๋„๋ฅผ ์ธก์ •. Contribute to devlasbe/rn-lazy-loading development by creating an account on GitHub.

github.com

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€


์˜คํ”ˆ ์ฑ„ํŒ