Dart Basics

명이·2023년 1월 3일
1

Warning 내용에 오류가 있을 수 있습니다. 댓글로 지적 부탁드립니다.

Dart language 문법 정리

왜 Dart가 특별한가?

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/ 를 참고해보면 좋다.

Variables

Entrypoint

모든 Dart Application의 entrypoint는 main 함수이다.

void main() {
	print("Hello world");
}

main 함수가 없다면, dart는 컴파일되지 않는다.

Var

Dart에서 변수 선언은 var 키워드를 통해서 한다. 혹은 Java처럼 선언하기도 한다.

var name = "myoun";
// or
String name = "myoun";

dart는 var 키워드를 사용할 시 변수의 타입을 자동으로 추론한다. dart의 스타일 가이드는, 함수나 메서드 안에서는 var를 사용하고, 클래스의 프로퍼티를 선언할때에는 프로퍼티의 타입을 통해서 선언하는 것을 권장한다.

Dynamic

Dynamic을 사용하면, 변수의 타입을 원하는대로 지정할 수 있다.

var name = "myoun";
name = 1; // type error

dynamic name = "myoun";
name = 1; // ok

Null Safety

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 키워드를 사용할 수 있다.

final name = "myoun";
// or
final String name = "myoun";

이렇게 사용하면, name을 다른 값으로 변경할 수 없다.

Const

const는 한번 정하면 변경할 수 없다는 점에서 final과 비슷하다. 하지만, const는 컴파일 타임에 컴파일러가 알고 있어야하는 상수이다.

예를 들면, 다음과 같은 경우에는 const로 선언할 수 없다.

final api = fetchApi();

외부 api에서 가지고 오는 경우에는 컴파일러가 알고 있을 수 없기 때문이다.

const API_KEY = "123456789";

하지만, API키와 같은 정해진 상수는 const로 선언할 수 있다.

Data Types

Basic Data Types

String name = "me"; // or 'me'
int age = 9;
double money = 99.99;
bool isAdult = false; // true | false

기본 타입들은 위와 같다.

List

Dart의 List는 참 쉽다.

var list = [1,2,3,4,5]; // List<int>

위와 같이 쓰면, List<int>로 자동으로 추론된다. 저기 <> 안에 리스트 안에 들어갈 타입을 넣으면 된다. 만약, 리스트 안의 내용들의 타입이 다르다면 자동으로 List<Object>로 추론된다. 여기서 Object는 최상위 객체로, 모든 객체는 이를 상속한다. 자바의 Object를 생각하면 된다 (내용물도 비슷하다).

String Interpolation

문자열 중간에 다른걸 끼워 넣고 싶을때 + 대신 사용할 수 있다.

var name = "myoun";
var greeting = "Hello, $name!";

Collection If

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]

Collection For

이 역시 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"]

Map

var map = { name : "me", age : 10 };

위와 같이 쓰면, map의 타입은 Map<String, Object>이다. Map도 다른 모든것과 똑같이 타입을 명시해줄 수 있다.

Set

Set의 가장 큰 특징은 중복이 없다는 것이다. 또한, 집합의 기능들을 사용할 수 있다. 다음과 같이 선언한다.

var set = { 1, 2, 3 };

타입은 Set<int>로 추론된다.

Function

Declaration

Dart의 함수 선언은 다음과 같이 한다.

int sum(int a, int b) {
	return a + b;
}

위의 예제에서 가장 앞에 있는 int는 함수의 반환 타입을 의미하며, 반환값이 없이 부수 효과(side effect)만 있는 함수의 경우에는 함수의 반환 타입을 void로 한다.

매개 변수의 경우에는 괄호 안에 타입 변수명 형태로 작성한다.

Named Parameter

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는 매개 변수의 위치와 상관 없이 이름을 통해 함수를 호출하는 것으로 함수 선언시에 위와 같이 {} 안에 매개 변수를 작성해야 한다.

Optional Positional 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 (??)

QQ Operator는 Question Question Operator로 ?? 형태로 쓰이는 연산자를 말한다. 만약 좌항이 null이라면, 우항을 대신 반환하는 연산자이다.

null ?? 1 // 1
1 ?? 2 // 1

또한 다음과 같이도 사용할 수 있다.

String? name;
name ??= "myoun";

Typedef

typedef 키워드를 통해 타입의 alias를 만들 수 있다. 아래 예시를 보자.

typedef IntList = List<int>;

Class

Dart는 객체 지향 언어답게 클래스를 가지고 있다.

class Player {
	String name = "player1";
    int hp = 20;
}

var player2 = Player();
player2.name = "player2";

기본적인 클래스 선언은 위와 같이 하면 된다.

Constructor

생성자는 클래스 생성시에 호출된다.

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 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});
}

Named Constructor

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 역시 기본 매개변수를 사용할 수 있다.

Cascade Notation

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

Enum은 선택지를 좁히는 역할을 한다. 만약 운동회 프로그램을 만들때, 청 팀과 홍 팀이 필요할 때, 문자열 'red''blue'를 이용한다면, bleu와 같이 오타가 날 수 있다. 이를 막기 위해 Enum을 사용할 수 있다.

enum Team { red, blue }

var myTeam = Team.blue;

Abstract Class

추상 클래스는 실제 구현은 하지 않고 정의만 해놓는 클래스이다.

abstract class Human {
  void walk();
}

class Player extends Human {
  String name;
  int hp;

  Player(this.name, this.hp);

  
  void walk() {
    print("Player is walking");
  }
}

실제 구현은 이를 상속받는 클래스가 한다.

Inheritance

상속은 객체지향의 중요한 개념이다. Dart에서는 extends라는 키워드를 통해 상속을 할 수 있다.

class Player extends Human {}

Mixin

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 클래스들을 조합하면서 클래스에 기능을 추가할 수 있다.

References

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

profile
tech explorer | https://duge.space/myounJAwobV

0개의 댓글

관련 채용 정보