Dart 3으로 가는 길: 완전히 sound한 null safe 언어
Dart 3으로 가는 길: 완전히 sound한 null safe 언어
Dart가 sound null safety만 지원하게 되는 다음 메이저 릴리스를 준비하면서, 어떤 변화가 오는지 하나씩 설명해 드릴게요.
출처: 원문
본문
지난 4년 동안 우리는 Dart를 빠르고, 이식성 좋고, 현대적인 언어로 발전시켜 왔어요. 다음 릴리스인 Dart 3는 완전히 sound한 null safe 언어로 가는 여정을 완성해요. 그 여정의 마지막 단계로, 우리는 역사적인 Dart 언어·SDK 아티팩트 몇 가지를 제거하는데, 그중에는 sound null safety 없이 실행하는 지원을 없애는 것도 포함돼요. 이렇게 하면 Dart를 배우기 쉬워지고, Dart SDK가 더 빠르게 새 기능을 지원하도록 진화할 수 있어요. Dart 3의 주요 변경사항과 앱·패키지를 준비하는 최선의 방법이 궁금하다면 계속 읽어 보세요!
Dart 3으로 가는 길. (사진: Unsplash의 Niklas Ohlrogge)
왜 sound null safety인가요?
많은 현대 프로그래밍 언어가 null safety(일명 void safety)를 지원해요. 이것은 null 역참조(dereferencing) 문제를 피하게 해 주는데, Tony Hoare는 이걸 '10억 달러짜리 실수'라고 불렀어요: "이것은 무수한 오류, 취약점, 시스템 크래시로 이어졌고, 지난 40년 동안 아마 10억 달러 상당의 고통과 손해를 일으켰을 거예요" (Hoare 2009). 이를 해결하기 위해 Swift, C#, Kotlin 같은 언어는 변수를 non-null(null 값을 절대 가질 수 없음) 또는 nullable(값이나 null을 가질 수 있음)로 선언할 수 있는 타입 시스템을 지원해요. 이 타입 시스템을 정적 분석과 결합하면 null이 non-nullable 변수에 할당되는 것을 감지할 수 있어요. Dart 언어의 null safety 지원도 비슷한 모델을 사용하는데, 변수를 기본적으로 non-nullable로 만들고 명시적으로 선언했을 때만 null을 허용해요.
한 단계 더 나아가, Dart는 기존 언어에 sound null safety를 도입해요. sound한 시스템은 non-nullable 변수가 null 값을 절대 포함하지 않음을 보장해요. Dart null safety를 계획하던 초기에 우리는 이 주제와 다른 언어들이 택한 트레이드오프를 탐구했어요. Swift의 설계는 처음부터 soundness를 포함했어요. TypeScript는 기본 타입 시스템이 어떤 객체든 어떤 정적 타입으로 볼 수 있게 허용하기 때문에 본질적으로 unsound해요. 이 TypeScript 예제처럼 null 값을 non-null 변수에 할당할 수 있죠. ergonomics 때문이면서도 기존 코드 전부를 마이그레이션하는 걸 피하기 위해 C#에는 여러 예외가 있어요. Kotlin도 Java와의 상호운용이라는 목표 때문이기도 하지만 여러 unsound한 예외가 있어요. 이 Kotlin 예제처럼 제네릭 타입은 null 값이 non-null 요소를 담는 것으로 선언된 리스트에 흘러 들어가는 경우를 만들 수 있어요.
Dart에서는 sound null safety의 길을 선택했어요. 여기엔 트레이드오프가 따랐죠. 몇몇 경우에는 null safety로의 마이그레이션이 조금 더 비쌌지만, 그 결과 non-null 타입 애너테이션을 완전히 신뢰할 수 있는 sound한 타입 시스템을 얻었어요. 우리는 타입 시스템의 nullability 측면을 바탕으로 컴파일러와 런타임에서 구현 최적화를 할 수 있어요. 변수가 언제 null이 아닌지 정확히 알죠. 우리는 이것이 Dart에 맞는 트레이드오프라고 생각해요.
Dart 3에서는 모든 Dart 코드가 sound null safety를 사용할 거예요
추적 이슈: SDK #49530.
Dart 2.12에서 Dart 언어에 null safety를 도입한 지 3년이 지났어요. 앞 절에서 언급했듯이, 우리는 기존 Dart 패키지와 앱을 마이그레이션하는 데 필요한 부담을 인지하고 있어요. 마이그레이션을 돕기 위해 Dart는 앱 코드를 세 가지 방식으로 실행할 수 있게 지원했어요. null safety 없이, 부분 null safety의 혼합 모드로, 또는 완전한 sound null safety로요. 완전한 sound null safety는 모든 의존성을 포함한 코드의 100%가 마이그레이션됐을 때 성립해요. 이 덕분에 Dart 개발자들은 기존 코드를 한 단계씩 마이그레이션할 시간을 가질 수 있었어요. 하지만 여러 모드를 지원하는 것은 오버헤드와 복잡성을 추가했어요.
첫째, Dart 개발자는 세 가지 모드를 모두 알아야 해요. Dart 코드를 볼 때마다 언어 버전을 확인해서 타입이 기본적으로 non-null인지, nullable인지, 아니면 그 조합인지 살펴봐야 하죠.
둘째, 컴파일러와 런타임에서 세 가지 모드를 모두 지원하는 것은 Dart SDK의 진화를 느리게 해요. 이 지원은 새 기능을 추가하는 비용과 복잡성을 키워요.
Dart 3에서는 sound null safety가 앞서 언급했고 발표했듯이 유일하게 지원되는 모드가 될 거예요. SDK 제약의 하한이 2.12 미만인 pubspec 파일은 Dart 3 이상에서 해석(resolve)이 중단돼요. 언어 마커가 포함된 소스 코드는 제약을 2.12 미만으로 설정하면(예: // @dart=2.9) 실패해요.
텔레메트리에 따르면 현재 모든 flutter run 실행의 약 85%가 sound null safety를 사용한다고 봐요. 나머지 15%에 해당하는 앱이나 패키지가 있다면, Dart 3가 출시되기 전(예상 시기는 2023년 중반)에 마이그레이션을 완료해 주세요.
대규모 코드베이스의 마이그레이션은 시간이 걸릴 수 있음을 이해해요. 하지만 그 노력이 가치 있다고 믿어요. BMW는 최근 약 300명의 개발자 팀이 만든 매우 큰 앱인 메인 MyBMW 앱을 마이그레이션했어요:
"MyBMW 앱 같은 대규모 코드베이스의 null-safety 마이그레이션이 분명 쉽지는 않았지만, Google의 도구가 마이그레이션 과정에서 큰 도움을 줬어요. 마이그레이션을 마친 뒤에는 오류에 덜 취약한 코드베이스를 갖게 되어 기뻐요." — Christian Schmid, BMW AG
마이그레이션에 대해 더 알고 싶다면 아래 동영상을 보거나 마이그레이션 가이드를 확인하세요.
"Dart 패키지를 null safety로 마이그레이션하는 방법"을 새 탭에서 YouTube로 보기
언어·API의 브레이킹 체인지
null safety 변경과 함께, 우리는 Dart 언어와 핵심 라이브러리 API에서 역사적인 아티팩트를 제거하기 위해 몇 가지 다른 변경도 해요. 여기에는 단종된 핵심 라이브러리 API 제거(#49529), 기본 매개변수 값의 역사적인 문법 제거(#2357), tear-off를 명시적으로 요구하기(#2399)가 포함돼요.
이 변경들은 null safety로 마이그레이션된 코드에는 영향이 적다고 생각해요. 첫 번째 Dart 3 alpha 빌드를 출시하면, 이 작은 브레이킹 체인지들이 여러분의 패키지나 앱에 적용되는지 빠르게 테스트할 수 있을 거예요.
Dart 3의 새 기능과 역량
Dart 3에는 다른 프로그래밍 언어와의 개선된 상호운용성과 새 언어 기능을 포함한 여러 새 역량도 포함될 예정이에요. 이 주제에 대해서는 다음 주요 이벤트인 2023년 1월 25일의 Flutter Forward에서 훨씬 더 자세히 이야기할 거예요.
한 가지 언어 기능을 미리 엿보자면, patterns가 있어요. Patterns는 Dart 언어를 훨씬 더 표현력 있게 만들고, 더 구조화된 데이터를 지원하며, 대수적 데이터 타입과 함께 더 함수형 스타일을 가능하게 해요.
아래 코드는 함수에서 여러 반환 값을 사용하고, 그걸 개별 변수로 *구조 분해(destructure)*하는 능력과 짝을 이루는 예제를 보여줘요:
// A function which returns a record -- in this case a pair of two doubles:
(double x, double y) getLocation(String name) {
if (name == 'Aarhus') {
return (56.1629, 10.2039);
} else {
...
}
}
// Deconstructing the returned record back into individual variables:
void main(List<String> arguments) {
final (lat, long) = getLocation('Aarhus');
print('Current location: $lat, $long');
}
// You can also define a hierarchy of classes, and then pattern match on those:
sealed class Shape {
double calculateArea();
}
class Square implements Shape {
final double length;
Square(this.length);
double calculateArea() => length * length;
}
class Circle implements Shape {
final double radius;
...
}
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r
};
올해 초에 이 새 기능에 대한 훨씬 더 자세한 내용을 공유할게요. 미리 엿보기를 보려면 언어 사양을 확인할 수 있어요.
Dart 3 너머
우리는 아이디어 구상에서 실험, 잠재적 출시까지 여러 잠재적인 새 기능을 병렬로 작업하고 있어요. 그래서 현재 작업 중 일부는 Dart 3 이후에 완성될 거예요.
첫째, 작년에 언급했듯이 Dart 코드를 WebAssembly(Wasm)로 컴파일하는 지원을 작업 중이에요. Wasm은 Flutter Web 앱이 브라우저에서 완전한 네이티브 코드로 실행되게 해 줘요. 이것은 드문 작업으로, Dart 컴파일러를 업데이트하는 것 이상의 작업이 필요해요. W3C와 브라우저 벤더와 협력해 WasmGC 확장을 통해 Wasm에서 가비지 컬렉팅 언어 지원을 추가해야 해요.
둘째, macros를 작업 중이에요. 이것은 정적 메타프로그래밍을 가능하게 해요. 이 강력한 메커니즘은 한 조각의 코드(macro)가 프로그램의 컴파일 중에 그 프로그램의 소스 코드를 수정하고 확장하게 해 줘요. 예를 들어 JSON을 역직렬화하거나 데이터 클래스를 만드는 데 필요한 보일러플레이트를 줄일 수 있어요.
이런 기능과 그 밖의 흥미로운 기능들은 Dart 3 이후에 기대하시면 돼요.
Dart 3으로 가는 길
앞 절에서 언급했듯이, 아직 sound null safety로 마이그레이션하지 않았다면 그게 첫 번째 단계예요. 지금 바로 하시길 권해요!
다음으로, Dart 3는 일련의 마일스톤으로 출시될 거예요. 현재 예상 일정은 이렇게 돼요:
- 2023년 1월/2월경: Dart 3 alpha 출시. 초기 Dart 3 호환성 테스트를 가능하게 하는 데 초점을 맞춰요. 정적 분석(
dart analyze/flutter analyze)을 실행할 수 있을 것으로 기대해요. Dart 3 alpha로 정적 분석을 통과하는 어떤 앱이나 패키지라도 Dart 3 stable을 지원해야 한다는 게 우리의 목표예요. - 2023년 3월/4월경: Dart 3 beta 출시. 이 릴리스는 Dart 3의 새 기능을 미리 보여줘요. 새 기능을 실험하고 이슈나 개선 제안에 피드백을 줄 수 있어요.
- 2023년 중반경: Dart 3 stable 출시. sound null safety가 유일하게 지원되는 모드가 돼요.
요약
Dart 3 릴리스는 2023년 중반쯤 출시될 예정이에요. 여기에는 몇 가지 브레이킹 체인지가 포함되는데, 주로 sound null safety 없이 실행하는 것을 중단하는 것이에요. 2023년 1월이나 2월쯤 Dart 3 alpha 빌드를 준비할 예정이며, 이를 Dart 3 호환성 테스트에 사용할 수 있어요.
앱을 준비하려면:
- 남아 있는 null safety 마이그레이션을 완료하세요.
- 코드가 어떤 단종된 API도 사용하지 않는지 확인하세요.
dart fix를 실행하세요.
Dart 3에는 patterns 같은 강력한 새 기능도 여러 개 포함될 거예요. 봄쯤 모든 새 기능을 보여주는 Dart 3 beta 릴리스를 준비할 예정이에요.
더 알아보기
- Dart 3 마이그레이션 가이드 — 기존 코드를 sound null safety로 옮기는 방법을 단계별로 확인해 보세요.
- Dart 언어 버전 관리 — 언어 버전이 타입 기본값에 어떤 영향을 주는지 알아보세요.
- Null safety 이해하기 — null safety의 개념을 심층적으로 다루는 글을 읽어 보세요.