[Dart] What is Dart


다트 문법 온라인 메뉴얼

다트 언어 연습환경

dartpad.dev

다트 문법은 main() 함수가 entry point이다.

void main() {
    print("Hello,World!");
}

변수

기본타입

  • int
  • double
  • String
  • bool

  • num 타입

int, double은 num 타입에 포함된다.

다트에서는 자동 형변환을 지원하지 않음. 반면 num타입에는 int, double 타입 모두 대입할 수 있음.

타입 추론

다트는 타입을 직접 명시하지 않고 var로 대체할 수 있는 타입 추론을 지원함.

var i = 10;
var d = 10.0;
var s = "hello";
var b = true;

상수

final String name = "임수빈"
// 타입생략 가능
final name = "임수빈"

산술 연산자

  • +
  • -
  • *
  • /
  • ~/ (몫 int타입)
  • % (나머지 int 타입)

타입 검사

  • is (같은 타입이면 true)
  • is! (다른타입이면 true)
int a = 10;
if(a is int) {
    print('정수');
}

String text = 'hello;
if(text is! int) {
    print('숫자가 아님');
}

형변환

같은 타입끼리 변환이 안 되고 더 상위 개념으로 변환할 수 있다.

// int와 double은 모두 num을 구현하는 타입이지만 서로는 관계가 없기 때문에 형변환 불가능
var c = 30.5;
int d = c as int; // error

dynamic d = 30.5;
num n = d; // as num 생략 가능

함수

함수형태
int f(int x) {
    return x + 10;
}

void main() {
    var result = f(10);
}

// 반환 타입 역시 타입 추론에 의해 생략 가능
void greaeting(String greet) {
    print('hello $greet');
}

// 위 코드와 동일
greeting(String greet) {
    print('hello $greet');
}

변수 앞에 $ 기호를 붙여 문자열 내에 변수를 삽입할 수 있다.

String _name = '임수빈'
int _age = 20;

void main() {
    print('$_name$_age살입니다.');
    print('$_name${_name.length} 글자입니다.);
    print('10 후에는 ${_age + 10}살입니다.);
}
함수, 메서드

main 메소드 처럼 가장 바깥에 작성한 함수는 어디에서나 호출할 수 있는 함수이며, 이 책에서 함수라고 칭하는 대부분은 이러한 최상위 함수를 가리킴.

클래스 내부에 작성하는 함수를 method라고 부르며, 정의된 클래스에 관계된 기능을 수행함.

큰 개념에서 함수와 메서드는 모두 함수이다. 클래스에 정의된 함수를 메서드라 구분지어 부를 뿐이다.

클래스 내부에 선언된 함수이더라도 static 키워드를 붙이면 정적 메서드가 되며 함수로 볼 수 있다. static 키워드가 붙은 함수는 최상위 함수처럼 사용할 수 있다.

class MyClass {
    //정적 메소드, 함수
    static bool isEven(int number) {
        return number % 2 == 0;
    }
}
...
print(MyClass.isEven(10));
익명 함수
([인수명]) { [동작 또는 반환값]}
(number) {
    return number %2 == 0;
}
람다식
([인수명]) => [동작 또는 반환값]
(number) => number % 2 == 0;
선택 매개 변수

함수 정의에서 {}로 감싼 매개변수는 선택적으로 사용할 수 있다.


void something(String name, {int age}) {}

void main() {
    something('홍길동',age: 10); // ok
    something('홍길동'); // ok
    something(age: 10); // error
    something(); //error
}

// 선택 매개변수는 기본값을 지정할 수 있다.
void something(String name, {int age=10}){}

분기와 반복

if else
String text 'hello';

if (text is int) {
    print('정수')
} else if (text is double) {
    print('실수');
} else {
    print('정수도 실수도 아님');
}
삼항 연산자
[조건] ? [참일 때] : [거짓일 때]
var todo = isRainy ? '빨래를 하지 않는다' : '빨래를 한다';
Switch case

조건에 맞는 값이 여러 개 일 때 유용한 문법.

특히 열거(enum) 타입과 함께 사용할 때는 모든 케이스를 검사해야 하는 강제성이 생김.

사람의 실수를 방지하는 이런 기능이 있어서 특수한 경우에는 if문 보다 유용함.

enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated}

void main() {
    var status = Status.Authenticated;
    swtich(Status) {
        case Status.Authenticated:
            print('인증됨');
            break;
        case Status.Authenticating:
            print(인증 처리 );
            break;
        case Status.Unauthenticated:
            print('미인증');
            break;
        case Status.Uninitialized:
            print('초기화');
            break;
    }
}
for
var items = ['짜장','라면','볶음밥'];

for(var i =0; i<items.length; i++) {
    print(items[i]);
}

Class

플러터 코드를 작성할 때 new 키워드는 일반적으로 생략함

var person = new Person();

var person2 = Person();
접근 지정자

변수명 앞에 _ 기호를 붙이지 않으면 외부에서 접근 가능하고, 붙이면 접근 불가능합니다.

class Person {
    String name;
    int _age;

    void addOneYear() {
        _age++;
    }
}

import 'person.dart';

...

var person = Person();
person._age = 10; //error

생성자

사용자 정의 생성자를 추가하면 기본 생성자를 사용할 수 없게 되지만, 선택 매개변수를 사용하면 Person()도 호출할 수 있다.

(기본 생성자를 호출한 것은 아니며, 모든 매개변수에 null을 대입한 생성자를 호출함.)

class Person {
    String name;
    int _age;

    Person({this.name, this._age});
}

var person = Person();
var person2 = Person(name: '홍길동',_age:20);

Getter, Setter

class Person {
    String name;
    int _age;

    int get age => _age;
}

import 'person.dart'

var person = Person();
print(person.age);

믹스인

with 키워드를 사용하면 상속하지 않고 다른 클래스의 기능을 가져오거나 오버라이드할 수 있다.

class DarkGoblin extends Goblin with Hero {

}

열거 타입

Enum type은 상수를 정의하는 특수한 형태의 클래스.

로그인과 로그아웃 두가지 상태만 있는 시스템에서는 사용할 상태를 열거 타입을 정의하면 유용함.

enum Status {login, logout}

Switch 문과 함께 사용하면 열거 타입으로 정의된 모든 상수를 case로 검토하도록 강제하기 때문에 에러방지하는 효과도 있다.

switch(authStaus) {
    case Status.login:
        print('로그인');
        break;
    case Satus.logout:
        print('로그아웃');
        break;
}

컬렉션

다트는 List, Map, Set등의 컬렉션을 제공

List

순서가 있는 자료를 담는 컬렉션

  • 다트는 배열을 제공하지 않는다.
    List<String> items = ['짜장','라면','볶음밥'];
    
은 리스트에 담길 데이터 타입을 지정한 것이다. 하지만 컬렉션도 타입 추론을 사용할 수 있어 일반적으로는 var로 선언한다. ```dart var items = ['짜장', '라면', '볶음밥']; ``` ** dynamic 모든 타입을 대변하는 dynamic이라는 특수한 타입이 있다. 여러 타입을 한 리스트에 넣거나 일반 변수를 선언할 때도 사용할 수 있다. (이때도 명시적으로 List을 쓰는 대신 var를 사용할 수 있다.) ```dart var list = [1,2,4,'헬로']; List list = [1,2,4,'헬로']; ``` #### 스프레드 연산자 ... 연산자는 컬렉션을 펼쳐주는 연산자로 스프레드 연산자라고함. ```dart var items = ['짜장','라면','볶음밥']; var items2 = ['떡볶이', ...items, '순대']; ``` 다음과 같이 리스트를 Set에 담게 되면 자동으로 중복 제거의 효과도 얻을 수 있다. ``` final items = [1,2,2,3,3,4,5]; final myNumbers = {...items, 6, 7}; ``` #### Map 순서가 없고 탐색이 빠른 자료구조 컬렉션 key-value의 쌍으로 이루어져 있어 키를 이용하여 값을 빠르게 얻을 수 있다. ```dart var cityMap = { '한국':'부산', '일본':'도쿄', '중국':'북경' } ``` #### Set 집합을 표현하는 자료구조 컬렉션 add(), remove() 메서드로 집합에 추가하거나 삭제. contains() 메소드가 찾곶다 하는 자료가 집합에 있는지 없는지 불리언 타입으로 반환하기 때문에 집합에 요소가 있는지 검사할 떄 사용. ```dart // Set citySet = {...} var citySet = {'서울','수원','오산','부산'}; citySet.add('안양'); citySet.remove('수원'); citySet.contains('서울') // true ``` ** 빈 Set, 빈 Map을 작성할 때는 문법을 조심해야함. 값없이 {}만 작성하면 Set이 아닌 Map으로 인식함. ```dart // Set mySet = {} var mySet = {}; // Map<dynamic,dynamic> var mySet2 = {}; ``` ### 함수형 프로그래밍 다트는 객체 지향 프로그래밍과 함수형 프로그래밍의 특징을 모두 제공함. ** 함수형 프로그래밍은 자료처리를 수학적 함수의 계산으로 취급하는 프로그래밍 패러다임임. (상태와 가변 데이터를 기피함.) #### 일급 객체 다트에서는 함수를 값으로 취급할 수 있다. 그러므로 다른 변수에 함수를 대입 할 수 있다. ```dart void greeting(String text) { print(text); } void main() { var f = greeting; f('hello'); } ``` 다른 함수의 인수로 함수 자체를 전달하거나 함수를 반환받을 수도 있다. ```dart void something(Function(int i) f) { f(10); } void main() { something((value) { print(value); }) } ``` 함수를 매개변수로 전달하기, 수정하기, 변수에 대입하기가 가능한 객체를 일급 객체라고 함. ```dart void something(Function(int i) f) { f(10); } void myPrintFunction(int i) { print('내가 만든 함수에서 출력한 $i'); } void main() { something(myPrintFunction); something((i)=>myPrintFunction(i)) sometinh((i)=> print(i)); something(print); } ``` #### forEach forEach() 함수는 내부 반복을 수행함. ```dart items.forEach(print); // 익명 함수로 표현 items.forEach((e) { print(e); }); // 람다로 표현 items.forEach((e)=>print(e)); ``` #### where 조건을 필터링할 떄 사용하는 함수. ```dart final items = [1,2,3,4,5]; for(var i = 0; i < itesm.length; i++) { if(items[i] % 2 == 0) { print(items[i]); } } ``` ```dart items.where((e)=> e % 2 == 0).forEach(print); ``` #### map ```dart final imtes = [1,2,3,4,5]; for (var i = 0; i < items.length; i++) { if(items[i]%2 == 0) { print('숫자 ${items[i]}'); } } ``` ```dart items.where((e)=> e % 2 == 0).map((e) => '숫자 $e').forEach(print); ``` #### toList 다트에서 함수형 프로그래밍을 지원하는 함수 대부분은 Iterable라는 인터페이스 타입 인스턴스를 반환한다. 하지만 실제 사용할 떄는 대부분 리스트 형태로 반환해야하는 경우가 많다. ```dart final result = []; items.forEach((e){ if(e%2==0) { result.add(e); } }); ``` ```dart fianl result = items.where((e)=> e % 2 ==0).toList(); ``` #### toSet ```dart final items = [1,2,2,3,3,4,5]; var result = []; for(var i = 0; i <items.length; i++>) { if(items[i]% 2 == 0) { result.add(items[i]); } } print(result); ``` ```dart final result = items.where(e) => e%2 ==0).toList(); ``` ```dart fianl result = items.where((e)=> e% 2==0).toSet().toList(); ``` #### any any 함수는 리스트에 특정 조건을 충족하는 요소가 있는지 없는지 검사할 때 사용함. ```dart final items =[1,2,2,3,3,4,5]; var result = false; for(var i =0; i < items.length; i++>) { if(items[i]%2 == 0) { result = true; break; } } print(result); ``` ```dart print(items.any((e)=> e% 2 == 0)); ``` #### reduce 반복 요소를 줄여가면서 결과를 만들 때 사용하는 함수. reduce() 함수를 연산 결과를 다음 요소와 연산한다. [타입]([타입] value, [타입] element) 혇태의 함수를 정의 해야함. max()함수는 정수인 두수 (e,v)를 받아서 큰 수를 반환함. ```dart final items = [1,2,3,4,5]; print(items.reduce((e,v)=> max(e,v))) ``` ```dart final result = items.reduce(max); ``` ### 기타 유용한 기법 #### 계단식 표기법 ..연산자 ..연산자를 사용하면 동일 객체에서 일련의 작업을 수행할 수 있다. 컬렉션의 add()메소드는 void를 반환하고 remove()메소드는 bool을 반환하지만 ..연산자를 사용하면 메소드를 수행한 객체의 참조를 반환함. 임시 변수를 만드는 단계가 절약되고 더 유동적인 코드를 작성할 수 있다. ```dart final items =[1,2,3,4,5]; var result = items; result.add(6); result.remove(2); ``` ```dart print(items..add(6)..remove(2)); ``` #### 컬렉션 if 다트에서는 컬렉션 내부에 if문이나 for문을 사용할 수 있다. ```dart bool promoActive = true; print([1,2,3,4,5, if(promoActive) 6]); ``` #### 컬렉션 for ```dart var listOfInts = [1,2,3]; var listOfStrings = [ '#0', for(var i in listOfInts) '#$i'; ] ``` #### null 처리에 관한 기능 다트에서는 모든 것이 객체이다. ㅅ미지어 int, double, bool 같은 타입도 모두 클래스이다. 이러한 타입들은 모두 null 값을 가질 수 있다. ```dart if(name != null) { print(name.length) } ``` ?. 연산자를 사용하면 null 여부를 간단히 판단할 수 있다. 객체 프로퍼티나 메서드에 접근할 때 . 연산자 대신 ?. 연산자를 사용하면 객체가 null 일때 에러를 발생하는 대신 null을 반환한다. ```dart print(name?.length); ``` ?? 연산자는 객체가 null일 때 작동을 간단히 구현하는데 사용 ```dart print(name?.length ?? 0); ```