[Dart] Dart의 컴파일과 실행 모델 정리
Dart는 “한 번 작성한 코드”를 여러 환경에서 실행할 수 있도록 설계된 언어이다.
이를 가능하게 하는 핵심은 컴파일 방식이 하나로 고정되어 있지 않다는 점이다.
개발 단계에서는 빠른 피드백을 위해 JIT(Just-In-Time) 컴파일을 활용하고, 배포 단계에서는 성능과 안정성을 위해 AOT(Ahead-Of-Time) 컴파일을 활용한다.
또한 웹 환경에서는 Dart 코드를 JavaScript로 변환하여 브라우저에서 실행한다.
이 글에서는 Dart가 어떤 방식으로 컴파일되고 실행되는지, 그리고 왜 이런 구조가 필요한지를 정리한다.
1. Dart의 실행 모델 개요
Dart의 실행 모델은 “어디에서 실행하느냐”와 “어떤 단계(개발/배포)냐”에 따라 달라진다.
크게 보면 다음 세 축으로 이해할 수 있다.
- Dart VM 기반 실행: 개발 단계에서 주로 사용(JIT), 때로는 네이티브 실행(AOT의 결과물)로 이어짐
- 네이티브(AOT) 실행: 배포용 바이너리로 컴파일해 빠르고 예측 가능한 실행 제공
- 웹(JavaScript) 실행: 브라우저에서 돌아가도록 JavaScript로 변환해 실행
즉 Dart는 단일한 “컴파일 → 실행” 파이프라인만 갖는 언어가 아니라, 목적과 플랫폼에 따라 컴파일러/런타임을 다르게 선택하는 구조를 가진다.
2. JIT(Just-In-Time) 컴파일과 개발 경험
JIT는 프로그램을 실행하는 시점에 필요한 부분을 즉석에서 기계어로 변환하는 방식이다.
Dart에서는 주로 개발 단계에서 JIT가 활용된다.
개발 모드에서 JIT를 쓰는 이유
개발 중에는 코드 수정이 매우 잦다. 매번 전체를 빌드해서 실행 파일을 만드는 방식은 피드백 속도가 느려진다.
JIT는 다음과 같은 장점 때문에 개발 경험을 크게 개선한다.
- 빠른 반복(수정 → 실행)
- 디버깅 친화적
- Hot Reload / Hot Restart 같은 생산성 기능과 궁합이 좋음
특히 Flutter 개발에서 “화면을 수정하고 바로 결과를 확인”하는 흐름은 JIT 실행과 강하게 결합되어 있다.
JIT에서의 최적화 관점
JIT는 “실행 중에 어떤 코드가 자주 호출되는지”를 관찰할 수 있다.
따라서 런타임에서 얻은 정보를 바탕으로 최적화를 적용할 수 있다는 장점이 있다.
다만 이 방식은 실행 시점에 컴파일 비용이 들어가며, 최적화가 진행되는 과정 자체가 런타임에 영향을 줄 수 있다.
3. AOT(Ahead-Of-Time) 컴파일과 배포 성능
AOT는 실행 전에 미리 기계어로 컴파일해 두는 방식이다. Dart에서는 주로 배포(릴리즈) 단계에서 AOT가 사용된다.
AOT가 필요한 이유
배포 환경에서는 실행 성능과 시작 속도, 일관성이 중요해진다.
AOT는 다음과 같은 장점을 제공한다.
- 빠른 앱 시작(Startup Time 개선)
- 일관된 성능(런타임 컴파일 부담 감소)
- 배포 환경에서의 예측 가능성 증가
모바일 앱의 경우 사용자는 실행 직후 반응 속도에 민감하며, 런타임에서 추가 컴파일 비용이 발생하는 구조는 불리할 수 있다. 그래서 릴리즈 빌드에서는 AOT 기반 네이티브 바이너리 형태로 제공되는 것이 일반적이다.
AOT의 트레이드오프
AOT는 배포 품질을 높이는 대신, 개발 단계에서 기대하는 “즉각적인 코드 반영” 흐름에는 적합하지 않다.
또한 플랫폼별로 빌드 산출물이 달라질 수 있어(예: Android/iOS), 빌드 파이프라인이 상대적으로 무거워지는 경향이 있다.
4. Dart Web: JavaScript로의 컴파일과 브라우저 실행
브라우저는 일반적으로 JavaScript 실행 환경을 제공한다.
따라서 Dart 코드를 웹에서 실행하려면 Dart 자체를 브라우저가 이해할 수 있는 형태로 변환해야 한다.
이때 선택지는 크게 두 가지로 이해하면 된다.
- Dart → JavaScript 변환: 가장 널리 쓰이는 방식
- (추가적으로) WebAssembly 기반 접근: 점점 확대되는 흐름이지만, 실제 사용은 도구/환경 성숙도에 영향을 받음
웹에서는 결국 “브라우저가 실행 가능한 결과물”이 필요하고, 현재까지 가장 현실적인 경로는 Dart를 JavaScript로 컴파일하는 것이다.
웹 환경에서 달라지는 점
웹으로 넘어오면 실행 모델이 “Dart VM” 중심이 아니라 “JS 런타임(브라우저 엔진)” 중심으로 바뀐다. 그래서 같은 Dart 코드라도 다음 요소들이 체감 차이를 만든다.
- 번들 크기(전송 비용)
- 초기 로딩/파싱 시간
- 브라우저 엔진 최적화에 따른 실행 성능
- 디버깅 경험(소스맵 등)
결과적으로 웹에서는 Dart의 장점(생산성, 타입 시스템)을 유지하면서도, 최종 실행은 브라우저의 런타임 특성에 맞춰 최적화하는 전략이 중요해진다.
5. 컴파일/실행 모델을 실무 관점에서 정리하기
Dart의 컴파일/실행 모델은 “상황에 맞게 최적의 선택을 한다”는 철학으로 묶을 수 있다.
개발 단계
- 빠른 수정 반영과 디버깅이 우선
- JIT 기반 실행이 생산성을 높임
- Hot Reload 같은 기능이 개발 속도를 끌어올림
배포 단계
- 시작 속도, 성능, 안정성이 우선
- AOT 기반 네이티브 바이너리가 유리
- 환경에 따른 실행 품질이 비교적 예측 가능해짐
웹 배포
- 브라우저 실행을 위해 JavaScript(또는 상황에 따라 다른 타깃)로 변환
- 네이티브와 다른 최적화 포인트(번들/로딩/런타임 특성)를 고려해야 함
마무리
Dart는 컴파일과 실행 모델이 하나로 고정된 언어가 아니다.
개발 단계에서는 JIT로 빠른 피드백을 제공하고, 배포 단계에서는 AOT로 성능과 안정성을 확보한다.
웹에서는 브라우저 환경에 맞추어 JavaScript로 변환하여 실행한다.
이러한 구조는 “생산성과 배포 품질을 동시에 잡는다”는 목표를 달성하기 위한 선택이며, Dart(특히 Flutter)를 이해할 때 가장 먼저 잡아야 할 핵심 개념이기도 하다.