Dart 3.7 발표

Dart 3.7 발표 (Announcing Dart 3.7)

새해가 되었으니, 새로운 Dart 안정 버전이 나올 때가 됐어요. Dart 3.7에 오신 걸 환영해요!

이번 릴리스의 주제는 개발자 생산성이에요. Dart 언어 자체는 wildcard 변수를 다루는 방식을 다듬어서 더 일관성 있게 만들었어요. Dart 포매터에는 완전히 새로운 스타일이 들어왔고, analyzer에는 새로운 퀵 픽스와 린트가 추가됐으며, pub.dev에도 멋진 새 기능이 몇 가지 생겼어요. 자세히 살펴볼게요!

출처: Announcing Dart 3.7

본문

매크로 업데이트

최근에 이슈 트래커와 블로그 글에서, 실험적인 매크로 기능에 대한 작업을 중단한다고 밝혔어요. 아쉬운 결과에도 커뮤니티가 너무 잘 이해해 줘서 감사하다는 말씀을 전하고 싶어요.

하지만 이 변경 덕분에 팀은 다른 가치 있는 개발에 투자할 역량을 되찾았어요. 이미 build_runner 성능 개선을 살펴보고 있고, 여전히 augmentations 언어 기능을 출시할 예정이며(아마 조금 다른 형태로), 데이터 모델링을 지원하고 (예를 들어 JSON으로의) 직렬화·역직렬화를 다루는 더 새롭고 직접적인 방법을 찾고 있어요.

Wildcard 변수 언어 기능

_로 이름 지은 지역 변수와 매개변수는 이제 함수형 언어들이 말하는 "wildcard"가 돼요. 이는 그 이름이 자리 표시자(placeholder)일 뿐, 실제 변수를 선언하지 않는다는 뜻이에요.

현재 Dart에서 콜백의 본문이 인자를 실제로 쓸 필요 없을 때 콜백 매개변수의 이름을 _로 짓는 게 흔해요. 이렇게요.

void announceCompletion(Future<void> future) {
  future.then((_) { // <--
    print('Complete!');
  });
}

그런데 콜백에 쓸 필요 없는 매개변수가 여러 개라면, 이름이 충돌하지 않도록 _, __, ___처럼 이름을 붙여야 했어요. 이제 Dart 3.7에서는 _로 이름이 지어진 매개변수와 지역 변수가 실제로 변수를 만들지 않기 때문에 이름 충돌 가능성이 없어요. 여러 매개변수에 _를 쓸 수 있죠. 이렇게요.

void announceFailure(Future<void> future) {
  future.onError((_, _) { // <--
    print('Error!');
  });
}

_로 이름 지은 매개변수나 변수를 선언하고 사용하는 코드가 있다면, 그 코드는 Dart 3.7에서 더 이상 동작하지 않아요. 예를 들어.

var [1, 2, 3].map((_) {
  return _.toString();
  //     ^ Error! Reference to unknown variable.
});

이런 상황에서는 매개변수나 지역 변수의 이름을 바꿔야 해요. 이 언어 변경은 매개변수와 지역 변수에만 적용되고(최상위 변수나 멤버에는 적용되지 않아요), 그래서 라이브러리의 public API를 깨지 않고 이름을 바꿀 수 있어요.

Wildcard 변수는 언어를 더 일관성 있게 만드는 작은 기능이에요. 패턴 안에서 _로 이름 지은 변수는 항상 이런 식으로 동작했어요.

var [_, _, third, _, _] = [1, 2, 3, 4, 5];
print(third); // Prints "3".

이제 매개변수와 지역 변수도 같은 방식으로 동작해요.

Dart 포매터의 새로운 포맷팅 스타일

Dart 3.7에는 다시 쓴 Dart 포매터(dart format)와 함께 새로운 포맷팅 스타일이 포함돼요. 새 스타일은 인자 목록에 trailing comma를 추가했을 때 나오는 스타일과 비슷해 보이는데, 이제는 그 콤마를 포매터가 알아서 추가하고 제거해 준다는 점이 달라요. 예를 들어.

// Old style:
void writeArgumentList(
    Token leftBracket, List<AstNode> elements, Token rightBracket) {
  writeList(
      leftBracket: leftBracket,
      elements,
      rightBracket: rightBracket,
      allowBlockArgument: true);
}

// New style:
void writeArgumentList(
  Token leftBracket,
  List<AstNode> elements,
  Token rightBracket,
) {
  writeList(
    leftBracket: leftBracket,
    elements,
    rightBracket: rightBracket,
    allowBlockArgument: true,
  );
}

이건 도구 자체와, 그 출력, 동작 방식 모두에 걸친 큰 변경이에요. 전환을 최대한 매끄럽게 만들기 위해, 받게 되는 포맷팅 스타일은 포맷팅하는 코드의 언어 버전에 따라 달라져요. 언어 버전이 3.6 이하이면 옛 스타일로, 3.7 이상이면 새 스타일로 포맷돼요.

패키지의 pubspec에서 SDK 제약을 올려(그리고 dart pub get을 실행해서!) 패키지를 Dart 3.7로 업그레이드하면, 새 포맷팅 스타일에 옵트인하게 돼요.

dart format은 포맷할 각 파일의 언어 버전을 알아내기 위해 package_config.json을 찾아봐요. 즉 패키지의 코드를 포맷하기 전에 dart pub get을 실행해야 한다는 뜻이에요. CI 서버에 포맷 확인이 있다면 그쪽에서도 dart pub get을 실행하도록 해야 해요.

두 스타일을 무기한 지원할 생각은 없어요. 언젠가 생태계 대부분이 3.7 이상으로 올라가면 옛 스타일 지원은 제거될 거예요.

새 스타일에는 오랫동안 요청받아 온 기능이 포함돼 있어요.

  • 프로젝트 전역 페이지 너비 설정. analysis_options.yaml 파일에서 프로젝트 전체에 적용되는 원하는 포맷팅 페이지 너비를 설정할 수 있어요. 이렇게요.
formatter:
  page_width: 123
  • 포맷팅에서 코드 영역 제외. 특수 마커 주석 한 쌍으로 코드 영역을 자동 포맷팅에서 제외할 수 있어요.
main() {
  // dart format off
  no   +   formatting     +     here;
  // dart format on
}

breaking change도 하나 있어요. 포매터가 더 이상 dart format --fix를 지원하지 않아요. 대신 dart fix를 써서 dart format이 할 수 있는 모든 수정과 그 이상을 적용하면 돼요.

더 자세한 내용은 CHANGELOG와 FAQ에서 확인할 수 있어요.

Dart analyzer의 업데이트된 퀵 픽스와 새 린트

우리는 개발자 생산성을 높이기 위해 Dart를 계속 개선하고 있어요. 그 말은 Dart 3.7에서 analyzer에 새 린트, 퀵 픽스, 어시스트를 추가한다는 뜻이기도 해요. 주목할 만한 추가 중 하나는 새 wildcard 변수 기능을 지원하는 unnecessary_underscores 린트예요.

Dart 3.7에 포함된 광범위한 새 퀵 픽스와 어시스트 중에는, 누락된 await 키워드, 잘못된 import 접두사, cascade_invocations 같은 린트 규칙 위반 같은 흔한 문제를 고치는 것들이 있어요. 코드를 리팩터링하는 데 유용한 어시스트도 있는데, 예를 들어 else 블록을 else if로 변환하거나 Flutter 위젯을 ExpandedFlexible로 감싸는 것이 있어요.

이 모든 것과 더 많은 내용은 analyzer 변경 목록 전체에서 확인할 수 있어요.

Dart 웹 플랫폼을 향해 나아가기

1년 전, 우리는 JavaScript와 브라우저 API와 상호운용할 수 있는 새 라이브러리와 패키지를 소개했어요. 앞으로 기본 JavaScript 라이브러리는 dart:js_interop이에요. 브라우저 API에는 package:web을 쓰면 돼요.

Dart와 Flutter 생태계를 WebAssembly 호환 API로 옮기려는 지속적인 노력의 일환으로, Dart SDK의 관련 레거시 API를 deprecated 처리하고 있어요. Dart 3.7 릴리스에서는 일곱 개의 Dart SDK 라이브러리가 deprecated 됐어요.

  • dart:html
  • dart:indexed_db
  • dart:js
  • dart:js_util
  • dart:web_audio
  • dart:web_gl

이 라이브러리들은 2025년 말에 제거할 계획이에요. 대체제에 대해서는 JavaScript interoperability 페이지에서 배울 수 있어요.

pub.dev 패키지 사이트의 새로운 생산성 기능

12월에 pub.dev 패키지의 다운로드 수를 출시했어요. 월간 다운로드와 함께 시간에 따른 주간 다운로드를 보여주는 작은 차트도 표시돼요.

오늘은 이를 확장해서 패키지 버전별 다운로드 수를 볼 수 있게 했어요. 이제 패키지 소비자 중 몇 명이 최신 버전(또는 최신 major 버전)으로 업그레이드했는지를 가늠할 수 있어요. 이 인사이트는 패키지 작성자가 이전 major 버전으로 수정 사항을 백포트하는 것의 가치를 측정하는 데 도움을 주기 위한 거예요.

pub.dev의 다크 모드는 오랫동안 인기 있는 요청이었는데, 오늘부터 pub.dev를 다크 모드로 전환할 수 있게 됐어요. 이 토글은 패키지 페이지와 pub.dev에 호스팅된 패키지 API 문서 모두에 적용돼요.

작년에는 pub.dev에서 topics를 지원하기 시작했어요. pubspec.yaml에 topics를 추가할 수 있고, 병합해야 할 연관된 topics가 보이면 풀 리퀘스트로 canonicalization을 제안할 수 있어요.

pub.dev를 탐색하는 사용자는 topics를 클릭해서 관련 패키지를 둘러볼 수 있어요. 예전에는 topic:widget 같은 검색 키워드를 써서 pub.dev에서 topics를 검색하는 기능을 도입했는데, 그 키워드를 찾기는 쉽지 않았어요. 그래서 pub.dev에 IDE 같은 검색 키워드 자동 완성기를 출시했어요. ctrl+space를 눌러 트리거하거나, topic:license: 같은 일치하는 접두사를 입력하면 자동으로 트리거돼요.

마무리

오늘 준비한 Dart 3.7 내용은 여기까지예요. 늘 그렇듯 여러분의 생각을 알려주세요! 그리고 오늘 출시된 Flutter 3.29 릴리스 블로그 글도 꼭 확인해 보세요. 다음에 뵈어요!

더 알아보기