Hello, Dart3 (feat. Flutter 3.10)

CuroGom
Flutter Seoul
Published in
11 min readMay 10, 2023

원본 문서 : https://medium.com/dartlang/announcing-dart-3-53f065a10635

오늘 (2023년 5월 11일, KST) Google I/O 2023이 열렸습니다.

Flutter와 Dart에서도 많은 소식이 있지만,
그 중에서도 단연코 가장 큰 업데이트는 Flutter 3.10과 Dart3 이 Release가 되었습니다.

Dart 3

사실 Dart 3에 대한 예고는 작년 말 부터 계속 흘러나오고 있다가,
Flutter Forward에서 Dart3a (Dart3 알파) 발표 이후,
몇 개월 안된 시간 내에 Dart3이 드디어 Release 되었습니다.

100% Null Safety 지원

Dart는 2.12 버전 때, Flutter 2.0과 함께 발표 되어 Null Safety를 지원하기 시작했습니다.
당시 Flutter를 쓰시던 분들은 새로운 개념이 들어오면서 Migration 하는 과정들이 추억속에 남아계신 분들이 많을텐데요, (모쪼록 즐거운 추억이었길 바랍니다.)

이제 그 Null Safety가 더욱 강력하게 보완되어 드디어 Dart 3에 들어서서 100% Null Safety 한 언어가 되었습니다.

이제 변수가 null인지, 아닌 지를 코드를 작성 하는 내에 명확하게 표시해야하며, 이제 컴파일러와 런타임 시에, Null Point Exception 에 대한 검증이 더욱 강화 될 것입니다.
이에 일부 코드들이 Miagration되어야 할 필요가 생겼지만 Dart가 지향하는 성능 개선 부분이나 Modern Language에는 더욱 가까워져 가는 것 같습니다.

Dart 3 Migration

현재 pub.dev의 상위 1000개 프로젝트 중에서 99%는 Dart Null Safety가 지원되고 있습니다.

이로써, Flutter 2.0 때 처럼 힘든 여정의 Migration이 아닌 자연스러운 Migration이 예상됩니다. 그래도 별도의 작업들이 필요한 경우들이 있을텐데, 그럴 경우에는 Dart 팀에서 제공하고 있는 Migration Guide를 확인 해 보시고 적용 해 보는 걸 추천 드립니다.

Dart 3 주요 기능 업데이트

Dart 3이 업데이트 되면서 Null Safety만 강력해 진 게 아닌, 언어 자체의 기능도 개선 된 부분이 꽤 많습니다.

대표적으로 Records 형태의 함수 반환, Pattern의 활용, Class Modifiers 입니다.

Records 형태를 활용한 구조화 된 데이터 구축

Dart 2에서는 하나의 함수는 하나의 값만 반환할 수 있었습니다.
그래서 여러 값이 필요했을 경우 Map이나, List 같은 Collection을 활용해서 코딩을 하셨던 분들이 적지 않으셨을 것 같습니다.

Dart3에서는 Python의 Tuple 같은 형태로 여러 값을 한 함수에서 반환 하는 것이 가능합니다.

(String, int) getUserInfo(Map<String, dynamic> json) {
return (json['userName'] as String, json['born'] as int);
}

위와 같은 코드를 통해서 Map을 한 번 감싸고, 쓸 때 다시 Map을 탐색 해야하는 과정들이 생략 될 수 있습니다.

Records는 단순한 값의 유형일뿐, Collection이 아니다보니 조금 더 간편하고 편리하게 개발하는데 활용될 수 있을 거라 생각됩니다.

자세한 내용은 별도 문서를 참고 하세요.

Pattern과 Pattern Matching을 활용하여 구조화된 데이터 작업

Records는 구조화된 데이터를 구축하는 방법을 간소화합니다.
이는 더 공식적인 유형 계층을 구축하는 데 클래스를 사용하는 것을 대체하는 것이 아닌 그저 다른 옵션을 제공할 뿐입니다.

어느 경우든 구조화된 데이터를 개별 요소로 분리하여 작업하려 할 수 있습니다. 이것이 Pattern Matching이 필요한 이유입니다.

기본 형태의 Pattern을 고려해 보세요.
다음 Records pattern은 Records를 두 개의 새 변수 userNameborn으로 분해합니다.
이러한 변수는 print 호출과 같은 다른 변수와 마찬가지로 사용할 수 있습니다.

var (String userName, int born) = userInfo({'userName': 'CuroGom', 'born': 1989});
print('User $nameName was born in $born.');

List와 Map에 대해서도 유사한 패턴이 존재합니다.
이 모든 경우에 밑줄 패턴을 사용하여 개별 요소를 건너뛸 수 있습니다.

var (String name, _) = userInfo(…);

Pattern matching은 switch 문에서 사용할 때 가장 빛나는 능력을 발휘합니다. Dart는 처음부터 switch를 지원했지만,
Dart 3에서는 switch 문의 기능과 표현력을 확장했습니다.

이제 단일 case외에도 Pattern matching을 지원합니다.
추가로 각 case 끝에 break를 추가하는 필요성을 제거했습니다.
(이로 인해 일부 타 언어와 switch 문의 결과가 달라질 수 있으니 기존 타 언어 (Java 등)를 활용 하셨던 분들은 한 번 더 확인 할 필요가 있습니다.)

또한 논리 연산자를 사용하여 case를 결합할 수 있습니다.
다음 예제는 문자 코드를 구문 분석하는 멋지고 간결한 switch 문을 보여줍니다.

switch (charCode) {
case slash when nextCharCode == slash:
skipComment();
case slash || star || plus || minus:
operator(charCode);
case >= digit0 && <= digit9:
number();
default:
invalid();
}

switch 문은 각 case에 대해 하나 이상의 문이 필요할 때 큰 도움이 됩니다.
경우에 따라 값을 계산하기만 하면 될 때도 있습니다. 이 경우에는 별도의 switch 함수를 제공합니다. 이는 switch 분기문과 유사하지만, 식에 맞게 최적화된 다른 구문을 사용합니다. 다음 샘플 함수는 switch 식의 값을 반환하여 오늘의 요일에 대한 설명을 계산합니다.

String describeDate(DateTime dt) =>
switch (dt.weekday) {
1 => 'Feeling the Monday blues?',
6 || 7 => 'Enjoy the weekend!',
_ => 'Hang in there.'
};

Pattern의 강력한 기능 중 하나는 “완전성(exhaustiveness)”을 확인하는 능력입니다.
이 기능은 스위치가 모든 가능한 경우를 처리하는지 확인합니다.
이전 예제에서 우리는 weekday라는 int에 대한 모든 가능한 값을 처리합니다.
특정 값 1, 6 또는 7에 대한 match 문의 조합과 나머지 경우를 위한 _ 기본 케이스를 사용하여 모든 가능한 값을 완전히 처리합니다.
이러한 검사를 사용자 정의 데이터 계층 구조(예: 클래스 계층 구조)에 대해 활성화하려면 다음 예제와 같이 최상위 클래스 계층에 새로운 sealed 수정자를 사용하면 됩니다.

sealed class Animal { … }
class Cow extends Animal { … }
class Sheep extends Animal { … }
class Pig extends Animal { … }

String whatDoesItSay(Animal a) =>
switch (a) { Cow c => '$c says moo', Sheep s => '$s says baa' };

이렇게 하면 다음 오류가 발생하여 가능한 하위 유형(Pig)을 처리하지 못했다는 경고가 표시됩니다.

line 6 • The type 'Animal' is not exhaustively matched by the switch cases
since it doesn't match 'Pig()'.

마지막으로, if 문도 패턴을 사용할 수 있습니다.
다음 예제에서는 맵 패턴을 사용하여 JSON 맵을 분해하는 if-case 매칭을 사용합니다.

그 안에서는 상수 값(예: 'userName''CuroGom'와 같은 문자열)과 유형 테스트 패턴 int born를 일치시켜 JSON 값을 읽습니다. 패턴 일치가 실패하면 Dart는 else 문을 실행합니다.

final json = {'nameName': 'CuroGom', 'born': 1989};

// Find CuroGom's born year.
if (json case {'nameName': 'CuroGom', 'born': int born}) {
print('CuroGom was born in $born.');
} else {
print('Error: json contains no born info for CuroGom!');
}

이것은 Pattern으로 할 수 있는 모든 것에 대해 간략히 소개한 것입니다.
더 자세한 내용은 Pattern 관련 문서Pattern Codelab을 확인하세요.

Dart의 향후 전망

Dart 3는 당장 사용할 수 있는 새로운 기능에 대한 중요한 발전뿐만 아니라 미래에 대한 미리보기도 제공합니다.

Dart 언어

Recodes, Pattern, Class modifiers는 매우 큰 새로운 기능이므로 개선할 부분이 있을 수 있습니다.

또한 마이그레이션 비용이 전혀 없으며 개발자 생산성을 높이는 더 작고 점진적인 기능을 탐색 중입니다. Dart팀에서 탐색 중인 두 가지 예는 기존 타입을 zero-cost “wrappers”로 래핑하는 인라인 클래스와 몇 가지 필드와 기본 생성자가 있는 클래스를 정의하는 더 간결한 구문을 도입하는 기본 생성자입니다.

특히 JSON (및 유사한)의 개선된 역직렬화와 데이터 클래스를 사용하기 위해 이를 가능하게 하기 위해 매우 철저한 접근 방식을 취하고 있으므로 최종적인 디자인 결정에 대한 구체적인 일정이 없다는 소식입니다. (당분간은 json serializable과 Frezzed 강세가 계속 되겠네요)

Native interop

모바일 및 데스크톱 앱은 일반적으로 알림, 지불 또는 전화, 위치 가져오기와 같은 기본 플랫폼에서 제공되는 많은 API에 의존합니다. Flutter에서는 일반적으로 이러한 기능을 플러그인을 구축하여 Dart 코드와 플랫폼별 코드를 모두 작성해야 합니다.

이미 [dart:ffi](<https://dart.dev/guides/libraries/c-interop>)로 C 라이브러리로 컴파일되는 코드와 상호 운용성을 지원합니다. 현재 Android에서 Java 및 Kotlin 상호 운용성과 iOS / macOS에서 Objective C 및 Swift 상호 운용성을 지원하기 위해 이를 확장하고 있습니다. Android 상호 운용성에 대한 소개는 Google I/O 23의 Android 상호 운용성 비디오를 확인하세요.

WebAssembly으로의 컴파일 — 네이티브 코드로 웹을 대상화

WebAssembly (Wasm)은 모든 최신 브라우저에서 플랫폼 중립적인 이진 명령어 형식으로 성숙해져왔습니다.

Flutter 프레임워크는 Wasm을 사용합니다. 이것은 C++로 작성된 SKIA 그래픽 렌더링 엔진을 Wasm 컴파일된 모듈을 통해 브라우저로 전달하는 방법입니다. Dart와 같은 객체 지향 언어는 garbage 수집을 사용합니다. 작년 동안 Wasm 생태계의 여러 팀과 협력하여 Chromium 및 Firefox 브라우저에서 새로운 WasmGC 기능을 추가하기 위해 노력들이 이루어졌으며, 이제 안정적인 수준에 도달하였습니다.

Dart팀에서는 Dart를 Wasm 모듈로 컴파일하는 작업은 웹 앱에 대해 두 가지 고수준 목표를 가지고 있습니다.

  • 로딩 시간: 브라우저가 더 빨리 로드할 수 있는 Wasm으로 배포 페이로드를 전달하여 웹 앱과 상호 작용하는 데 걸리는 시간을 단축할 수 있도록 기대합니다.
  • 성능: JavaScript로 구동되는 웹 앱은 좋은 성능을 얻기 위해 JIT(Just-In-Time) 컴파일이 필요합니다. Wasm 모듈은 더 낮은 수준이며 기계 코드에 더 가까우므로 덜 지연이 발생하고 더 일관된 프레임 속도를 제공할 수 있다고 생각합니다.
  • 의미론적 일관성: Dart는 지원되는 플랫폼 간에 매우 일관성을 자랑합니다. 그러나 웹에서는 이에 대한 몇 가지 예외가 있습니다. 예를 들어, 현재 Dart 웹은 숫자가 나타내는 방식에서 차이가 있습니다. Wasm 모듈을 사용하면 웹을 다른 네이티브 대상과 유사한 의미론적으로 다룰 수 있습니다.

자세한 내용은 별도 문서를 참고하세요. (https://flutter.dev/wasm)

--

--

CuroGom
Flutter Seoul

BroadCast Eng (2013 ~ 2018), Developer (2019~ ) / mail : i_am@curogom.dev