< Web 개발 – “Hello, World!” Dart web-only app >
사실 앞서의 글을 통해서, Dart 언어로 웹 브라우저를 통해서 컨텐츠를 제공하는 Web 어플리케이션을 만든 셈 입니다. 앞서 글의 main.dart 소스 코드의 내용을 아래와 같이 “Hello, World!”를 출력하도록 변경합니다.
querySelector(‘#output’).text = ‘Hello, World!’;
그리고, 다시 WebDev를 통해서 해당 Web 어플리케이션을 실행해서 화면 결과가 [그림 1]과 같이 나오는지 확인 바랍니다.
그렇다면, 굳이 “Hello, World!”를 별도의 챕터로 다룬 이유는 무엇 일까요? 바로 Dart로 만든 Web 어플리케이션이 어떻게 Web 브라우저에서 동작하는 지를 이해하기 위함입니다. 이 글은 HTML/CSS/JS에 대한 경험이 일부 있다면, 이해가 용이합니다. 그렇지 않다면, “아 이런 개념이구나” 정도의 큰 그림만 이해하면 되지 않을까 싶습니다.
Dart에서는 개발중인 프로그램은 development 버전, 그리고 개발을 마치고 실제 서비스에 들어가는 프로그램을 deploy(혹은 release)로 구분 합니다. 이렇게 구분하는 이유는 같은 프로그램이지만, Dart에서 적용하는 도구가 다르기 때문 입니다. Dart 언어로 만든 Web 어플리케이션을 기존의 Web 브라우저에서 실행 할 수 있도록 하기 위해서는 Dart 언어로 만든 프로그램을 기존 Web 브라우저가 이해할 수 있는 언어로 표현하는 중간 과정이 필요한데, 이를 위해서 development 단계시에는 dartdevc( https://dart.dev/tools/dartdevc )가 사용되고 deploy를 하여 실제 서비스를 릴리즈(release)하는 단계에서는 dart2js( https://dart.dev/tools/dart2js )가 사용됩니다. 이번 글은 이 두가지 tool을 이해함으로서, 어떻게 Dart 언어로 만든 프로그램을 Web 브라우저들이 이해하고 수행하는지에 대해서 중점적으로 다룹니다.
dart2js는 Dart 코드를 실제 서비스를 운영하는 수준의 JavaScript 코드로 바꿔주는 tool 입니다.dartdevc도 목적은 같지만, 개발 용도로만 사용하는 것으로 권장합니다. 앞서 WebDev는 webdev serve로 실행하는 경우는 dartdevc를 실행하고 (–release 플래그 옵션을 줘서 dart2js를 실행하도록 변경 가능), webdev build로 실행하면 dart2js를 실행합니다.
갑자기 JavaScript에 대한 이야기가 나오니 당황할 수 있습니다. Dart 언어는 새롭게 나온 언어로서, 기존의 Web 브라우저들에 대한 호환성을 고려할 필요가 있었습니다. 이에 Source-to-Source 컴파일 방식을 생각하게 되었습니다. 즉 Dart 언어로 만든 프로그램을 JaVascript 언어 기반 프로그램으로 변환하는 것 입니다. 따라서 대부분의 Web 브라우저들이 JavaScript를 지원하기에, Dart 언어로 만든 프로그램도 궁극적으로 JavaScript를 지원하는 모든 Web 브라우저에서 수행이 가능한 것 입니다. 성능 적인 부분에 대해서 고민을 할 지 모르겠지만, dart2js와 dartdevc로 만들어진 JavaScript 코드들은 최적화된 형태로 만들어진다고 하니, 성능에 관심이 있는 경우는 직접 분석해 보기 바랍니다.
참고로 Desktop에서 다시 설명하겠지만, Dart 언어로 만든 프로그램이 운영체제 위에서 직접 구동하는 Native 방식(프로그램이 Intel/ARM의 시스템 코드(기계어)로 동작한다는 의미)인 경우, 즉 노트북이나 데스크탑 위에서 직접 Dart로 만든 프로그램을 실행하는 경우는 Stand-alone 모드 혹은 Ahead-of-time (AOT) 컴파일 방식을 사용합니다. Stand-alone 방식의 경우는 Dart VM(Virtual Machine)을 사용하는데, Dart SDK에 포함된 Dart VM이 CLI 환경에서 Dart 언어로 만든 프로그램을 실행하는 방식 입니다. AOT 방식은 Dart 코드를 기계언어로 변경하는 방식이며, 사실 앞서 설명한 모바일 앱 들도, 앱 스토어 등재시 AOT 컴파일된 Dart 코드 형태로 배포되게 됩니다.
먼저, dart2js에 대해서 설명 하겠습니다. dart2js를 사용하여, Dart 언어로 만들어진 프로그램을 JavaScript 언어의 프로그램으로 변경하는 명령은 다음과 같습니다.
dart2js -O1 -o target.js source.dart
이 명령은 source.dart 프로그램을 target.js 프로그램으로 변환하라는 의미이며, 이 경우 최적화 옵션은 -O1으로 할당하여 default 모드를 사용한다는 것 입니다. -o 옵션이 output 화일을 지정하는 것으로 “-o target.js”가 하나의 의미로 묶이는 형태 입니다. dart2js를 통해서 만들어지는 화일은 하나가 아니고 복수 개 입니다. 앞서의 “Hello, World!” 프로그램의 main.dart 화일에 대해서 다음과 같이 실행해 봅니다.
dart2js -o target.js main.dart
위의 명령을 실행해 보면, 가장 기본적으로 target.js가 생성되고 부수적인 화일들이 생성된 것을 확인할 수 있습니다. 각각의 화일들의 내용에 관심이 있다면 Googling을 통해서 좀 더 심도 있게 이해하기를 권장 합니다.
최적화 옵션은 -O0의 경우 최적하 하지 않음 이고, -O1이 default 수준의 최적화이고, 그리고 -O2/-O3/-O4는 숫자가 클수록 강화된 최적화를 지원합니다. 최적화에 대해서는 각각의 단계가 어떤 작업을 수행하는지를 명확하게 이해하고, 특별히 문제가 될 수 있는 부분에 대한 설명을 사전에 숙지하여 본인의 프로그램에 적용 시 문제가 없는지를 확인하기 바랍니다.
dart2js를 사용하는 경우를 위해서 Dart 공식 사이트에서는 다음의 문법에 주의하면 더 작고 빠른 JavaScript 코드가 생성된다고 가이드라인을 제시하고 있습니다. 우리가 지나온 내용 안에서 설명이 된 문법도 있고, 다루지 않은 내용도 있으니, 지금은 유념하고, 나중에 본인이 직접 Web 어플리케이션을 전문적으로 개발해야 할 때 참조하기 바랍니다.
- Don’t use Function.apply().
- Don’t override noSuchMethod().
- Avoid setting variables to null.
- Be consistent with the types of arguments you pass into each function or method.
dart2js와 같이, 자동으로 소스 코드를 생성하는 경우는 항상 자동 생성된 코드의 품질에 걱정을 하게됩니다. 이에 대해서 Dart 공식 사이트에서는 자동 생성으로 포함 된 라이브러리의 크기에 대해 걱정하지 않아도 된다고 설명하고 있습니다. 이에 대한 이유를, dart2js 도구는 tree shaking 기법을 수행하여 사용하지 않는 클래스, 함수, 메소드 등을 생략한다고 합니다. 따라서, 개발자가 부담없이, 필요한 라이브러리를 가져 와서 프로그램을 만들면, dart2js가 필요없는 것을 제거하도록 한다고 안심 시키고 있습니다.
dart2js로 생성된 JavaScript 코드가 Web 브라우저에서 실행할 때, 이를 디버그 하고자 한다면, Web 브라우저의 개발자 모드에서 실행이 가능합니다. 이를 위한 Web 브라우저별 설정에 대해서는 Dart 공식 사이트의 dart2js 설명 부분을 참조( https://dart.dev/tools/dart2js#debugging )하기 바랍니다.
dartdevc은 Dart로 만든 Web 어플리케이션을 개발 및 디버그 목적으로 Chomer 브라우저를 통해서 수행하는 경우에 사용합니다.
dart2js와 달리 dartdevc는 incremental 컴파일(프로그램 전체를 컴파일 하는 방식이 아니고, 수정된 프로그램의 일부만 다시 컴파일하는 방식)을 지원하고 modular JavaScript를 생성합니다. dartdevc를 사용하는 webdev serve와 같은 도구를 사용하면, Dart 파일을 수정하고, Chrome을 새로 고침하면, 수정 사항을 거의 즉시 확인할 수 있습니다. 이 속도는 dartdevc가 Web 어플리케이션이 의존하는 모든 패키지가 아니라 업데이트 된 모듈 만 컴파일하기 때문에 가능합니다.
dartdevc를 사용한 첫 번째 컴파일은 전체 Web 어플리케이션을 컴파일 해야 하므로 가장 오래 걸립니다. 그 후, serve 명령이 계속 실행되는 한 dartdevc를 사용한 새로 고침 시간은 dart2js보다 훨씬 빠릅니다. 이러한 이유로 webdev serve의 개발/디버그 모드 동작 시에는 dartdevc를 사용하여 Dart 언어로 만들어진 프로그램을 실행 합니다.
마무리
이제 우리는 앞서 WebDev와 Stagehand를 설치하고 실행한다는 것이 어떤 의미인지를 보다 구체적으로 이해하게 되었습니다. Stagehand를 통해서는 새롭게 시작하는 프로젝트에 필요한 화일들을 자동으로 생성해주는 작업을 합니다. 이를 토대로 만든 Web 어플리케이션은 Web 브라우저와의 호환성으로 인하여 JavaScript로 변환하는 단계를 거치게 되는데, WebDev는 이를 위한 dart2js와 dartdevc 도구를 사용하여 Dart 언어 프로그램을 JavaScript 언어로 변경하고, Web 서버가 기본적으로 수행해야 하는 HTTP 서버의 기능을 수행하여 줍니다.
Creative Commons License (CC BY-NC-ND)