Dart 3.5 발표, 그리고 Dart 로드맵 업데이트
Dart 3.5 발표, 그리고 Dart 로드맵 업데이트 (Announcing Dart 3.5, and an update on the Dart roadmap)
분기마다 이어지는 Dart SDK 릴리스의 차례가 또 돌아왔어요. 이번에는 상호운용(interoperability) 개선, pub.dev 패키지 매니저의 새로운 기능, 그리고 안정 버전이자 1.0으로 승격된 새로운 웹 통합 API가 담겨 있어요.
우리는 시간의 상당 부분을 여러 분기에 걸친 큰 작업에 쏟아왔어요. 그래서 이번에는 Dart 로드맵 업데이트도 준비했는데, 앞으로 올 분기에 무엇을 진전시키고 싶은지에 대한 내용이 담겨 있어요.
본문
Dart 3.5의 새로운 기능
Dart 3.5에는 아래에서 설명하는 여러 새로운 기능이 담겨 있어요. 이 밖에도 핵심 라이브러리 API에 대한 소수의 변경과, 아주 사소한 10여 개의 breaking change가 있는데, changelog에서 확인할 수 있어요.
웹 플랫폼과 JS 상호운용
Dart 3.4와 Flutter 3.22에서 Flutter Web 앱을 WebAssembly로 컴파일하는 것을 지원하기 시작했어요. WebAssembly로 컴파일하려면 이전에는 프리뷰 상태였던 새로운 Dart-to-JS 상호운용 모델을 사용해야 해요. Dart 3.5부터 이 모델은 안정적이고 완전한 것으로 간주되며, 옛 dart:html 라이브러리를 대체하는 package:web의 브라우저 API 바인딩도 1.0으로 업데이트했어요.
모든 웹 패키지 작성자가 package:web로 마이그레이션하길 권장해요. 다음 Dart 릴리스에서는 옛 상호운용 API(dart:html, dart:js, package:js 등)를 deprecated로 만들고, 내년 후반쯤 완전히 제공을 중단할 계획이에요. 이 계획에 대한 피드백을 tracking issue에서 받고 있어요. 또한 pub.dev 패키지 매니저의 점수 책정을 업데이트해서, 새로운 상호운용 모델을 지원하는 웹 패키지에 점수를 부여할 계획이에요.
새로운 JS 상호운용 타입을 올바르게 사용하고 있는지 검증해 주는 새로운 lint도 추가했어요. 웹 패키지를 마이그레이션할 때 이 lint를 analysis_options.yaml 파일에 추가하길 권장해요.
Dart 네이티브 상호운용
Dart에서 C, Java, Kotlin, Objective-C, Swift로 바로 호출할 수 있게 해주는 네이티브 상호운용도 여러 가지로 개선했어요.
C 상호운용은 몇 년간 지원해 온 FFI(Foreign Function Interface) 라이브러리로 가능해요. Dart 3.5에서는 Dart TypedData 객체의 포인터를 FFI로 직접 전달할 수 있게 개선했어요. 그래서 메모리를 먼저 Dart에서 네이티브로 복사하지 않아도 돼요 (details).
Java & Kotlin 상호운용은 현재 프리뷰 상태인 JNIgen 생성기가 담당해요. 이 생성기는 Java Native Interface(JNI)를 통해 Dart에서 Java & Kotlin으로 호출하는 바인딩 코드 생성을 자동화해요. 성능을 개선하고 Java 예외와 Kotlin 최상위 함수를 지원하도록 했어요. 또 이전의 C 기반 바인딩은 제공을 중단했는데, 대체할 Dart 전용 바인딩이 이제 비슷한 성능과 기능을 가지면서 훨씬 쓰기 쉬워졌기 때문이에요. 자세한 내용은 changelog를 참고하세요.
Objective-C 상호운용은 FFI와 (현재 프리뷰 상태인) FFIgen 생성기를 기반으로 해요. Objective-C 프로토콜과 NSString 같은 일반적인 타입을 지원하도록 했어요. FFIgen으로 만든 패키지의 큰 예시로는 Apple의 URL Loading System 네트워킹 라이브러리와 상호운용하는 cupertino_http가 있어요.
앞으로도 상호운용에 계속 투자할 거예요. 위에서 언급한 라이브러리를 완성하는 것도 있고, Swift 지원도 포함돼요. 자세한 내용은 아래 로드맵 섹션에서 볼 수 있어요.
Pub.dev 패키지 저장소
pub.dev는 커뮤니티가 다양한 기능의 패키지를 공유하고 찾을 수 있는 패키지 저장소예요. 여기에 여러 개선이 있었어요. 먼저 topics 지원을 다듬었어요. 이건 패키지 작성자가 자신의 패키지를 해당 카테고리(예: widget)로 태그하는 메커니즘이에요. 같은 카테고리를 다루지만 표현만 살짝 다른 topics(예: widgets vs widget)는 이제 하나로 통합해요.
둘째, 새로운 pub unpack 명령을 추가했어요. 이걸로 패키지를 파일 시스템에 쉽고 빠르게 다운로드할 수 있어요. 예를 들어 패키지의 예제 프로그램을 로컬 머신에서 실행해 보고 싶을 때 쓸 수 있어요.
$ dart pub unpack path
Downloading path 1.9.0 to `./path-1.9.0`...
$ cd path-1.9.0/example/
$ dart run example.dart
Current path style: posix
Current process path: /Users/mit/tmp/path-1.9.0/example
셋째, 새로운 pub downgrade --tighten 명령을 추가했어요. 이 명령은 패키지 의존성의 모든 버전 제약을 확인하는 데 쓸 수 있어요. 실행하면 pub가 해석(resolution)에 성공한 최저 버전으로 하한 제약을 업데이트해요.
Dart 로드맵 업데이트
위에서 설명한 완성된 기능 외에도, 장기 로드맵을 진전시키기 위해 매우 넓은 영역에서 작업을 해왔어요.
대규모 모놀레포를 위한 IDE와 analyzer 성능
'모놀레포(monorepo)'는 Flutter의 packages 저장소처럼, 관련된 패키지와 앱 세트의 소스 코드를 하나의 저장소에 담는 흔한 구조예요. 모놀레포는 모든 소스 코드를 "가까이" 두는 편리함만을 위한 게 아니에요. 저장소 안의 각 패키지와 앱이 서로 호환되도록 보장하는 중요한 도구이기도 해요.
대규모 모놀레포에서 일하는 개발자들로부터 도구, 특히 analyzer의 성능이 부족할 수 있다는 일관된 피드백을 받아 왔어요. 이 문제를 분석해 보면 근본 원인은, 각 패키지와 그 모든 의존성에 대해 겹치는 여러 분석 컨텍스트를 로드하다 보니, 모놀레포 안의 각 패키지 분석이 메모리에 여러 번 복사되어 동시에 존재하게 된다는 거예요. 근본 해법은 그러한 저장소에서 각 의존성 버전에 대한 하나의 공유된 해석을 만드는 것이라고 생각하며, workspaces라는 새 pub 기능으로 그런 능력을 만들고 있어요. 다음 Dart 릴리스에서 이에 대해 훨씬 더 많은 내용을 공유할 거예요. 그때까지는 이 기능이 최근 Flutter engine 저장소에 어떻게 적용됐는지 먼저 살펴볼 수 있어요.
Pub.dev 패키지 저장소
pub.dev 패키지 저장소 사용자들은 오랫동안 각 패키지가 얼마나 사용되고/다운로드되는지에 대한 더 나은 지표를 요청해 왔어요. 이는 패키지 작성자에게는 얼마나 많은 사용자가 자신의 작업에서 혜택을 얻고 있는지의 신호가 되고, 패키지 소비자에게는 다른 개발자들이 어떤 패키지를 사용하는지의 신호가 될 수 있어요. 이 기능에 좋은 진전을 이루고 있으며, 연말까지 프리뷰로 선보이길 바라고 있어요.
Dart 네이티브 interop
JNIgen을 이용한 Java/코틀린 상호운용은, 앞으로 두 분기 안에 핵심 지원을 마무리하고 experimental에서 stable 1.0으로 승격시킬 예정이에요. 자세한 내용은 JNIgen tracker를 참고하세요. Objective-C 상호운용에도 비슷한 목표가 있어요. Objective-C tracker를 참고하세요.
다음으로 Swift 코드와의 직접 상호운용을 조사하고 있어요. 초기 실험이 유망해 보이며, 내년 초에는 experimental 지원을 추가할 수 있길 바라고 있어요.
네이티브 상호운용과 네이티브 소스 코드 번들링
많은 경우 직접 상호운용은 운영체제에 존재하는 API를 호출하는 데 쓰여요. 그래서 그 API는 해당 호스트 플랫폼에서 항상 사용 가능하죠. 하지만 어떤 경우에는 Dart가 상호운용하는 코드가 호스트에 직접 포함되지 않은 네이티브 소스 코드예요. 이건 그런 상호운용을 쓰는 패키지 작성자에게 실질적인 도전 과제가 돼요. 그 네이티브 소스 코드를 어떻게 번들로 묶고 빌드할 것인가 — 사용자에게 수많은 수동 단계를 떠넘기지 않고 말이죠. 이를 지원하기 위해 native assets 시스템을 탐구하고 있어요. 이 시스템은 네이티브 소스 코드를 담은 Dart 패키지를 게시하는 것을 지원하고, dart와 flutter CLI 도구가 그 소스 코드의 빌드와 번들링을 자동화할 수 있게 하는 표준화된 프로토콜을 제공할 거예요. 이를 통해 새로운 종류의 상호운용 사용 사례를 가능하게 하면서, 동시에 네이티브 소스 코드에 의존하는 패키지를 쓰는 개발자에게도 매끄러운 사용자 경험을 제공할 것이라고 상상하고 있어요.
Dart 언어 & 매크로
언어와 컴파일러 팀의 시간 대부분은 현재 아주 큰 언어 기능인 매크로를 진전시키는 데 쓰이고 있어요. 이 기능은 Dart 3.4 블로그 글에서 소개했죠. 당시에도 말했듯이 이건 아주 큰 작업이고, 핫 리로드 같은 핵심 사용 사례에 회귀(regression)를 일으킬 가능성도 있어요. 그래서 철저한 접근을 취하고 있으며, 다음 단계를 공유하기까지 아마 몇 분기 더 작업이 필요할 거예요.
매크로 외에도, Dart 언어 펀넬(funnel)에 기록된 대로 여러 더 작은 언어 기능을 동시에 탐구하고 있어요.
지난 가을부터 Dart 포매터를 다시 쓰고 있어요. 옛 설계는 수년간 잘 동작했지만, Flutter의 성공과 함께 Flutter 사용자들이 자주 작성하는 선언형 코드에 더 잘 맞는 새로운 스타일로 옮기고 싶어요. 옛 포매터는 그런 종류의 출력을 만들어 낼 수 없었어요. 다시 쓰는 작업은 거의 끝났고 곧 배포될 거예요. 직접 써보고 싶다면 tall-style 실험 플래그를 넘기면 돼요 (flag instructions). 이상한 출력이 보이면 피드백을 환영해요.
마무리
오늘 준비한 내용은 여기까지예요. 논의한 로드맵 항목과 Dart 3.5의 새로운 기능 둘 다에 대한 피드백을 환영해요. Dart 3.5는 Dart.dev에서 받거나, 오늘 출시된 Flutter 3.24 릴리스에 함께 담겨 있어요.
더 알아보기
- Changelog — Dart 3.5의 세부 변경 사항
- Pub workspaces — 모놀레포 의존성 해석을 위한 기능