Dart 튜토리얼 27편: JavaScript interop 현대 방식(dart:js_interop·JS types·package:web)

요약#

핵심 요지#

  • 문제 정의: 웹에서는 브라우저 API와 JS 라이브러리를 함께 써야 하는데, Dart 값과 JS 값은 서로 다른 도메인이라 타입/변환/런타임 제약을 모르고 섞으면 오류가 난다.
  • 핵심 주장: next-gen JS interop은 external1 선언과 interop 타입(예: extension type)으로 JS 멤버를 명시적으로 선언하고, JS types(JSString 등)와 변환(toJS/toDart)으로 값 이동을 통제하는 방식이 기준이다.
  • 주요 근거: @JS()2로 글로벌 멤버를 선언하는 예시, extension type로 JS 타입을 표현하는 예시, toJS/toDartInt 변환 예시, package:webdart:html을 대체/마이그레이션하는 예시가 제시된다.
  • 실무 기준: “(1) JS API를 global scope에 노출 → (2) Dart에서 external 멤버 선언 → (3) 변환이 필요한 값만 변환 → (4) 브라우저 API는 package:web로 접근” 순서로 진행한다.

문서가 설명하는 범위#

  • JS interop의 개요와 학습 진입점
  • interop 멤버(external)와 interop 타입(확장 타입 기반) 선언 방식
  • JS types 계층과 변환(toJS/toDart) 및 제약(런타임 타입 체크, external 타입 제한)
  • package:web로 브라우저 API에 접근하고 dart:html에서 마이그레이션하는 이유/절차

읽는 시간: 16분 | 난이도: 초급


참고 자료#


문제 상황#

웹 앱은 브라우저 API와 JS 생태계를 함께 사용합니다.
하지만 Dart 값과 JS 값은 서로 다른 언어 도메인이고, Wasm 타깃에서는 런타임도 분리될 수 있다고 설명됩니다.
그래서 “아무 값이나 넘기기”가 허용되지 않고, 타입과 변환 규칙을 따라야 합니다.


해결 방법#

단계 1: interop는 “글로벌 JS scope 노출 → external 선언”으로 시작한다#

Why#

NOTE

JS 라이브러리를 쓰려면 먼저 JS 쪽에서 사용할 API가 접근 가능한 위치에 있어야 합니다.
그 다음 Dart에서 그 멤버를 선언하고 호출할 수 있습니다.

What#

NOTE

이 방식에서는 “JS API를 global JS scope 어딘가에 노출한 뒤”, Dart에서 external interop 멤버로 접근합니다.
또한 top-level interop 멤버는 @JS()로 표시해야 한다고 설명합니다.

How#

TIP

예시로 JS의 globalThis.name과 함수 isNameEmpty를 만들고, Dart에서 getter/setter/함수로 선언하는 코드가 제시됩니다.

@JS()
external String get name;
@JS()
external set name(String value);
@JS()
external bool isNameEmpty();

Watch out#

WARNING

top-level externaldart:ffi 같은 다른 의미의 external과 구분해야 하므로 @JS()가 필수라는 설명이 제시됩니다.
즉, annotation을 빼면 interop 의도가 전달되지 않습니다.

결론: JS interop는 “JS 글로벌 노출 → Dart @JS() + external 선언” 흐름으로 시작합니다.


단계 2: interop 타입은 extension type로 “JS 값에 붙일 Dart 인터페이스”를 만든다#

Why#

NOTE

JS 값은 구조가 있지만, Dart에서 타입이 없으면 자동 완성/정적 검사/설계 의도가 흐려지기 쉽습니다.
따라서 JS 값에 대응하는 Dart 타입을 만들어두는 것이 유리합니다.

What#

NOTE

interop 타입은 Dart가 제공하는 JS 타입(JSObject 등)을 감싸는 extension type로 선언할 수 있다고 설명됩니다.
또한 “런타임에서 실제 Window라는 보장”이 없다는 점도 함께 설명됩니다.

How#

TIP

다음은 JSObject를 표현 타입으로 갖는 interop 타입 예시입니다.

extension type Window(JSObject _) implements JSObject {}

Watch out#

WARNING

런타임 보장이 없으므로, 특정 JS 타입임을 가정하고 사용하면 위험할 수 있습니다.
따라서 필요한 경우 interop를 통해 타입을 확인하는 방법을 별도로 사용해야 한다는 안내가 있습니다.

결론: interop 타입은 “타입 안정성/가독성”을 위해 만들되, 런타임 보장 여부를 구분해야 합니다.


단계 3: JS types와 toJS/toDart 변환으로 “값 이동”을 통제한다#

Why#

NOTE

값을 Dart↔JS로 넘기는 순간, 성능과 의미(참조/복사) 문제가 생길 수 있습니다.
그래서 변환은 “필요할 때만” 명시적으로 수행하는 기준이 필요합니다.

What#

NOTE

JS types는 JS 접두사를 가진 타입(JSString, JSNumber 등)이며, Dart 값과 JS 값을 컴파일 시점에 구분하기 위한 체계로 설명됩니다.
변환은 Dart→JS는 toJS, JS→Dart는 toDart... 형태로 제공된다고 설명됩니다.

How#

TIP

변환 예시는 다음과 같이 제시됩니다.

String str = 'hello world';
JSString jsStr = str.toJS;
JSNumber jsNum = ...;
int integer = jsNum.toDartInt;

Watch out#

WARNING

JS↔Dart 변환은 컴파일 타깃(JS vs Wasm)에 따라 비용/의미가 달라질 수 있고, List 같은 값은 참조/프록시/복사 여부를 가정하지 말라고 경고합니다.
즉, 변환 후 두 값 사이의 연결 관계에 기대지 말아야 합니다.

결론: 값 이동은 toJS/toDart로 통제하고, 변환의 비용/의미 차이를 전제로 코드를 작성합니다.


단계 4: 브라우저 API는 package:web로 접근하고, dart:html에서 마이그레이션한다#

Why#

NOTE

Wasm 타깃을 고려하면, 기존 dart:html 같은 코어 웹 라이브러리는 제약이 될 수 있습니다.
그래서 장기적으로는 package:web로 이동하는 방향이 제시됩니다.

What#

NOTE

package:web는 브라우저 API 접근을 제공하고, Wasm 호환을 위해 dart:js_interop 기반으로 설계되었다고 설명됩니다.
또한 dart:html 등은 deprecated이며 Wasm에서 지원되지 않는다고 안내합니다.

How#

TIP

기본 사용 예시는 다음과 같이 제시됩니다.
코드 블록 주석은 한글로 맞췄습니다.

import 'package:web/web.dart';
void main() {
final div = document.querySelector('div')!;
div.text = 'Text set at ${DateTime.now()}';
}

마이그레이션은 dart:html import를 제거하고 package:web/web.dart로 바꾸는 흐름이 제시됩니다.

import 'dart:html' as html; // 제거
import 'package:web/web.dart' as web; // 추가

Watch out#

WARNING

package:webList를 그대로 반환하지 않는 API가 있고, 런타임 타입 테스트(is)가 dart:html과 다르게 동작할 수 있다는 안내가 있습니다.
즉, 마이그레이션은 import만 바꾸는 작업이 아니라, 타입/컬렉션/테스트 방식까지 함께 바꾸는 작업이 될 수 있습니다.

결론: 브라우저 API 접근은 package:web로 표준화하고, 마이그레이션 시 타입/리스트/테스트 차이를 함께 처리합니다.

Footnotes#

  1. external(external): 구현이 Dart에 없고 외부(interop/FFI)로 연결되는 멤버를 선언하는 키워드다.

  2. @JS()(@JS): JS interop 멤버/타입임을 표시하는 annotation이다.

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Dart 튜토리얼 27편: JavaScript interop 현대 방식(dart:js_interop·JS types·package:web)
https://moodturnpost.net/posts/dart/dart-js-interop-modern/
작성자
Moodturn
게시일
2026-01-04
Moodturn

목차