View
[Flutter] Dart는 Native Machine Code로 컴파일 되지만, Dart VM은 여전히 사용된다?
sm_amoled 2024. 7. 12. 16:58이번에 Flutter Engine에 대해 계속 내용들을 공부하고 있는데, 자꾸 헷갈리는 내용이 있어서 계속 찾아보다가 그냥 글로 정리했다.
ㅤ
바로 Dart는 Native Machine Code로 컴파일 되지만, 실행 시점에 Dart VM은 여전히 사용된다는 것이다.
내가 배웠던 바에 따르면 Flutter의 장점이 Native Machine Code로 변환되어 실행이 되기 때문에, VM을 사용하는 리액트 같은 JS 언어보다 성능적으로 우수하다는 것이였는데, 그 배움이 부정당하는 듯한 문장이였다.
ㅤ
Dart 가 VM 을 사용한다고…?
ㅤ
그래서 GPT 한테도 열심히 물어봤다. 근데, 더 헷갈리는 답변인, “Native와 VM이 결합되어 사용됩니다” 라는 두루뭉술한 답변을 남겼다. 아니 이게 무슨말이냐
GPT왈 : “Flutter 엔진 위에서 네이티브 머신 코드가 돌아간다는 의미는, Flutter 애플리케이션이 네이티브 머신 코드로 컴파일되어 실행되지만, 이 코드가 Flutter Engine을 통해 실제로 동작한다는 것입니다. Flutter Engine은 Dart 런타임을 포함하고 있어, 컴파일된 코드가 실행될 수 있는 환경을 제공합니다. 또한, 그래픽 렌더링, 입력 이벤트 처리, 네이티브 기능 호출 등을 관리하여 애플리케이션이 원활하게 동작할 수 있도록 지원합니다.”
ㅤ
그러니깐, Dart 코드가 환경에 적합한 Native Machine Code로 컴파일이 되기는 하는데, 그게 VM 위에서 돌아간다는 소리인가?? 그게 무슨 말도안되는 비효율이지?? 라는 생각에 플러터 Docs를 열심히 서치했지만 답은 찾지 못했다. 그러나 스택오버플로우에서 해답을 찾을 수 있었다. ☺️
ㅤ
https://stackoverflow.com/questions/46961097/is-the-dart-vm-still-used
결론적으로는 Dart VM 을 사용하는게 맞으나, 전통적인 java 방식처럼 VM 위에서 bytecode가 돌아가는 방식은 아니였다. (휴~~) 플러터가 JIT 방식(인터프리터처럼 필요할 때 그때그때 컴파일 하는 방식)과 AOT 방식 (처음에 모두 컴파일 해놓고 실행하는 방식)을 모두 사용하는데, 사용중인 방식에 따라서 Dart VM의 역할이 달라진다고 봐야한다.
ㅤ
1) JIT 를 사용할 때
디버그 모드에서는 코드를 저장하면 바로 에뮬레이터에 변경사항이 반용되는 핫리로드 기능을 사용할 수 있는데, 이 방식이 JIT 방식으로 코드를 컴파일한 상황이다. 이때 바로바로 코드가 실행되기 위해서는 에뮬레이터에 실행중인 Dart Virtual Machine으로 중간 코드를 전달하고, 이를 Flutter Engine이 실행하는 방식으로 앱을 가동한다.
ㅤ
즉, 디버그 모드로 개발을 할 때는 Dart VM 이 전통적인 Virtual Machine으로서의 역할을 담당한다.
ㅤ
2) AOT를 사용할 때
릴리즈 모드에서는 코드를 처음에 모두 컴파일을 해두고, 이를 앱으로 묶어 실행하게 된다. 몇 가지 이유가 있는데, 빌드 파일의 용량을 줄일 수도 있고 좀 더 타이트하게 최적화를 적용할 수도 있지만, 나는 iOS에서 동적 컴파일(dynamic compilation)을 지원하지 않기 때문에 이렇게 처리할 수 밖에 없었다는 생각도 든다.
ㅤ
AOT를 적용할 때는 실행될 환경을 특정하여 빌드와 컴파일을 진행하기 때문에, iOS 환경에서 실행할 앱을 빌드하는 경우에는 iOS 의 Native Machine Code로 컴파일을 진행하게 된다. 그리고 앱을 실행하면 이 Machine Code 로 앱을 실행하게 된다. 여기에서는 Virtual Machine위에서 실행되지 않는다! 다만, Machine Code를 실행하더라도 플러터 앱 실행 환경에서 필요한 요소들(예를 들면 가비지콜렉터 등)과 플러터 라이브러리(dart:* 에 들어있는 함수 등)나 함수 실행에 필요한 런타임 타입 정보, 동적 함수 실행(다형성) 등의 지원을 위해 Dart VM이 사용된다고 한다. 여기에서는 정확히는 중간 언어를 실행하는 Virtual Machine으로서 역할을 수행하는 것이 아니라, 메모리에 로드된 플러터 라이브러리로서의 기능을 수행한다고 보면 된다.
ㅤ
정리해보자면
- 디버그 모드에서는 JIT 컴파일 방식으로 앱이 실행되면서, 에뮬레이터 속 앱이 Dart VM 위에서 실행된다.
- 릴리즈 모드에서는 AOT 컴파일 방식으로 앱이 실행되면서 Machine Code 수준으로 앱이 실행되며, Dart VM은 동적인 라이브러리이자 유틸리티 툴로서의 역할을 수행한다.