Dart 3 alpha 소개
Dart 3 alpha 소개
지금까지 가장 생산적이고, 이식성 있고, 접근하기 쉬운 Dart 버전을 미리 만나보세요. 이제 조기 테스트와 실험에 사용할 수 있어요.
본문
지난 12월, 우리는 Dart 3를 다루는 첫 블로그 게시물로 미래를 엿볼 수 있게 해 드렸어요. 그리고 오늘 — 케냐 나이로비에서 열린 Flutter Forward 개발자 이벤트에서 생중계로 — Dart의 다음 메이저 릴리스에 대한 더 넓은 비전과, 첫 번째 프리뷰 빌드인 Dart 3 alpha를 공유해요.
Dart의 사명은 어떤 앱 플랫폼에서든 고품질 앱을 만드는 데 가장 좋은 프로그래밍 언어를 만드는 것이에요. Dart는 접근하기 쉽고(approachable), 배우기 쉬우며, 불필요한 복잡성을 피하고 내부적으로 일관돼요. Dart는 생산적이에요(productive). 앱 개발자는 끊임없이 변화하는 요구 사항을 가진 앱을, 더 적은 시간 안에, 그렇지만 타협 없는 품질과 완성도로 제공해야 한다는 압박을 끊임없이 받아요. 그래서 우리는 빠른 개발, 빠른 반복, 높은 품질을 가능하게 하는 데 깊이 집중해서 Dart를 만들었어요. 그리고 Dart는 이식성이 좋아요(portable). 가장 넓은 범위의 플랫폼을 아우르기 때문에, 오늘과 내일의 기기들에 앱을 배포할 수 있게 해 줘요.
우리는 수년에 걸쳐 이 특성들을 확립하기 위해 노력해 왔어요. Dart 3에서는 접근성, 생산성, 이식성을 상당히 다듬고 확장하는 또 하나의 큰 걸음을 내디딜 계획이에요.
Dart 3는 새로운 메이저 릴리스예요. 새로운 기능 면에서 큰 도약을 상징한다는 뜻도 있고, 시맨틱 버저닝 측면에서 breaking 릴리스이기 때문이기도 해요. 즉 타입 시스템을 소리 나는(sound) null 안전성만 지원하도록 바꾸고(Dart 2.12 이상에서는 이게 옵트인이었어요), Dart의 핵심 라이브러리에서 그에 상응하는 breaking 변경을 만들 거예요. 자세한 내용을 살펴볼까요!
Dart 3 생산성 향상
더 표현력 있는 Dart 언어: 레코드, 패턴, 접근 제어
우리는 지난 몇 년간 Dart 언어를 빠른 속도로 진화시켜 왔어요. 2.0 이후 23개의 새 기능을 추가했죠. Dart 3에서는 레코드(records)와 패턴(patterns)이라는 두 개의 주요 기능을 추가해서, 구조화된 데이터를 다루는 작업을 더 생산적으로 만드는 것을 목표로 해요.
레코드를 사용하면 값을 담기 위해 클래스를 선언해야 한다는 개념적 부담 없이, 기존 데이터에서 익명 합성 값을 효율적이고 간결하게 만들 수 있어요. 레코드를 사용하면 기존 데이터를 결합하는 새 데이터 구조를 쉽게 만들 수 있어요. 예를 들어, 값 한 쌍을 반환하려면 이렇게 해요:
(double x, double y) geoLocation(String name) {
if (name == 'Nairobi') {
return (-1.2921, 36.8219);
} else {
...
}
}
레코드가 데이터를 결합하게 해 준다면, 패턴은 합성 데이터를 구성 요소로 분해(destructure)할 수 있어요. 예를 들어 위의 geoLocation의 반환 값(정수 한 쌍으로 이루어진 레코드)을 lat과 long이라는 두 개의 개별 int 변수로 분해하려면, 이런 패턴 선언을 사용하면 돼요:
void main(List<String> arguments) {
final (lat, long) = geoLocation('Nairobi');
print('Current location: $lat, $long');
}
패턴은 완전히 타입 안전하고, 개발 중에 검사돼요.
값의 타입(예를 들어 클래스 계층에서)에 대해 패턴 매칭을 할 수도 있어요. switch는 여기 calculateArea의 본문처럼 타입과 각 타입의 개별 필드에 매칭되는 패턴을 사용할 수 있어요:
sealed class Shape {
}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r
};
전반적으로, 우리는 많은 선택지를 가진 패턴 세트를 추가하고 있어요. 이 패턴들이 결합되면 Dart는 구조화된 데이터에 대해 훨씬 더 표현력 있게 돼요.
패턴과 함께, 우리는 여러 새 수정자(modifier)를 통해 클래스에 기능 제어(capability controls)도 추가하고 있어요.
- interface class: 확장(extend)될 수 없어요.
- base class: 암시적 인터페이스를 비활성화해서 구현(implement)될 수 없어요.
- final class: (현재 라이브러리 밖에서) 클래스를 확장, 구현, 믹스인할 수 없어요.
- sealed class: abstract + final과 같으면서, 타입이 완전성(exhaustiveness) 검사를 위한 밀봉 타입 패밀리의 루트로 간주돼요. 예를 들어 위의
Shape클래스 계층을 보면,Shape타입에 대한switch문(calculateArea함수 같은)에서, sealed 타입의 가능한 모든 하위 타입을 처리하지 않으면 분석기가 오류를 발생시켜요. - mixin class: 믹스인으로 사용될 수 있는 클래스예요.
모든 새 기능은 언어에 복잡성을 더해요. Dart가 접근하기 쉬운 상태를 유지하기 위해, 클래스는 오늘처럼 기본적으로 완전히 허용적(permissive)으로 유지되지만, 믹스인으로 사용하려는 클래스는 이제 mixin 키워드를 사용해야 한다는 작은 예외가 있어요.
직접 플랫폼 라이브러리 인터롭을 통한 생산적인 플랫폼 통합
우리는 Dart에서 플랫폼 라이브러리를 직접 호출하기 위한 Dart 언어 상호 운용성(interoperability)을 확장하는 작업을 하고 있어요. Apple 플랫폼에서는, C 호출 규약을 따르는 모든 C 모듈을 Dart에서 호출할 수 있게 해 주는 C 인터롭을 위한 FFI 작업을 기반으로 구축하고 있어요. 이것은 지금까지 C, Go, Rust 같은 언어를 지원해 왔어요. 이제 iOS/macOS에서 Swift와 Objective-C 지원을 추가하고 있어요. Android에서는 FFI와 Android의 Java Native Interface(JNI)를 활용해서, Kotlin으로 작성된 Jetpack 라이브러리와 Java로 작성된 Android 라이브러리를 호출할 수 있어요.
새 도구의 힘으로, Dart는 원래의 C/ObjC/Swift/Java/Kotlin 코드의 헤더/인터페이스 파일에 기반한, Dart 인터페이스를 가진 바인딩과 그 뒤의 크로스 언어 인터롭 코드를 자동으로 만들 수 있어요. 이것을 사용해서 Apple의 Core Motion API와 Android의 HealthConnect API를 호출하는 데모를 보려면 샘플 앱을 확인해 보세요.
이 새로운 인터롭 메커니즘은 현재 실험적이지만, Dart 3.0 stable에서 최소한 베타 품질에 도달하길 바라고 있어요. 피드백은 Java/Kotlin 또는 C/ObjectiveC/Swift의 이슈 트래커에서 받고 있어요. 마지막으로, 우리는 개선된 Dart-to-JavaScript 인터롭도 작업 중이고, 그에 대한 더 많은 소식은 향후 블로그 게시물에서 다룰게요.
이식성 개선
Dart는 이미 네이티브 및 웹 컴파일러로 넓은 범위의 대상 플랫폼을 지원해요. 웹에서는 현재 JavaScript로 컴파일해요. 네이티브 기기에서는 현재 Intel 32비트·64비트, ARM 32비트·64비트 아키텍처를 지원해요. Dart가 미래의 기기에 대비할 수 있도록, 우리는 지원을 추가하기 위해 작업 중인 여러 새로운 기기와 표준이 있어요.
웹을 위해서는 WebAssembly(Wasm) 지원을 작업 중이에요. Wasm은 모든 현대 브라우저에서 플랫폼 중립적인 바이너리 명령 형식으로 성숙해 가고 있어요. 우리는 Wasm이 네이티브 기기의 앱만큼 빠르게 시작하는 Dart 웹 앱을 가능하게 해 주길 바래요.
관심이 커지고 있는 또 다른 플랫폼 아키텍처는 RISC-V예요. 넓은 용도를 위해 설계된 오픈 표준 명령어 집합 아키텍처(ISA)죠. Android 팀이 최근 RISC-V 지원에 대한 발표를 했고, 우리는 이것이 임베디드 기기에도 넓게 적용될 수 있다고 생각해요. RISC-V 지원은 Dart 3 alpha에서 미리 볼 수 있어요.
마지막으로, 우리는 현재 ARM 기기 지원을 확장해서 Windows에서 ARM64 지원도 포함하는 작업을 하고 있어요.
Dart 3 breaking 변경
새 Dart 3 기능을 다뤘으니, 더 일관되고 접근하기 쉬운 Dart라는 정신으로 만들고 있는 breaking 변경을 살펴볼게요.
Dart 3 타입 시스템: 100% 소리 나는 null 안전성
우리는 2021년 Dart 2.12에서 소리 나는 null 안전성을 도입했어요. 소리 나는 타입 시스템에서는 타입을 신뢰할 수 있어요. 즉 타입이 null이 아니라고 명시하면 변수는 결코 null이 아니라는 뜻이에요. 이는 문제를 프로덕션에서가 아니라 개발 중에 잡아냄으로써 생산성을 높이고, Dart 컴파일러가 더 작고 최적화된 코드를 만들게 해 줘요. 우리가 12월에 논의했듯이, Dart는 기존 언어에 100% 소리 나는 null 안전성을 도입한 유일한 언어예요.
Dart 2.12 이후로 null 안전성은 켜거나 끌 수 있는 구성 옵션이었어요. Dart 3에서는 모든 코드가 소리 나는 null 안전성을 켠 상태로 실행돼요. null 안전성 없이, 또는 부분적 null 안전성을 가진 혼합 모드로 앱을 실행하는 것은 더 이상 불가능해요.
null 안전성 없이 실행하는 지원을 중단하는 것은 기존 앱과 패키지에 문제를 일으킬 수 있다는 걸 인지하고 있어요. 하지만 지금이야말로 이 변경을 진행할 적절한 시기라고 생각해요. null 안전성이 항상 켜져 있는 단일 타입 시스템을 가지면 오버헤드와 복잡성이 줄어들어요. 개발자가 Dart 3 코드를 읽을 때마다 모든 변수가 기본적으로 non-null이라는 것을 안심할 수 있어요. 구현 측면에서, Dart의 컴파일러와 런타임은 Dart 코드를 실행하는 단일 방식만 지원하는 데 집중할 수 있어서, 새 기능을 추가하는 비용과 복잡성이 낮아져요.
Dart 생태계는 기존 코드를 null 안전성으로 마이그레이션하려는 강한 헌신과 의지를 보여줬어요. 오늘 기준으로,
- pub.dev의 상위 250개 패키지 중 100%가 null 안전성을 지원해요.
- pub.dev의 상위 1000개 패키지 중 98%가 null 안전성을 지원해요.
flutter run세션 중 단 14%만 여전히 null 안전성 없이 실행돼요.
마이그레이션 노력에 대해 생태계에 감사드려요!
Dart 3 핵심 라이브러리와 언어 정리
Dart 언어와 타입 시스템이 진화하면서, 핵심 라이브러리의 여러 API가 중복되거나 차선이 돼 버렸어요. Dart가 접근하기 쉽게 유지되도록, 우리는 불필요한 API를 정리하기 위해 여러 breaking 변경을 만들었어요. 자세한 내용은 GitHub 이슈 #34233과 #49529를 참조하세요. 기본 매개변수 값에 대한 레거시 문법(#2357)도 제거했어요.
Dart 3 alpha를 마쳤으니, 우리의 관심은 이제 Dart 3 beta로 옮겨갔어요. 거기서는 많은 새 API를 추가해서 Dart 핵심 라이브러리를 더 다듬을 예정이에요. 이에 대한 의견은 피드백 이슈에서 제안할 수 있어요.
Dart 3 stable을 위한 코드 준비
일반적으로, Dart 2.12 이상을 사용해서 이미 null 안전성으로 마이그레이션한 대부분의 코드는 Dart 3에서도 작동할 거라고 믿어요. 하지만 소수의 패키지와 앱은 위에서 논의한 breaking 변경의 영향을 받을 수 있어요. 따라서 올해 후반의 Dart 3 stable 릴리스를 준비할 시간을 드리기 위해, 우리는 Dart 3 alpha 이후에 Dart 언어와 핵심 라이브러리에 대한 추가 breaking 변경을 계획하지 않았어요.
이미 null 안전성으로 마이그레이션된 코드를 활용하기 위해, Dart 3에서 이전 버전 호환성(backwards compatibility)을 구현했어요. 마이그레이션된 패키지는 >=2.12 <3.0.0 같은 SDK 버전 제약 조건을 가지고 있어도, 다른 중단된 핵심 라이브러리 API나 언어 기능에 의존하지 않는 한 Dart 3에서 pub get으로 해석될 수 있어요. 로컬에 설치된 Dart 3 alpha SDK로 테스트하는 방법을 포함한 자세한 내용은 Dart 3 null 안전성 문서를 참조하세요.
pub.dev 저장소에 게시된 패키지에 대해서는 Dart 3 alpha로 분석을 실행하고, 통과한 패키지에 "Dart 3 ready" 태그를 붙였어요. 이 태그는 올해 후반 stable로 출시될 때 해당 패키지가 Dart 3에서 작동할 가능성이 높다는 것을 나타내요.
Dart 3 도구 정리
Dart 3를 준비하면서, 우리는 모든 터미널 개발자 도구를 통합된 dart 개발자 도구로 옮기기 위한 여러 변경을 만들었어요. 이는 새 개발자에게 Dart를 훨씬 더 접근하기 쉽게 만들어 줬어요. 앞으로 몇 달 동안 추가 정리를 위한 도구 변경을 더 할 계획이에요. 바라건대 이 변경들이 너무 방해가 되지는 않겠지만, Dart 3 beta에서 도구에 대한 일부 사소한 손상은 예상해야 해요.
다음 단계
Dart 3 alpha는 오늘 Dart dev 채널과 Flutter master 채널에서 사용할 수 있어요. 레코드와 패턴 같은 새 언어 기능을 실험해 보거나, 새 직접 접근 플랫폼 인터롭을 사용해 보거나, 앱과 패키지의 Dart 3 호환성을 테스트해 보는 등 사용해 보시길 초대해요.
앞으로 몇 달 동안 Dart 3를 계속 다듬고, 올해 후반에 완전한 Dart 3 stable 릴리스를 제공하길 바래요. 대부분의 기능은 여전히 개발 중이라 stable 릴리스 전에는 변경될 수 있다는 점에 유의하세요. 그때까지 Dart 코드를 즐겁게 작성해 보세요!