Warning 내용에 오류가 있을 수 있습니다. 댓글로 지적 부탁드립니다.
Dart language 문법 정리
Dart 언어는 Flutter라는 프레임워크를 위해 존재한다고 말할 수 있다. Flutter와 Dart는 때어놓을 수 없는 관계에 있다.
Flutter team은 프레임워크를 위해 언어를 바꿀 수 있다. 이는 다른 프레임워크들은 할 수 없는 행동이다. 예를 들면, React.js를 개발하는 facebook team은 React를 위해 Javascript를 변경할 권한을 가지고 있지 않다. 하지만, Flutter team과 Dart team은 같은 구글의 팀으로 서로에게 영향을 미치고 실제로 이로 인해 Dart가 바뀐 전적 역시 존재한다.
Dart의 핵심 특징 중 하나는 JIT 컴파일과 AOT 컴파일을 지원한다는 것인데, flutter가 dart를 선택할 당시에는 dart에 native binary를 지원하는 AOT toolchain이 존재하지 않았다. 하지만, fluttter team이 dart team에 이를 요청해서 현재는 native binary를 지원하는 AOT toolchain이 존재한다.
또한, Dart는 언어가 UI에 최적화 되어 있다.
자세한 내용이 궁금하다면 https://dart.dev/ 를 참고해보면 좋다.
모든 Dart Application의 entrypoint는 main 함수이다.
void main() {
print("Hello world");
}
main 함수가 없다면, dart는 컴파일되지 않는다.
Dart에서 변수 선언은 var
키워드를 통해서 한다. 혹은 Java처럼 선언하기도 한다.
var name = "myoun";
// or
String name = "myoun";
dart는 var
키워드를 사용할 시 변수의 타입을 자동으로 추론한다. dart의 스타일 가이드는, 함수나 메서드 안에서는 var
를 사용하고, 클래스의 프로퍼티를 선언할때에는 프로퍼티의 타입을 통해서 선언하는 것을 권장한다.
Dynamic을 사용하면, 변수의 타입을 원하는대로 지정할 수 있다.
var name = "myoun";
name = 1; // type error
dynamic name = "myoun";
name = 1; // ok
Dart는 Null Safety를 보장한다. Null Safety란 null pointer에 접근함으로 인해 일어나는 일을 예방하게 해주는 기능이다.
bool isEmptyString(String string) => string.isEmpty;
isEmptyString(null);
위와 같은 코드가 실행된다면 어떻게 될까? null에는 isEmpty라는 프로퍼티가 없기 때문에 오류가 나게 된다. 하지만, 컴파일러는 이를 알지 못하기에 런타임에 런타임 에러가 나게 된다.
이를 막기 위해서, Dart에는 Null Safety라는 기능이 존재한다.
bool isEmptyString(String string) => string.isEmpty;
isEmptyString(null); // The argument type 'Null' can't be assigned to the parameter type 'String'.
만약 위와 같이 null을 넣게 된다면 에러 메세지를 받을 수 있다. 만약 null값 역시 들어와야하는 상황에는, 타입 옆에 ?
기호를 붙일 수 있다.
bool isEmptyString(String? string) => string?.isEmpty ?? true;
String?
타입은 이 값은 String
일수도 있고 null
일수도 있다라는 의미의 타입이다.
변수의 값을 한번 선언하고 변경하지 않고 싶을때가 있다.
그런 경우에는 final
키워드를 사용할 수 있다.
final name = "myoun";
// or
final String name = "myoun";
이렇게 사용하면, name을 다른 값으로 변경할 수 없다.
const
는 한번 정하면 변경할 수 없다는 점에서 final
과 비슷하다. 하지만, const
는 컴파일 타임에 컴파일러가 알고 있어야하는 상수이다.
예를 들면, 다음과 같은 경우에는 const
로 선언할 수 없다.
final api = fetchApi();
외부 api에서 가지고 오는 경우에는 컴파일러가 알고 있을 수 없기 때문이다.
const API_KEY = "123456789";
하지만, API키와 같은 정해진 상수는 const
로 선언할 수 있다.
String name = "me"; // or 'me'
int age = 9;
double money = 99.99;
bool isAdult = false; // true | false
기본 타입들은 위와 같다.
Dart의 List는 참 쉽다.
var list = [1,2,3,4,5]; // List<int>
위와 같이 쓰면, List<int>
로 자동으로 추론된다. 저기 <>
안에 리스트 안에 들어갈 타입을 넣으면 된다. 만약, 리스트 안의 내용들의 타입이 다르다면 자동으로 List<Object>
로 추론된다. 여기서 Object
는 최상위 객체로, 모든 객체는 이를 상속한다. 자바의 Object
를 생각하면 된다 (내용물도 비슷하다).
문자열 중간에 다른걸 끼워 넣고 싶을때 +
대신 사용할 수 있다.
var name = "myoun";
var greeting = "Hello, $name!";
Dart만의 특별한 기능이라고 보면 된다. Collection을 선언할 때 if문을 내부적으로 쓸 수 있다.
var addFive = true;
var numbers = [
1,
2,
3,
4,
if (addFive) 5,
6,
7,
];
print(numbers); // [1, 2, 3, 4, 5, 6, 7]
이 역시 Dart만의 특별한 기능이다.
var fruits = ["banana", "apple", "mango"];
var plants = [
"rose",
"sunflower",
for (var fruit in fruits) "(fruit) $fruits",
];
print(plants); // ["rose", "sunflower", "(fruit) banana", "(fruit) apple", "(fruit) mango"]
var map = { name : "me", age : 10 };
위와 같이 쓰면, map의 타입은 Map<String, Object>
이다. Map도 다른 모든것과 똑같이 타입을 명시해줄 수 있다.
Set의 가장 큰 특징은 중복이 없다는 것이다. 또한, 집합의 기능들을 사용할 수 있다. 다음과 같이 선언한다.
var set = { 1, 2, 3 };
타입은 Set<int>
로 추론된다.
Dart의 함수 선언은 다음과 같이 한다.
int sum(int a, int b) {
return a + b;
}
위의 예제에서 가장 앞에 있는 int
는 함수의 반환 타입을 의미하며, 반환값이 없이 부수 효과(side effect)만 있는 함수의 경우에는 함수의 반환 타입을 void
로 한다.
매개 변수의 경우에는 괄호 안에 타입 변수명
형태로 작성한다.
Dart는 Named Parameter라는걸 지원한다.
void sayHello({required String name, required int age}) {
print("Hello, my name is $name and I am $age.");
}
sayHello(name: "myoun", age: 10);
Named Parameter는 매개 변수의 위치와 상관 없이 이름을 통해 함수를 호출하는 것으로 함수 선언시에 위와 같이 {}
안에 매개 변수를 작성해야 한다.
Dart는 Optional Positional Parameter를 지원한다.
void sayHello(String name, [int age = 10]) {
print("Hello my name is $name and I'm $age.");
}
sayHello("myoun"); // Hello my name is myoun and I'm 10.
QQ Operator는 Question Question Operator로 ??
형태로 쓰이는 연산자를 말한다. 만약 좌항이 null이라면, 우항을 대신 반환하는 연산자이다.
null ?? 1 // 1
1 ?? 2 // 1
또한 다음과 같이도 사용할 수 있다.
String? name;
name ??= "myoun";
typedef
키워드를 통해 타입의 alias를 만들 수 있다. 아래 예시를 보자.
typedef IntList = List<int>;
Dart는 객체 지향 언어답게 클래스를 가지고 있다.
class Player {
String name = "player1";
int hp = 20;
}
var player2 = Player();
player2.name = "player2";
기본적인 클래스 선언은 위와 같이 하면 된다.
생성자는 클래스 생성시에 호출된다.
class Player {
late String name;
late int hp;
Player(String name, int hp) {
this.name = name;
this.hp = hp;
}
}
var player2 = Player("player2", 20);
late
키워드는 변수가 늦게 초기화 될 수 있게 만들어준다.
하지만 이렇게 불편하게 선언할 필요가 없다.
class Player {
String name;
int hp;
Player(this.name, this.hp);
}
이렇게 선언하면 아까 작성했던 코드와 정확히 같은 역할을 한다.
Named Constructor Parameter는 위에서 배웠던 Named Parameter의 Constructor 버전이다. 문법도 거의 같다.
class Player {
late String name;
late int hp;
Player({required String name, required int hp}) {
this.name = name;
this.hp = hp;
}
}
var player2 = Player(name: "player2", hp: 20);
물론 이렇게도 쓸 수 있다.
class Player {
String name;
int hp;
Player({required this.name, required this.hp});
}
Constructor에 이름을 붙여줄 수 있다.
class Player {
String name;
int hp;
Player.createZeroHpPlayer({required String name}):
this.name = name,
this.hp = 0;
}
var player2 = Player.createZeroHpPlayer(name: "player2");
물론 Named Constructor 역시 기본 매개변수를 사용할 수 있다.
class Player {
String name;
int hp;
Player(this.name, this.hp);
}
아래에 나오는 설명은 위 클래스 기반이다.
var player2 = Player("player2", 20)
..name = "player3"
..hp = 10;
이 코드는 아래 코드와 같은 역할을 한다.
var player2 = Player("player2", 20);
player2.name = "player3";
player2.hp = 10;
Enum은 선택지를 좁히는 역할을 한다. 만약 운동회 프로그램을 만들때, 청 팀과 홍 팀이 필요할 때, 문자열 'red'
와 'blue'
를 이용한다면, bleu
와 같이 오타가 날 수 있다. 이를 막기 위해 Enum을 사용할 수 있다.
enum Team { red, blue }
var myTeam = Team.blue;
추상 클래스는 실제 구현은 하지 않고 정의만 해놓는 클래스이다.
abstract class Human {
void walk();
}
class Player extends Human {
String name;
int hp;
Player(this.name, this.hp);
void walk() {
print("Player is walking");
}
}
실제 구현은 이를 상속받는 클래스가 한다.
상속은 객체지향의 중요한 개념이다. Dart에서는 extends
라는 키워드를 통해 상속을 할 수 있다.
class Player extends Human {}
Mixin은 굉장히 독특한 기능인데, 클래스에 프로퍼티를 추가할 때 사용한다. with
키워드를 이용하며, 사용 가능한 클래스는 생성자가 없는 클래스이다.
class Runnable {
void run() {
print("I am running");
}
}
class Swimmable {
void swim() {
print("I am swimming");
}
}
class Human with Runnable, Swimmable {
void run() {
print("Human is running");
}
void swim() {
print("Human is swimming");
}
}
class Cheetah with Runnable {
void run() {
print("Cheetah is running");
}
}
다음과 같이 Mixin 클래스들을 조합하면서 클래스에 기능을 추가할 수 있다.
Nomad Coder, Dart 시작하기 : https://nomadcoders.co/dart-for-beginners?utm_medium=website&utm_source=webpage&utm_campaign=roadmap
Dart official website : https://dart.dev/
Flutter FAQ : https://docs.flutter.dev/resources/faq