코드 팩토리의 플러터 프로그래밍 1장, 다트 입문하기
"이 글은 골든래빗 《코드 팩토리의 플러터 프로그래밍》의 01장 써머리입니다."
다트 소개
히스토리
- 구글이 개발
- 2011년 10월 공개
- 크롬에 Dart Virtual Machine 을 도입해 JS 를 대체하려 했으나 실패함
특징
- UI 제작에 최적화
- Isolate 를 이용한 동시성 기능 제공
- Null Safety
- Spread Operator
- Collection If
- 핫 리로드
- 완전한 비동기 언어
- 이벤트 기반
- VM 기반이라 멀티 플랫폼에서 로깅, 디버깅, 실행 가능
- AOT 컴파일이 가능해서 어떤 플랫폼에서든 빠름
- 자바스크립트로의 완전한 컴파일 지원
- 백엔드 프로그래밍 지원
다트의 컴파일 플랫폼
- JS 로 완전 컴파일이 가능
- 증분 컴파일을 지원
개발 시
- 네이티브 x64/ARM
- JIT + VM
- 웹 자바스크립트
- dartdevc
배포 시
- 네이티브 x64/ARM
- AOT + 런타임
- 웹 자바스크립트
- dart2js
다트 플레이그라운드
엔트리 포인트
main()
함수가 엔트리 포인트가 된다.- 자바랑 다른 점은 클래스 내부에 있지 않아도 된다는 것
- Flutter 에서는 보통
main()
에서 앱을 초기화하고 초기 루트 위젯을 설정하는runApp()
함수를 실행한다.
변수 선언
var
var name = 'Jake Seo';
- 타입 추론 (Type Inference): 변수에 들어가는 초기 값을 기준으로 변수의 타입이 추론되어 결정된다.
- 불변성 (Immutability): 컴파일 될 때, 추론된 타입으로 var 가 치환된다.
- 이 말은 var 로 선언한 변수의 타입이 일단 추론되면, 컴파일 시에 실제 타입으로 변경됐으니 더이상 타입을 변경할 수 없다는 것이다.
dynamic
dynamic name = 'Jake Seo';
name = 1
- 동적 선언: 변수의 타입이 고정되지 않아 계속 다른 타입의 값을 저장할 수 있다.
직접 타입 명시하기
String name = 'Jake Seo';
int age = 50;
double weight = 101.11;
bool isMale = true;
- 타입을 직접 명시해주면 코드가 더욱 직관적이어서 알아보기 쉽다.
별 일 없는 한 이 방식을 제일 많이 쓰게 될 것 같다.
상수 선언
final
final String name = 'final name.';
name = 'I can not change final name.'; // ERROR
final
로 선언된 것은 상수이기 때문에 변경할 수 없다.
const
const String name = 'const name.';
name = 'I can not change const name.'; // ERROR
const
로 선언된 것은 상수이기 때문에 변경할 수 없다.
final
(런타임) 과 const
(빌드 타임) 비교
DateTime.now()
는 런타임에 순간의 날짜/시간을 제공해야 하기 때문에 런타임에 값이 생성된다.final
과const
는 각각 런타임에 생성되는 값을 넣을 수 있냐 없냐로 갈린다.
final DateTime now = DateTime.now();
final
은 런타임에 알 수 있는 값을 넣을 수 있다.final
에 들어갈 값은 빌드 타임에 값을 알 수 없어도 된다.
const DateTime now = DateTime.now(); // ERROR
const
는 런타임에 알 수 있는 값을 넣을 수 없다.const
에 들어갈 값은 빌드 타임에 값을 알 수 있어야 한다.
나라면 일단
const
를 최대한 활용하고 안되는 곳에 마지못해final
을 쓸 것 같다.
컬렉션
- 여러 값을 단일 단위로 그룹화하는 컨테이너 역할을 하는 타입이다.
- ex)
List
(나열) ,Map
(키, 값 매칭),Set
(중복 제거) - 서로 형변환이 가능하다는 것이 장점이다.
리스트 (List)
리스트[인덱스]
형식으로 원소에 접근이 가능- 인덱스는 0부터 시작
List.length
로 길이를 알 수 있다.
List<String> alphabets = ['a', 'b', 'c'];
print(alphabets[1]); // 'b'
print(alphabets.length); // 3
리스트 유틸 메서드
add()
,where()
,map()
,reduce()
가 있다.add()
와where()
은 자바스크립트로 치면 각각push()
,filter()
에 대응된다.map()
,reduce()
,where()
은 자바스크립트처럼 인수로 값이 아니라 콜백 함수를 받는다.
where()
where()
의 결과는List
가 아니라Iterable
타입임에 유의해야 한다.Iterable
은List
와Set
과 같은 컬렉션이 상속받는 추상 클래스이다.
Iterable.toList()
메서드를 통해 리스트로 변환할 수 있다.
List<String> alphabets = ['a', 'b', 'c'];
final b = alphabets.where((alphabet) => alphabet == 'b');
print(b); // (b) -> 다트에서 Iterable 을 표현할 때는 () 로 감싸서 표현된다.
print(b.toList()); // [b] -> 다트에서 List 를 표현할 때는 [] 로 감싸서 표현된다.
map()
- 마찬가지로
Iterable
이 반환된다. $
는 자바스크립트에서 Template Literal 이 하는 역할과 비슷하다.- String interpolation (문자열 보간) 이라고 한다.
- 변수 치환, 표현식 평가(
${표현식}
) 등의 역할을 한다.
List<String> alphabets = ['a', 'b', 'c'];
final aAlphabets = alphabets.map((alphabet) => 'a$alphabet'); // 'a' + alphabet 과 같다
print(alphabets); // [a, b, c]
print(aAlphabets); // (aa, ab, ac)
reduce()
List
가 받은 제네릭 타입을 반환한다.
List<String> alphabets = ['a', 'b', 'c'];
final reduced = alphabets.reduce((acc, alphabet) => '$acc, $alphabet'); // acc + ', ' + alphabet 과 같다
print(reduced); // 'a, b, c'
fold()
reudce()
와 흡사하지만,List
가 받은 제네릭 타입과 다른 타입을 반환할 수 있다.- 콜백함수 앞의 인수는 초기 값을 의미한다.
List<String> alphabets = ['a', 'bb', 'ccc'];
final fold = alphabets.fold<int>(10, (acc, alphabet) => acc + alphabet.length); // acc + ', ' + alphabet 과 같다
print(fold); // 16
맵
Map[key]
형식으로 원소에 접근 가능keys
와values
프로퍼티는 키와 값을Iterable
타입으로 받아볼 수 있게 해준다.
Map<String, String> me = {
'name': 'Jake Seo',
'gender': 'male',
'nationality': 'Korea'
};
print(me['name']); // Jake Seo
print(me['gender']); // male
print(me['nationality']); // Korea
print(me.keys); // (name, gender, nationality)
print(me.values); /// (Jake Seo, male, Korea)
셋
- 값의 유일함을 보장받을 수 있다.
Set.from()
메서드로 다른Collection
을Set
으로 변경 가능- 자바의 정적 팩터리 메서드랑 비슷하다.
Set<String> languages = {'C', 'C++', 'Java', 'JavaScript', 'C'};
print(languages); // {C, C++, Java, JavaScript}
print(languages.contains('Java')); // true
print(languages.contains('Smalltalk')); // false
List<String> languageList = ['C', 'C++', 'Java', 'JavaScript', 'C'];
Set<String> languageSet = Set.from(languageList);
print(languageSet); // {C, C++, Java, JavaScript}
enum
- 제한된 선택지가 있을 때 유용한 타입이다.
// 아래 코드는 자바스크립트 `Promise` 의 상태를 나열해본 것이다.
enum PromiseStatus {
pending,
fulfilled,
rejected
}
void main() {
PromiseStatus status = PromiseStatus.pending;
print(status); // PromiseStatus.pending
}
연산자
- 수치 연산자 (다른 언어에서도 제공하는 사칙연산)
null
관련 연산자- 값 비교 연산자
- 타입 비교 연산자
- 논리 연산자
null
관련 연산자
- 기본적으로 모든 변수는
null
값을 가질 수 없음 null
을 가지려면 타입 뒤에?
를 추가해줘야 함- NPE 를 예방하는 null safety
void main() {
double number1 = null; // 에러
double? number2 = null; // 에러 아님
}
- 할당 연산자 앞에
??
를 추가하면, 기존 값이null
일 때만 값을 저장하게 할 수 있음if (something == null) { a = b; }
의 축약형이라고 생각함
void main() {
double? number2; // 자동으로 null 할당
number2 ??= 10; // null 이 들어있는 상태라 10 이 할당
number2 ??= 5; // 10 이 들어있는 상태라 값 새로 할당하지 않음
print(number2); // 10
}
타입 비교 연산자
is
키워드를 사용하여 타입 비교 가능
void main() {
double? number2; // 자동으로 null 할당
print(number2 is int); // false
print(number2 is double); // false, 아직 null 임
number2 = 1.1;
print(number2 is double); // true, 이제 null 아님
}
제어문
if
와switch
가 있음- 다른 언어들과 비슷하나,
switch
에서는 각case
끝에break
를 안쓰면 에러남
for
문
index
를 조정해서 사용하는for
문은 다른 언어와 같음- 그 외,
for ... in
패턴의for
문도 제공 - 내부적으로
Iterator
를 사용하여 순회
Iterable<int> numbers = [1, 2, 3];
for (var number in numbers) {
print(number);
}
위 코드는 아래의 코드와 동치임
Iterable<int> numbers = [1, 2, 3];
Iterator<int> numberIterator = numbers.iterator;
while (numberIterator.moveNext()) {
var number = numberIterator.current;
print(number);
}
while
문과 do...while
문
- 다른 언어들과 같음
함수와 람다
- 다트 언어에서도 함수와 람다를 제공함
순서가 고정된 매개변수와 이름이 있는 매개변수
- 다트 언어에서는 순서가 고정된 매개변수 (positional parameter) 와 이름이 있는 매개변수 (named parameter) 를 제공
- 순서가 고정된 매개변수는 다른 언어들과 같음
- 이름이 있는 매개변수는 매개변수가 아주 많을 때 효과적임
이름이 있는 매개 변수
- 이름이 있는 매개변수는 초기에
required
키워드를 통해 기본값을 줄지 안줄지 선택 가능함 required
키워드가 없으면 반드시 기본 값을 주어야 함
int add({
required int a,
required int b
}) {
return a + b;
}
void main() {
print(add(a: 10, b: 15));
}
int add({
required int a,
int b = 10
}) {
return a + b;
}
void main() {
print(add(a: 10));
}
순서가 고정된 매개변수에 기본 값 주기
- 반드시 기본 값을 뒤에 줘야 한다.
- 기본 값을 뒤에 주지 않으면, 언어에서 어떤 것이 기본값인지 파싱 시에 알 수 없다.
int add(int a, [int b = 10]) {
return a + b;
}
void main() {
print(add(15)); // 25
}
순서가 고정된 것과 이름이 있는 매개변수 섞어쓰기
- 섞어쓸 때는 반드시 포지셔널 파라미터가 네임드 파라미터보다 앞에 와야 한다.
- 이것도 섞여버리면 파싱할 때 난감하다.
- 언어 레벨에서 파싱의 모호성이 늘어난다.
int add(int a, {
required int b
}) {
return a + b;
}
void main() {
print(add(10, b: 10));
}
익명 함수와 람다 함수
- 다트에서는 익명 함수와 람다 함수를 구분하지 않는다.
익명 함수 포맷
{}
를 사용하였으므로 명시적 리턴 필요
(parameter) {
body
}
람다 함수 포맷
{}
를 사용하지 않았으므로 명시적 리턴은 필요 없음
(parameter) => only_one_statement
typedef
typedef
는 함수의 시그니처를 정의한다.- 자바의
interface
와 흡사하다. - 함수형 프로그래밍 방식으로 파라미터에 함수를 이용할 때 코드의 표현력을 높여준다.
typedef Operation = void Function(int x, int y);
void add(int x, int y) {
print(x + y);
}
void subtract(int x, int y) {
print(x - y);
}
void main() {
Operation oper = add;
oper(1, 2); // 3
oper = subtract;
oper(2, 1); // 1
}
typedef Operation = void Function(int x, int y);
void add(int x, int y) {
print(x + y);
}
void subtract(int x, int y) {
print(x - y);
}
void calc(Operation oper, int x, int y) {
oper(x, y);
}
void main() {
calc(add, 1, 2); // 3
calc(subtract, 2, 1); // 1
}
try ... catch
- 자바와 예외 던지는 방법이 흡사한데, 생성자를 통해 던지지 않는다는 점이 좀 다름
void main() {
try {
const String name = 'Jake Seo';
throw Exception('그냥 예외를 던집니다!');
print(name); // 닿지 않음
} catch (e) {
print(e);
}
}
반응형
'코드팩토리의 플러터 프로그래밍' 카테고리의 다른 글
코드 팩토리의 플러터 프로그래밍 - 플러터 기본 다지기, 4장 플러터 입문하기 (1) | 2023.10.29 |
---|---|
코드 팩토리의 플러터 프로그래밍 - 다트 언어 마스터하기, 3장 다트(Dart) 비동기 프로그래밍 (0) | 2023.10.21 |
코드 팩토리의 플러터 프로그래밍 - 다트 언어 마스터하기, 2장 다트(Dart) 객체지향 프로그래밍 (1) | 2023.10.15 |