본글은 코드팩토리의 플러터 프로그래밍 도서를 보고 공부한 내용을 정리한 내용입니다.
구글이 개발한 다트 프로그래밍 언어로, 크롬에 Dart 가상 머신을 심어 자바스크립트를 대체하려고 시도했으나, 웹 개발에 혼란을 가져온다는 여론을 극복하지 못하고 결국 자바스크립트로 완전 컴파일 가능하게 만드는데 그쳤다.
주석 작성, 콜솔출력, 변수를 선언하는 방법, 변수의 타입을 알아보자.
다트는 프로그램 시작점인 엔트리 함수 기호로 main()을 사용한다.
void main() {
}
프로그램을 실행했을때 프로그램에서 코드로 인식하지 못하는 부분, 일반적으로 개발자끼리 소통 및 코드에 대한 정보를 남기는데 사용, 주석 기호로 //, /**/, ///를 사용한다.
void main() {
// 한줄 주석
/*
* 여러줄 주석
*/
/// 문서 주석을 작성할 수 있따.
}
문자열을 콘솔에 출혁하는 함수
void main() {
print("Hello world");
}
var 변수명 = 값; 형식으로 선언한다. 변수의 값이 들어가면 자동으로 타입을 추론한다.
실제 코드가 컴파일 될때는 추론된 타입으로 var이 치환된다.
var name = "송기영";
print(name);
name = "카이";
print(name);
// 다음과 같이 중복되는 변수명은 재선언 불가능
var name = "재선언";
var은 한번 타입을 유추하면 추론된 타입이 고정된다. 따라서 다른 변수 타입 값을 변수에 다시 저장하려고 하면 에러가 난다. dynamic은 이 부분을 해결해준다.
dynamic name = "송기영";
name = 1;
변수의 값을 처음 선언 후 변경할 수 없다.
const String name = "송기영";
final String name2 = "송기영2";
// 아래 모두 값 변경 불가능
name = "카이";
name2 = "카이2";
final은 런타임, const는 빌드 타임 상수로 코드를 실행하지 않은 상태에서 값이 확정되면 const를, 실행될 때 확정되면 final을 사용한다. 즉 const는 값이 정해져있는 변수
final check = test();
// 에러
const check = test();
int test() {
return 42;
}
var을 사용해도 되지만 변수 타입을 명시해주면 코드가 직관적이여서 유지보수에 용이하다.
String name = "송기영";
int isInt = 10;
double isDouble = 2.5;
bool isTrue = true;
하나의 변수에 여러 값을 저장할 수 있는 타입으로 아래와 같은 타입이 존재한다.
List - 여러 값을 순서대로 저장
Map - 특정 키값을 기반으로 빠르게 값을 검색
Set - 중복된 데이터를 제거
여러 값을 순서대로 한 변수에 저장할 때 사용한다. 구성 단위를 원소라고하며, 리스트명[인덱스] 형식으로 특정 원소에 접근 가능하다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
print(blackPinkList);
print(blackPinkList[0]); // 리사
print(blackPinkList[blackPinkList.length - 1]); // 로제
List에 값을 추가할 때 사용하며 추가 하고 싶은 값을 매개변수에 입력한다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
blackPinkList.add("기영");
List에 있는 값들을 순서대로 순회하면서 특정 조건에 맞는 값만 필터링 하는데 사용한다. 조건에 맞으면 true를 반환하고 값을 유지하고, false를 반환하면 값을 버린다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
final newList = blackPinkList.where(
(name) => name == "리사" || name == "지수", // 리사 또는 지수만 유지
);
print(newList); // (리사, 지수)
print(newList.toList()); // [리사, 지수]
List에 있는 값들을 순서대로 순회하면서 값을 변경할 수 있다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
final newList = blackPinkList.map(
(name) => "블랙핑크 $name", // 리사 또는 지수만 유지
);
print(newList); // (블랙핑크 리사, 블랙핑크 지수, 블랙핑크 제니, 블랙핑크 로제)
print(newList.toList()); // [블랙핑크 리사, 블랙핑크 지수, 블랙핑크 제니, 블랙핑크 로제]
List에 있는 값들을 순서대로 순회하면서 매개변수에 입력된 함수를 실행한다. 다만 값을 쌓아가는 특징이 있다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
final allMembers = blackPinkList.reduce((value, element) => value +', ' + element);
print(allMemebers); // 리사, 지수, 제니, 로제
reduce() 함수와 실행되는 논리는 똑같다. 하지만 reduce() 함수는 리스트 요소들의 타입이 같아야 하지만 fold() 함수는 어떤 타입이든 반환할 수 있다.
List<String> blackPinkList = ["리사", "지수", "제니", "로제"];
final allMemebers = blackPinkList.fold<int>(0, (value, element) => value + element.length);
print(allMemebers); // 8
reduce() 함수와 달리 fold() 함수는 첫번째 매개변수에 입력된 값이 초기값으로 사용된다.
키와 값의 짝을 저장한다. 자바스크립트의 object와 같은 형태로 Map<키의 타입, 값의 타입> 맵이름 형식으로 생성한다.
Map<String, String> dictionary = {
"Harry Potter": "해리 포터",
"Ron Weasley" : "론 위즐리",
"Hermione Ganger": "헤르미온느 그레인저",
};
print(dictionary); // {Harry Potter: 해리 포터, Ron Weasley: 론 위즐리, Hermione Ganger: 헤르미온느 그레인저}
print(dictionary["Harry Potter"]);
print(dictionary.keys); // (Harry Potter, Ron Weasley, Hermione Ganger)
print(dictionary.values); // (해리 포터, 론 위즐리, 헤르미온느 그레인저)
키와 값의 조합이며 중복 없는 값들의 집합으로, Set<타입> 세트이름 형식으로 생성한다. 중복을 방지하므로 유일한 값들만 존재하는걸 보장한다.
Set<String> blankPink = {"로제", "지수", "리사", "제니"}; //
print(blankPink);
print(blankPink.contains("로제"));
print(blankPink.toList()); // 리스트로 변환
List<String> blankPink2 = ["로제", "지수", "지수"];
var check = Set.from(blankPink2); // 이렇게 적용하면 기존 blankPink2 값도 변경이 될지 궁금했다.
print(blankPink2); // [로제, 지수, 지수]
print(check); // {로제, 지수}
한 변수의 값을 몇가지 옵션으로 제한하는 기능으로, 선택지가 한정적일 때 사용한다.
String으로 완전 대체가 가능하지만 기본적으로 자동완성을 지원하고 정확히 어떤 선택지가 존재하는지 정의해둘 수 있기 때문에 유용하다.
enum Status {
approved,
pending,
rejected
}
수치 연산자, null값 입력 관련 연산자, 값 비교 연산자, 타입 비교 연산자, 논리 연산자
double number = 2;
print(number + 2);
print(number - 2);
print(number * 2);
print(number / 2);
print(number % 2);
number++;
number--;
number += 2;
number -= 2;
number *= 4;
number /= 2;
null은 아무 값도 없음을 뜻한다. 타입 뒤에 ‘?’를 추가해줘야 null값이 저장될 수 있다.
double? number1 = 1; // null 허용
double? number1; // 자동으로 null값 지정
number1 ??= 3; // 기존 값이 null일 때만 저장
number1 ??= 4; // 기존 값이 null이 아니기 때문에 3이 유지된다.
double number2 = null; // 에러
int number1 = 1;
int number2 = 2;
print(number1 > number2); // false
print(number1 < number2); // true
print(number1 >= number2); // false
print(number1 <= number2); // true
print(number1 == number2); // false
print(number1 != number2); // true
is 키워드를 사용하면 변수의 타입을 비교할 수 있다.
int number = 1;
print(number is int); // true
print(number is String); // false
print(number is! int); // false
print(number is! String); // true
and와 or을 의미한다.
bool result1 = 12 > 10 && 1 > 10; // true
bool result2 = 12 > 10 && 0 > 1; // false
bool result3 = 12 > 10 || 1 > 0; // true
bool result4 = 12 > 10 || 0 > 1; // true
bool result5 = 12 < 10 || 0 > 1; // false
원하는 조건 기준으로 다른 코드를 실행하고 싶을때 사용한다.
int number = 2;
if (number % 3 == 0) {
print('3의 배수');
} else if (number % 3 == 1) {
print('나머지 1');
} else {
print('맞는 조건 없음');
}
입력된 상수 값에 따라 case 블록을 수행, break 키워드를 사용하면 switch문 밖으로 나갈 수 있다.
enum과 함께 사용하면 유용하다.
enum Status {
approved,
pending,
rejected
}
void main() {
Status status = Status.approved;
switch (status) {
case Status.approved:
print('승인');
break;
case Status.pending:
print('대기');
break;
case Status.rejected:
print('거절');
break;
default:
print('알 수 없음');
}
}
for (int i = 0; i < 3; i++) {
print(i);
}
List<int> numberList = [1,2,3];
for (int number in numberList) {
print(number);
}
조건을 기반으로 반복문을 행한다. 조건이 true면 실행, false면 멈춘다.
do…while은 실행한 후 조건을 확인한다.
int number = 0;
while (number < 10) {
print(number);
number += 1;
}
do {
number += 1;
} while(number <10);
함수는 여러 곳에서 재활용이 가능하며, 반환값이 없을 때에는 void 키워드를 사용한다.
int addTwoNumber(int a, int b) {
return a + b;
}
네임드 파라미터(자바스크립트의 object 같은것을 의미)를 지정하려면 중괄호 {}와 required 키워드를 사용해야 한다.
int addTwoNumber({
required int a,
required int b,
}) {
return a + b;
}
print(addTwoNumber(a:1, b:2));
required 키워드는 매개변수가 null값이 불가능한 타입이면 기본값을 지정해주거나 필수로 입력해야 한다는 의미이다.
포지셔널 파라미터는 일반적인 순서대로 매개변수를 가지는 것을 의미하며 기본값을 주기 위해서는 []기호를 사용한다.
int addTwoNumber(int a, [int b = 2]) {
return a + b;
}
print(addTwoNumber(a:1));
포지셔널 파라미터와 네임드 파라미터를 같이 사용할 때는 포지셔널 파라미터가 먼저 위치해야한다.
int addTwoNumber(int a, {
required int b,
int c = 3,
}) {
return a + b + c;
}
print(addTwoNumber(1, b:3, c:4));
List<int> numbers = [1,2,3,4,5];
// 익명함수
final allMembers = numbers.reduce((value, element) {
return value + element;
})
// 람다함수
final allMembers2 = numbers.reduce((value, element) => value + element);
함수의 시그니처를 정의하는 값, 시그니처는 반환값 타입, 매개변수 개수와 타입 등을 의미한다.
즉 함수 선언부를 정의하는 키워드며, 함수의 동작에 대한 정의는 없다.
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);
oper = subtract;
oper(2,1);
}
다트에서 함수는 일급 객체로 함수를 값처럼 사용할 수 있다.
typedef Operation = void Function(int x, int y);
void add(int x, int y) {
print(x + y);
}
void calculate(int x, int y, Operation oper) {
oper(x, y)
}
void main() {
calculate(1,2, add);
}
try{
// 에러가 없을때
final String name = "기영";
// 고의적인 에러 발생시 catch에 잡힘.
throw Exception('Error);
}catch(e){
print(e);
}
다른 객체지향 언어와 비슷하면서도 자바스크립트의 기능을 포함한것을 볼 수 있었음. 특히 일급 객체 부분에서 자바스크립트를 대체하려고 했음이 보였다.
2023.11.27. 추가
다트의 내용을 공부하다보니 다트는 자바스크립트와 같이 싱글 스레드 기반 언어로 이벤트 루프(Event Loop) 및 Future와 Stream과 같은 비동기적인 패턴을 활용하여 처리된다고 한다.