Dart 공부하기 - 1일차
포스트
취소

Dart 공부하기 - 1일차

Flutter 베이스 언어인 Dart 에 대해 공부한다. Dart 는 완전한 OOP를 지원한다. (1일차)

0. 시작하기에 앞서

메타 프로그래밍을 위해 Svelte 를 선택했는데, React 에 비해 예제와 문서가 적어 고생을 하는 중이다. 하지만 후회는 안함. 문제는 애플리케이션 개발을 위해 BaaS 도구를 다루어야 하는데, 프로그래밍 방식에 익숙치 않아 더 다양한 예제가 필요한 상황이다.

Flutter 는 다양한 라이브러리에서 지원하고 코드가 많기 때문에 Firebase, Supabase 등과 연결된 예제가 많다. 플러터를 중심으로 애플리케이션의 메카니즘을 능숙하게 익히고 다시 Svelte 로 돌아가려 한다.

Nomad 의 니꼬쌤이 말한대로, Flutter 를 잘 다루기 위해서는 구현 언어인 Dart 에 대한 기본을 탄탄히 하는게 러닝커브를 줄이는 방법이라 Dart 공부부터 시작한다.

1. Dart 언어 (2023년 2월 기준 최신버전 2.19.1)

1) 특징

  • Dart VM 으로 멀티플랫폼 지원 : 윈도우, 리눅스, 맥(amd, arm)
  • 모바일, 데스크톱, 서버, 웹 앱 용도에 사용됨 (다목적)
  • 구글에서 지원, 2011년 10월에 공개 (충분한 기간동안 성숙됨)
  • 다양한 예제와 라이브러리, 커뮤니티 지원이 강점
  • Javascript 를 대체할 목적이라, Typescript 특징을 대부분 커버함
    • Python, Deno 라던지, Go 언어 등의 현대적 특징을 포함

2) 핵심특징 5가지

  • 모든 것들이 Object 로 취급됨 (Fuction, number, null 전부 Objcet)
  • List<int>, List<dynamic> 같은 제너릭 type을 지원
  • Typed 언어지만, 자유도를 준다. (형식 추정)
  • 클래스 멤버의 (파일 범위) private 정의가 없으며, _ 식별자 사용
  • Identifier는 _ 아니면 letter로 시작된다. (낙타 표기법)

3) Dart 주요기능

  • 두 가지 컴파일 방법 지원 (JIT + AOT)
  • 핫 리로드 : 빠른 개발 가능
  • 초당 60 프레임의 훌륭한 애니메이션
  • 선제적 스케줄링, 타임 슬라이싱 및 공유 리소스
  • Lock 없이 객체 할당 가비지 수집 가능
  • 선언적인 방식의 레이아웃

3) 참고

2. Dart 튜토리얼

console 환경에서 프로젝트를 구성하고 예제를 따라해본다.

1) Console 프로젝트 구성

  • main 함수와 lib 파일을 임포트하는 기본 구성을 생성
    • pubspec.yaml 에 프로젝트 구성정보를 설정
    • bin/{프로젝트명}.dart 을 생성
  • 멀티플랫폼에 맞는 컴파일을 통해 실행파일을 생성
    • bin/{프로젝트명}.exe 을 생성
1
2
3
4
5
6
7
8
9
10
11
$ dart create -t console cli

$ cd cli

$ dart run
Hello world: 42!

$ dart compile exe bin/cli.dart

$ time bin/cli.exe
Hello world: 21!
  • 외부 패키지 추가 : pubspec.yaml 의 dependecies 에 명시됨
    • 반대로 pubspec.yaml 에 기술 후, dart pub get 해도 됨
1
2
3
4
5
$ dart pub add vector_math
Resolving dependencies... 
+ vector_math 2.1.3
Downloading vector_math 2.1.3...
Changed 1 dependency!

프로젝트 템플릿 (-t 옵션)

  • console (디폴트) : 커맨드라인 애플리케이션
  • package : 공유 라이브러리
  • server-shelf : 서버 애플리케이션
  • web : 웹 애플리케이션

수동으로 프로젝트 생성하기

  1. mkdir hello && cd hello
  2. vi pubspec.yaml : name 과 sdk 를 명시해야 함
  3. dart pub get : 필요한 라이브러리 구성 (golang 의 get 과 유사)
  4. mkdir bin && vi bin/hello.dart : 애플리케이션 시작 파일
  5. dart run : 시작 파일을 실행

dart run 으로 파일명을 명시하고 실행해도 된다.

2) 튜토리얼 코드

  • Importing User libraries
  • Variables 선언
  • Control flow statements : if 문, for 문, while 문
  • 가로 연결 출력을 하려면 (print 대신에) stdout.write 사용
  • 파라미터를 List<String> arguments 로 받음
  • 익명함수(Anonymous function)
  • Class, Enum, Function 등
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import 'package:cli/cli.dart' as cli;
import "dart:io";

// Importing User libraries
import 'package:cli/class.dart' as cls;
import 'package:cli/enum.dart' as enm;

void main(List<String> arguments) {
  // Hello world
  print('Hello world: ${cli.calculate()}!');

  // Variables
  var name = 'Voyager I';
  var year = 1977;
  var antennaDiameter = 3.7;
  var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
  var image = {
    'tags': ['saturn'],
    'url': '//path/to/saturn.jpg'
  };

  // Control flow statements
  if (year >= 2001) {
    print('21st century');
  } else if (year >= 1901) {
    print('20th century');
  }

  print('${'--' * 20}\nfor .. in:');
  for (final object in flybyObjects) {
    stdout.write('$object, ');
  }
  print('\n${'--' * 20}\nfor .. condition:');
  for (int month = 1; month <= 12; month++) {
    stdout.write('$month, ');
  }
  print('\n${'--' * 20}\nwhile .. condition:');
  while (year < 2016) {
    year += 1;
  }

  print('\n${'--' * 20}\nfunction call:');
  var result = fibonacci(20);
  print('Fibonacci of 20 is "$result"');

  // Anonymous function
  flybyObjects.where((name) => name.contains('turn')).forEach(print);

  // Classes
  var voyager = cls.Spacecraft('Voyager I', DateTime(1977, 9, 5));
  voyager.describe();
  var voyager3 = cls.Spacecraft.unlaunched('Voyager III');
  voyager3.describe();

  final yourPlanet = enm.Planet.earth;
}

// Functions
int fibonacci(int n) {
  if (n == 0 || n == 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

클래스 관련 항목

  • Class
  • Enum
  • Inheritance
  • Mixins
  • Interfaces and abstract classes

비동기 Async / Await

파일을 읽는 동안 다른 dart 코드가 CPU 를 사용하도록 함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() async {
  // Read some data.
  final fileData = await _readFileAsync();
  final jsonData = jsonDecode(fileData);

  // Use that data.
  print('Number of JSON keys: ${jsonData.length}');
}

Future<String> _readFileAsync() async {
  final file = File(filename);
  final contents = await file.readAsString();
  return contents.trim();
}

Example: Incorrectly using an asynchronous function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Future<void> printOrderMessage() async {
  print('Awaiting user order...');
  var order = await fetchUserOrder();
  print('Your order is: $order');
}

Future<String> fetchUserOrder() {
  // 3초 후에 문자열 반환
  return Future.delayed(const Duration(seconds: 3), () => 'Large Latte');
}

void main() async {
  countSeconds(4); // 4초 동안 카운트
  await printOrderMessage();
}

void countSeconds(int s) {
  for (var i = 1; i <= s; i++) {
    Future.delayed(Duration(seconds: i), () => print(i));
  }
}

// 실행 결과
// Awaiting user order...
// 1
// 2
// 3
// Your order is: Large Latte
// 4

(이벤트 루프같은) Isoate.run 과 클로저

Isolate.run 안에 Future 함수를 넣거나, 클로저 함수를 바로 넣을 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main() async {
  // Read some data.
  final jsonData = await Isolate.run(() async {
    final fileData = await File(filename).readAsString();
    final jsonData = jsonDecode(fileData) as Map<String, dynamic>;
    return jsonData;
  });

  // Use that data.
  print('Number of JSON keys: ${jsonData.length}');
}

Future<Map<String, dynamic>> _readAndParseJson() async {
  final fileData = await File(filename).readAsString();
  final jsonData = jsonDecode(fileData) as Map<String, dynamic>;
  return jsonData;
}

Exception 처리 : try … on .. catch .. finally

1
2
3
4
5
6
7
8
9
10
11
try {
  //code that has potential to throw an exception
} on SomeException {
  //code
} on AnotherException {
  //code
} catch(e) {
  //code
} finally {
  //code
}

3. 샘플 및 자습서

1) null 세이프티

  • 타입+? : nullable 선언 (var 는 안되고 타입 선언만)
  • ??= : null 할당 연산자
  • ?? : null 병합 연산자
  • .? : 선택적 연결 연산자
1
2
3
4
int? a = 5;

int b = a; // Not allowed.
int b = a!; // Allowed.

2) 선언

  • ? 으로 nullable 선언하거나 또는 기본값 초기화
    • ex) int? lineCount;, int lineCount=0;
  • Late variables : 우선 선언하고 나중에 초기화
    • ex) late String descriptioin;
  • final (변경불가), const (컴파일 타임 상수)
    • ex) final name = 'Bob';, const bar = 1000;

3) 내장타입

  • 숫자 : int, double, bool
  • 문자열 : String (UTF-16 코드 사용)
    • 객체 출력시 객체의 toString 멤버 함수를 호출함
    • 다중 문자열 기술할 때는 삼중 따옴표 (``` 또는 “””) 을 사용
    • 원시 문자열, ex) var s = r'line \n next line';
  • 배열 : List
    • ex) var list = const [1,2,3];
    • ... 연산자로 배열을 삽입, nullable 의심되면 [...?list]
  • 집합 : Set
    • ex) var halogens = {'A','B'}
  • 맵 : Map
    • ex) var gift = {1:'first', 2:'second'};
  • 룬 : Runes, characters
    • 개별 유니코드 문자를 읽거나 쓸 때 characters 패키지를 이용해야 함
    • ex) var hi = 'Hi 🇩🇰'; print('${hi.characters.last}');
  • 기호(#) : Symbol
  • null 값 : Null

4) 매개변수

선택적 위치 매개변수 [ .. ]

1
2
3
4
5
6
7
8
9
10
multiply(int a, [int b = 5, int? c]) {
  ...
}

main() {
  // All are valid function calls.
  multiply(3);
  multiply(3, 5);
  multiply(3, 5, 7);
}

명명된 매개변수 { .. }

1
2
3
4
5
6
7
multiply(bool x, {required int a, int b = 5, int? c}) {
  ...
}

multiply(false, a: 3); // Only provide required named parameters
multiply(false, a: 3, b: 9); // Override default value of `b`
multiply(false, c: 9, a: 3, b: 2); // Provide all named parameters out of order

5) 제너레이터 yield

1
2
3
4
5
6
7
8
9
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) {
    yield k++;
  }
}

// Returns an iterable with [0, 1, 2, 3, 4]
print(naturalsTo(5).toList());

6) 비동기 프로그래밍 Streams

  • 스트림은 이벤트 또는 데이터의 비동기 시퀀스를 제공
  • 스트림 이벤트 수신 : await for
  • 스트림은 단일 구독 또는 브로드캐스트 두 종류가 있음

Java 의 Stream 과 유사하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    yield i;
  }
}

void main() async {
  var stream = countStream(10);
  var sum = await sumStream(stream);
  print(sum); // 55
}

9. Review

  • 언어들이 서로 베끼기를 해서 짬뽕이다. 유사하면서도 헷갈린다.
    • Java, Kotlin, Go, Python, Typescript 등등의 혼합
  • Javascript 와의 유사점을 비교하며 설명한 글이 도움이 된다.
  • Flutter 사용 사례를 바탕으로 Dart 언어를 계속 발전시키고 있다.
    • 계속 본다고 해도 끝이 안날듯
    • Flutter 를 사용하며 부족한 문법을 살펴보는게 낫겠다.

 
 

끝!   읽어주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.