Flutter 개발 상자

[Flutter] @pragma('vm:entry-point') 이란 무엇을 뜻하는가? 본문

Flutter

[Flutter] @pragma('vm:entry-point') 이란 무엇을 뜻하는가?

망고상자 2024. 2. 8. 11:25
728x90

FCM에서 쓰이는 최상위 함수

 

우리는 평소에 저 pragma 어노테이션을 쓸일이 별로 없습니다.

하지만 앱을 개발하면 거의 반필수로 들어가는 FCM 구현 예제를 복붙하다보면 쌩판 처음보는 예제 코드를 볼 수 있습니다.

 

// Be sure to annotate the handler with `@pragma('vm:entry-point')` above the function declaration.
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  await setupFlutterNotifications();
  showFlutterNotification(message);
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  print('Handling a background message ${message.messageId}');
}

 

위 코드는 FCM 패키지의 공식 예제 코드의 일부입니다.

 

위 함수는 최상위 함수로 쓰이며 주석을 살펴보면 반드시 @pragma('vm:entry-point)를 달아주라고 써있습니다.

혹시라도 호기심이 많은 사람은 저걸 지우고 테스트를 해볼 수도 있을 것 같습니다.

반드시 쓰라고는 되어있지만 어째 뭔가 필수인가? 싶은 중요하지 않은 느낌도 들거든요.

 

근데 실제로 저걸 지우고 테스트 했을때의 결과물은 좀 놀랍습니다.

실제로 동작에 문제가 없거든요!

 

 

초심으로 돌아가서... JIT vs AOT

 

우리가 처음 플러터를 배울때로 다시 돌아가봅니다.

Flutter의 장점으로는 핫리로드가 있고! 매우 좋은 퍼포먼스가 있고! 이걸 가능하게 해주는것이 바로

Dart의 JIT & AOT 2가지 컴파일 방식에 있다!

 

대충 무슨소리인지도 모르는데 꼭 플러터 처음 배우면 한번은 듣고 가는 내용이였죠.

 

간단하게 ChatGPT의 설명으로 빠르게 복습하겠습니다.

  1. JIT (Just-In-Time) 컴파일:
    • Dart 언어의 기본 실행 방식이며, 대부분의 경우에는 디버깅, 개발, 테스트 목적으로 사용됩니다.
    • 코드는 실행 시점에 기계어로 컴파일됩니다.
    • JIT 컴파일은 런타임 성능 최적화를 위해 동적으로 코드를 분석하고 최적화합니다.
    • 개발자가 코드를 수정하거나 디버깅할 때 빠른 반복 및 개발 시간 단축을 제공합니다.
  2. AOT (Ahead-of-Time) 컴파일:
    • Dart 코드를 미리 기계어로 컴파일하여 실행 파일을 생성하는 방식입니다.
    • 애플리케이션 시작 시점에 기계어 코드가 바로 실행됩니다.
    • AOT 컴파일은 실행 파일 크기를 줄이고, 시작 속도를 향상시킵니다.
    • 모바일 애플리케이션 및 웹 애플리케이션과 같이 크기와 성능이 중요한 환경에서 주로 사용됩니다.

여기서 중요한건

 

AOT의 3번째 항목, 실행 파일의 크기를 줄이고 시작 속도를 향상시킨다 <<< 입니다.

실행파일 크기를 줄이기 위해 불필요한 코드를 날리는 과정을 Tree Shaking 이라고 하는데

 

여러분들의

_firebaseMessagingBackgroundHandler

 

함수를 살펴보시면 저 함수를 호출하고 있는 부분이 전혀 없는것을 확인할 수 있습니다.

 

저 함수는 네이티브 영역에서 호출되기때문에 플러터 코드에서 호출되는 부분이 없더라도 반드시 있어야 하는 함수입니다.

하지만 Dart 컴파일러는 이를 인식하지 못하고 호출이 되지 않는 불필요한 함수라고 생각하고

트리 쉐이킹 과정에서 해당 함수를 날려버리게 되는겁니다.

 

이 트리 쉐이킹 과정에서 함수가 날아가는것을 방지해주는것이 바로

@pragma('vm:entry-point) 인겁니다.

 

이 부분은 공식 저장소의 위키 문서에 나와있습니다.

https://github.com/flutter/flutter/wiki/Experimental:-Launch-Flutter-with-non-main-entrypoint#avoid-tree-shaking-in-release

 

 

자 그렇다면 아까 프라그마를 지웠는데도 실제 동작에 문제가 없던 이유는 무엇일까요?

바로 디버그 모드로 실행하여 JIT컴파일을 했기 때문입니다. 여기서는 트리 쉐이킹 과정이 없으니까 딱히 문제가 없었던거죠.

 

 

결론

 

자 그래서 한마디로 정의하면 무엇인가? 언제언제 써야하는가? 에 대한 정리를 하자면

@pragma(vm:entry-point) 는 네이티브에서 해당 함수를 직접 할당, 호출할 수 있다고 Dart컴파일러에 명시적으로 알려주는 코드입니다.

일반적으로는 쓸일이 없지만 네이티브 연동시 FCM의 예제처럼 필요에 따라서 사용할 수 있겠습니다.

 

마지막으로는 이에 대한 공식문서입니다.

이부분을 읽는게 이해가 더 빠를수도 있겠습니다.

https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md

728x90