빠르게 메타지식을 흡수해 변환에 적응하는 능력을 지적 겸손이라 한다. 태도의 겸손이 아닌 내가 아는 것이 전부가 아니고 더 낳은 방법과 기술이 있다면 메타지식을 빠르게 흠수할 수 있는 능력이 필요한 시대가 되었다. 새로운 웹 개발언어로 성능과 생산성 증대를 목표로 하는 Dart에 대해 공부를 해본다.
중요 개념
- 모든 변수는 Object 이다.
- 정적 타입과 동적(dynamic) 타입이 가능하다
- 정적 타입 오류를 컴파일 타임에 알려준다
- main() 펑션이 있다
- public, protected, private 같은 것이 없고 _ (underscore)로 표현한다
- warning 은 프로그램이 수행 안될지도 모른다 이지만 수행을 막지는 않는다.
error가 컴파일 타임이 나면 수행할 수 없고 런타임에 발생하면 exception이 발생한다.
- 런타임 모드는 production 과 checked 두가지다. 개발시에는 checked 모드로 사용한다.
production 모드에서는 assert 구문을 무시한 최적화를 통해 성능을 향상시켜준다.
- 키워드
1 : built-in indentifier : 자바스크립트를 다트로 포팅을 쉽게 해준다.
2 : 제한적인 예약어로 비동기 지원과 관련이 있다.
변수 (Variables)
- 예 : 'Peter' 값을 가지는 String 객체의 레퍼런스 주소값을 name이라는 변수가 가지고 있다.
- 최기화 되지 않은 변수는 null 초기값을 갖는다. 숫자가 초기값이 없어도 null 초기값을 갖는다.
assert 구문은 production 모드에서 무시된다. 만일 assert 구문이 true가 아니면 exception을 던진다.
int lineCount;
assert(lineCount == null);
- 옵셔널 타입으로 String name = 'Bob'으로 직접 지정해 줄 수도 있지만 타입 스타일 가이드에 따라 var를 사용한다. swift와 유사함
- final은 변수의 값을 변경하고 싶지 않을 경우 var 대신 사용한다. const는 수치값을 지정한다.
- numbers, strings, booleans, lists, maps, symbols 관련 빌트인 타입을 가지고 있다. (예제 참조)
+ numbers : int, double 있고 이들은 num의 서브타입이다. 다트의 int와 자바스크립트 int는 틀리다. 다트 int는 arbitrary-precision integer
+ strings : UTF-16 코드를 따르고 '' 또는 "" 사용가능. 'hi peter ${expression}' 식으로 문자안에 ${} 표현가능. swift와 유사함
+ booleans : bool 타입으로 true, false를 값을 가져야 하고 자바스크립트와 틀리다. 즉, var name='pter'; if(name)하면 false이다.
+ lists : 다트의 배열은 List 오브젝트이다. 인덱싱은 0부터 시작한다. Generics와 Collections을 살펴보자
+ maps : key-value에 대해 Map 오브젝트를 사용한다. JSON처럼 literal 지정
+ symbols : Symbol 오브젝트를 통해서 operator와 identifier를 표현한다.
펑션 (Functions)
- 예 : 스타일 가이드에 따라 파라미터와 리턴 타입 지정한다.
// 파라미터와 리턴 타입을 명시하는게 스타일 가이드
void printNumber(num number) {
print('this is $number');
}
// 하지만 생략도 가능
printNumber(number) {
print('this is $number');
}
- => expr; 은 { return expr; } 표현과 같다. coffeescript 같은 축약형 좋다. 하기 표현은 위 표현과 동치
printNumber(number) => print('this is $number');
- 옵션널 파라미터
+ 기본 설정 : enable(bool flag1, bool flag2) { ... }
+ Optional named parameter는 paramName: value로 지정
예) enable({bool flag1: false, bool flag2: true}) { ... } 사용 enable(flag1: true)
+ Optional positional parameter는 [] 마크를 사용
예) say(String from, String msg, [String device]) { ... } 사용 say('hi', 'peter') 또는 say('hi', 'peter', 'ios')
- 애플리케이션은 최상위 레벨에서 main() 펑션를 갖는다. 애플리케이션의 진입점역할을 한다. 자바와 유사함
.. 오퍼레이터는 cascade operator이다. 즉, 이어서 호출한다.
void main() {
querySelector('#peter')
..text = 'Hi dowon'
..onClick.listen(reservedText);
- Functions as first-class objects 이다. 펑션을 다른 펑션에 파라미터로 전달하거나 리턴하는 것이 가능. 이건 자바스크립트부터 쭉...
printElm(element) {
print(element);
}
var list = [1,2,3];
list.forEach(printElm); // printElm을 파라미터로 전달
- 자바스크립트는 function 레벨에서 lexical scope를 갖듯 다트도 lexical scope를 갖는다. 즉, 변수의 스코프가 정적으로 정해진다. (단, 자바스크립의 this는 호출되는 시점에 결정되므로 이부분에 틀림. 아래 참조 링크를 보자) 다트는 curly raches outwards를 따른다. (자바스크립트는 변수에 var를 줌으로 로컬 변수 스코프임을 지정한다)
- lexical closures는 lexical scope안의 변수를 접근하는 펑션 오브젝트이다.
A closure is a function object that has access to variables in its lexical scope, even when the function is used outside of its original scope.
- 모든 펑션은 리턴값을 갖고 지정하지 않으면 return null; 이다.
연산자 (Operators)
- 연산자 종류
- 타입 테스트 오퍼레이터
제어 흐름 (Control Flow Statement)
- if(){ ... } else if() { ... } else { ... }
- for ( 초기값 ; 비교 ; 증분 ) { ... } 또는 for in
- while() { ... } 또는 do { ... } while ()
- while에서 break, for 에서 continue
- switch ~ case ~ default
예외처리 (Exceptions)
- 다트의 예외는 unchecked exception 이다.
- 다트는 Exception 과 Error 타입을 제공한다.
- throw 하면 에러를 일으킨다.
- on catch 으로 exception을 catch 하고 전파되는 것을 막는다.
try {
aa();
} on OutofException { // known exception
bb();
} on Exception catch (e) { // anyting else that is an exception
print('exception is: $e');
} catch (e) {
print('really unknown : $e');
}
- finally : finally가 수행된 후에 exception이 전파된다
클래스 (Classes)
- 다트는 객체지향 언어이다.
- new 키워드로 클래스의 인스턴스인 오브젝트를 생성한다.
- 클래스는 펑션과 데이터를 멤버로 갖고 특히 오브젝트의 펑션을 메소드라 부른다. dot(.)로 호출한다. cascade operator(..)은 싱글 객체의 멤버를 연속적으로 호출할 때 사용한다.
- 생성자는 클래스와 같은 이름을 자고 this는 현재 인스턴스를 가르킨다. 자바와 유사
- 생성자를 선언하지 않으면 아규먼트 없는 기본 생성자가 자동 제공됨. 자바와 유사
- 생성자는 상속되지 않는다.
class Point {
num x;
num y;
Point(this.x, this.y);
}
- get, set 키워드를 통해 프로퍼티를 만들 수 있다.
class Rectangle {
num left;
num top;
Rectangle(this.left, this.top);
num get right => left;
set right(num value) => left = value - left;
}
- abstract class를 만들 수 있다. 상속은 extends를 사용함. 자바와 유사
abstract class Door {
void open();
}
- 연산자도 오버라이드 할 수 있음
- 모든 클래스인 암묵적으로 interface를 정의할 수 있다. 만일 A 클래스가 B의 구현체를 상속받지 않고 B의 API만을 사용하고 싶을 경우 implements 키워드를 사용한다. 자바에는 없는 재미난 부분
// A person. The implicit interface contains greet().
class Person {
final _name; // In the interface, but visible only in this library,
Person(this._name); // Not in the interface, since this is a constructor.
String greet(who) => 'Hello, $who. I am $_name.'; // In the interface.
}
// An implementation of the Person interface.
class Imposter implements Person {
final _name = ""; // We have to define this, but we don't use it.
String greet(who) => 'Hi $who. Do you know who I am?';
}
greetBob(Person person) => person.greet('bob');
main() {
print(greetBob(new Person('kathy')));
print(greetBob(new Imposter()));
}
- extends 키워드로 상속을 하고 super는 부모 클래스를 가르킴. 자바와 유사
- 상속시 부모 메소드에 대한 @override를 명시적으로 사용해 오버라이딩이 가능하다.
- 클래스에 @proxy라고 하면 @override시에 warning을 준다.
- 다중 상속시에 mixin 개념의 클래스 코드 재사용이 도입. with 키워드를 사용한다. mixin 할때는 upser, 생성 선언이 없다.
class Musicain extends Performer with Musical, Aggressive, Demented {
...
}
- 클래스 소속 메소드와 변수는 static 키워드를 사용한다. 자바와 유사
제네릭 (Generics)
- 다트에서 타입은 옵셔널이다. 의도를 명확히 하기 위해 공식적으 타입 파라미터를 같는 타입이다. (generic == parameterized) type
예에서 문자로 지정을 한다는 의도를 분명히 한다.
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
// ...
names.add(42); // Fails in checked mode (succeeds in production mode).
- type variables로는 통상 E, T, S, K, V 같은 단일 문자를 갖는다.
- 제네릭은 코드의 중복을 방지해 준다. 상속하여 구현 하면 됨
// Object 경우
abstract class ObjectCache {
Object getByKey(String key);
setByKey(String key, Object value);
}
// String 경우
abstract class StringCache {
String getByKey(String key);
setByKey(String key, String value);
}
// 하나로 줄여줌
abstract class Cache<T> {
T getByKey(String key);
setByKey(String key, T value);
}
- Collection literal에도 사용가능하다.
var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
라이브러리와 가시성 (Libraries and visibility)
- import, part, library 지시자(Directives) 사용한다.
- 라이브러리는 Pub Package and Asset Manager인 pub 툴로 패키지를 사용 배포한다.
- import 'dart:html'; 형식으로 import는 URI 형시그로 라이브러리를 지정한다.
- 빌트인은 'dart:xxx' 로 시작한다.
- pub 툴 배포로 제공되는 것은 import 'package:<file path>'; 로 지정한다. 예) import 'package:utils/utils.dart';
- as로 aliasing이 가능 예) import 'package:lib2/lib2.dart' as lib2;
- show, hide 통해 라이브러리 일부만 가져오기 가능 예) import 'package:lib2/lib2.dart' show foo;
- 라이브러리의 Lazily loading이 가능하다. deferred as 키워드를 사용한다. AngularJS의 $q를 보자
+ 애플리케이션 초기 시작시간을 줄이기 위해 사용
+ A/B 테스트를 수행하기 위해
+ 사용이 적은 것들 로딩할 때
예) import 'package:deferred/hello.dart' deferred as hello; 하기에 then 구문으로 비동기적 호출을 한다.
hello.loadLibrary().then((_) {
hello.printGreeting();
});
- 다른 방법으로는 async 키워드를 사용한다. loadLibrary()는 Future를 리턴한다.
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
- library <라이브러리명칭>; 으로 사용한다. part는 라이브러리의 첨부된 파일을 지칭한다.
library ballgame; // Declare that this is a library named ballgame.
import 'dart:html'; // This app uses the HTML library.
part 'ball.dart'; // ballgame의 일부이다.
비동기 지원 (Asynchrony support)
- async 와 await 키워드를 사용한다.
- 다트 라이브러리는 Future 오브젝트를 리턴하는 펑션셋을 가지고 있다.
- dart 또는 dartanalyzer에서 async 지원을 위해 옵션을 주어야 한다. DartEditor (Eclipse기반)에도 넣어줌
dart --enable-async async_await.dart
dartanalyzer --enable-async async_await.dart
- async 펑션선언
check() async {
//...
}
look() async => //...
- await <expression> 에서 expression은 타입 Future를 가지고 있고 이는 오브젝트를 리턴하기 위한 promise이다. 리턴된 오브젝트가 유효할 때까지 실행은 멈춘다.
Typedefs
- 다트에서 펑션, 문자, 숫자 모두 오브젝이다.
- typedef 또는 function-type alias 는 필드나 리턴타입을 선언할 때 사용할 수 있는 펑션타입에 이름을 준다.
// f를 compare로 할당할 때 int f(Object, Object)의 타입정보가 사라진다.
class SortedCollection {
Function compare;
SortedCollection(int f(Object a, Object b)) {
compare = f;
}
}
// typedef로 펑션타입을 정의한다.
typedef int Compare(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
메타데이터 (Metadata)
- 메타데이터는 추가적인 정보를 주기위해 사용한다. @ 키워드로 애노테이션을 사용한다.
- @deprecated, @override, @proxy 등의 메타데이터 애노테이션을 사용한다.
- 커멘트는 한줄 // 사용하고 다중은 /* */을 사용한다.
참조
- 다트 기초
- 구글 취업이 원하는 인재상 : 지적 겸손에 대하여
- 자바스트립트에서의 lexical scope와 this의 의미 번역 (원문)
- 컴퓨터 사이언스에서 Lexical Scope 위키피디아
- Dart Slides 및 동영상
- Dart VM으로 서버 만들기 Framework 종류