본문 바로가기
TIL

Dart 기본 문법

by sun_HY 2024. 8. 11.

 

Introduction 

 

Dart is a language Optimized for UI

Dart has 2 compilers
  - dart web: javascript로 변환
  - dart native: ARM64, x86_64 등 여러 CPU 아키텍처에 맞게 변환

AOT(Ahead-Of-Time)
JIT(Just-In-Time)

Dart는 개발 환경에서 VM을 이용하여 JIT로 바로 실행 결과 볼 수 있게 함. 이때는 코드가 가상 머신에서 작동하기 때문에 느리다
배포 시에는 AOT를 이용하여 컴파일된 파일이 더 빠르게 작동할 수 있게 한다.

null safety: 안전한 프로그램 위해 반영됨 (null 참조 오류 방지)

Flutter가 Dart를 채택한 이유
  - JIT와 AOT가 모두 제공됨
  - Flutter와 Dart 모두 구글에서 만들기 때문에, 서로 필요 시에 수정이 가능함 (cf. 리액트를 위해 JS를 수정할 수는 없음)

 

 

Variables 

 

1. Var

 

dart style guide에서는 var 사용을 권장한다. (지역 변수 등에는 var 사용-컴파일러는 String임을 알고 있음)
특정 자료형은 class property 작성할 때 유용하다.

void main() {
  var name1 = 'EJ';
  String name2 = 'NC';
  name2 = 'NCLS';
  
  // 값 바꿀 때는 자료형 같아야 함
  // name1 = 97;
  // name2 = 79;  
}

 

 

2. Dynamic

 

타입 알기 전에 사용하면 좋다 (flutter, json에 유용)

이상적으로는 dynamic 쓰는 것을 피할 것

(var과의 차이점: var는 초기에 값을 지정하면 타입이 고정되어 변경 불가)

void main() {
  var name1;  // 처음 선언 시 초기화하지 않으면 dynamic으로 지정됨
  name1 = 'EJ';
  name1 = 97;
  name1 = true;

  dynamic name2;
  if (name2 is String) {
    // 이 안에서는 name2가 String인 것 알 수 있음
    // name2.   -> 그래서 더 다양한 메소드 조회 가능
  }
  
  var name3 = 'NC';
  name3 = 79;  // 에러 발생
  
  dynamic name4 = 'JO';
  name3 = 78; // 에러 발생하지 않음
}

 

 

3. null safety

 

컴파일 전에 null 참조 잡아내어 Runtime Error를 방지한다.
null이 될 수 있는 값 명시해줄 것

type 뒤에 ?을 붙여 사용한다 (e.g., String?)
기본적으로 모든 변수는 non-nullable

void main() {
  String? ej = 'EJ';
  ej = null;

  ej.length;
  // ej가 null일 수도 있기 때문에 에러

  if (ej != null) {
    ej.isNotEmpty; // 여기서는 사용 가능
  }

  ej?.isNotEmpty; // 이렇게 간단하게 체크할 수도 있음
}

 

 

4. final

 

이후에 값을 변경할 수 없는 변수 (재할당할 수 없는 변수)

void main() {
  final name = 'EJ';
}

 

 

5. late

 

late: var / final 앞에 붙여줄 수 있음
초기 데이터 없이 변수 선언하게 해 준다

void main() {
  late final String name;
  print(name); // -> 아직 name 값이 없으니 작동할 수 없음

  // 데이터 받아오기 등 작업
  name = 'EJ';

  print(name);  // 이제 가능
}

 

 

6. const

 

const: compile-time constant

컴파일 하기 전에 답을 알 수 있는 경우에 사용
값 변경은 불가능
final은 런타임 중에 만들어질 수 있음

void main() {
  const API = fetchApi(); // 이건 compile 시점에 알 수 없음
  const API2 = 1123124; // 이건 가능
}

 

 

Data Types 

 

7. List

void main() {
  var nums1 = [
    1,
    2,
    3,
    4, // 마지막에 쉼표 넣으면 여러 줄로 포매팅 가능
  ];
  List<int> nums2 = [1, 2, 3, 4];
  nums2.add(5);

  nums1.first; // 첫 번째 요소

  // collection if : 존재할 수도, 안 할 수도 있는 변수로 리스트 생성 가능
  var five = true;
  var ifFive = [
    1,
    2,
    3,
    4,
    if (five) 5, // five가 true인 경우 5가 추가됨
  ];

  // collection for
  var olds = ['N', 'B'];
  var news = [
    'J',
    'K',
    for (var o in olds) "👽$o",
  ];

  print(news);
  // 출력 결과: [J, K, 👽N, 👽B]
}

 

 

8. String Interpolation

 

$ 기호 뒤에 변수 사용하기 (변수가 이미 존재할 때)
계산할 때는 ${} 안에 변수 넣기
', " 모두 사용 가능
escape는 \

void main() {
  var name = 'EJ';
  var age = 22;
  var greeting = 'Hi Hello, I\'m $name and I\'m ${age + 10}';
  print(greeting);
}

 

 

9. Map

 

python의 dictionary와 유사
dart에서 object는 어떤 것도 될 수 있음 (typescript의 any와 유사)

API에서 값을 얻어 오는 경우에는 map 대신 class 권장

void main() {
  // Type: Map<String, Object>
  // dart에서는 컴파일러가 자동으로 타입 파악
  var player1 = {
    'name': 'EJ',
    'xp': 97,
    'power': false,
  };

  // 컴파일러 대신 직접 타입 설정할 수 있음
  Map<int, bool> player2 = {
    1: true,
    // 2: 1; => 에러 발생
  };
}

 

 

10. Set

 

set은 중복 허용하지 않음
python의 tuple과 유사

void main() {
  var nums = {1, 2, 3, 4};
  nums.add(1); // 변화 없음
}

 

 

 

Functions 

 

11. Function

 

String sayHello(String name) {
  return 'Hello $name';
}

// fat arrow syntax
// 즉시 리턴 등의 한 줄 코드에서 사용 가능
String sayHello2(String name) => 'Hello $name';

num plus(num a, num b) => a + b;

void main() {
  print(sayHello("EJ"));
}

 

 

12. named parameters

 

named parameter를 지정하는 방법

1. default value를 미리 지정한다

2. required로 설정한다

 

String sayHello(
    // 1. default value 지정
    //{String name = 'johnDoe', int age = 100, String country = 'sunshineCity'}) {

    // 2. required modifier
    {required String name,
    required int age,
    required String country}) {
  return 'Hello $name, you are $age years old, and you are from $country';
}

void main() {
  // positional parameter의 단점: 파라미터의 의미 직관적으로 알기 어려움
  // print(sayHello('EJ', 23, 'Korea'));

  // 해결책: named arguments
  // 순서 관계 없이 자료형 맞춰주면 사용 가능
  print(sayHello(age: 19, country: 'taiwan', name: 'NC'));
}

 

 

13. optional positional parameter

String sayHello(String name, int age, [String? country = 'taiwan']) =>
    'Hello $name, you are $age years old, and you are from $country';
// 대괄호, nullable 표시, default value 지정

void main() {
  print(sayHello('EJ', 2));
  // Hello EJ, you are 2 years old, and you are from taiwan
}

 

 

14. QQ Operator (QQ 연산자)

 

left ?? right
left가 null이면 right를 반환, 아니면 left를 반환

//String cap(String? name) => name.toUpperCase();
//String cap(String? name) => name != null ? name.toUpperCase() : 'ANON';
String cap(String? name) => name?.toUpperCase() ?? 'ANON';

void main() {
  cap('ej');
  cap(null);

  String? name;
  name ??= 'ej'; // name이 null이라면 우항의 값 할당
}

 

 

15. typedef

 

자료형에 alias 붙일 수 있게 해 주는 역할

typedef ListOfInts = List<int>;
typedef UserMap = Map<String, String>;

//List<int> reverseList(List<int> list) {
ListOfInts reverseList(ListOfInts list) {
  var reversed = list.reversed;
  return reversed.toList();
}

//String sayHi(Map<String, String> user) {
String sayHi(UserMap user) {
  return 'Hi, ${user['name']}!';
}

void main() {
  print(reverseList([1, 2, 3])); // [3, 2, 1]
  sayHi({"name": "ej"});
}

 

 

Classes 

 

16. class

 

class 생성 시에는 타입 명시 필수 (함수 안에서는 var 사용 가능한 것과 다름)

final로 선언한 변수는 값 변경 불가능

class Player {
  // String name = 'ej';
  final String name = 'ej';
  int xp = 9797;

  void sayHi() {
    //this.name; // 가능하지만 class method 안에서는 사용하지 않는 것 권고
    print('Hi, I\'m $name!');

    // var name = 'nc';
    // 위와 같이 같은 이름의 변수 선언 후 Player의 name을 가져오려면 this.name이라고 명시
  }
}

void main() {
  var player = Player(); // player 인스턴스 생성
  // var player = new Player();  new 키워드는 옵션

  print(player.name); // ej
  // player.name = 'nc';  final 선언하는 순간 값 변경 불가
  player.sayHi(); // Hi, I'm ej!
}

 

 

17. constructor

 

argument를 받아서 초기화하는 생성자

class Player {
  // 값은 나중에 받아 올 것이기 때문에 late
  // late final String name;
  // late int xp = 9797;

  // Player(String name, int xp) {
  //   this.name = name;
  //   this.xp = xp;
  // }

  // 개선된 코드
  final String name;
  int xp = 9797;

  Player(this.name, this.xp);

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  var player1 = Player('nc', 7979);
  player1.sayHi(); // Hi, I'm nc!

  var player2 = Player('jo', 7878);
  player2.sayHi(); // Hi, I'm jo!
}

 

 

18. named constructor parameters


positional argument가 가져올 수 있는 혼란을 방지하기 위해 named arguments로도 선언 가능하다.

can't have a value of 'null' 방지하는 방법 2가지: 1. 기본값 설정, 2. required

class Player {
  final String name;
  int xp;
  String team;
  int age;

  //Player(this.name, this.xp, this.team, this.age);

  // positional arguments를 named arguments로 변경
  Player({
    required this.name,
    required this.xp,
    required this.team,
    required this.age,
  });

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  // var player1 = Player('nc', 7979, 'team1', 22);
  var player1 = Player(
    name: 'nc',
    xp: 7979,
    team: 'team1',
    age: 22,
  );

  //var player2 = Player('jo', 7878, 'team2', 20);
  var player2 = Player(
    name: 'jo',
    xp: 7878,
    team: 'team2',
    age: 20,
  );
}

 

 

 

19. named constructors


: 을 이용하여 일부 값만 받고, 일부 값은 미리 설정한 상태로도 생성자 사용 가능

class Player {
  final String name;
  int xp, age; // 같은 타입 한 줄에 선언 가능
  String team;

  Player({
    required this.name,
    required this.xp,
    required this.team,
    required this.age,
  });

  Player.createRedPlayer({
    required String name,
    required int age,
  })  : this.age = age, // parameter로 받은 age를 this.age에 할당
        this.name = name,
        this.team = 'blue',
        this.xp = 0;

  Player.createBluePlayer(
      String name, int age) // 기본적으로 모든 positional arguments는 required
      : this.age = age, // parameter로 받은 age를 this.age에 할당
        this.name = name,
        this.team = 'red',
        this.xp = 0;

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  var player1 = Player.createRedPlayer(
    name: 'nc',
    age: 22,
  );

  var player2 = Player.createBluePlayer(
    'jo',
    20,
  );
}

 

 

20. 예시: json형태로 받아 오는 경우의 class 생성

class Player {
  final String name;
  int xp;
  String team;

  Player.fromJson(Map<String, dynamic> playerJson)
      : name = playerJson['name'],
        xp = playerJson['xp'],
        team = playerJson['team'];

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  var apiData = [
    {
      "name": "ej",
      "team": "green",
      "xp": 0,
    },
    {
      "name": "nc",
      "team": "pink",
      "xp": 0,
    },
    {
      "name": "jo",
      "team": "blue",
      "xp": 0,
    },
  ];

  apiData.forEach((playerJson) {
    var player = Player.fromJson(playerJson);
    player.sayHi();
  });

  //Hi, I'm ej!
  //Hi, I'm nc!
  //Hi, I'm jo!
}

 

 

 

21. cascade notation


xx.name = .. , xx. age = .. 처럼 번거롭게 기술하지 않고 .. 을 이용하여 생성 가능하다

class Player {
  String name;
  int xp;
  String team;

  Player({
    required this.name,
    required this.xp,
    required this.team,
  });

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  // var ej = Player(name: 'ej', xp: 9797, team: 'green');
  // ej.name = 'jj';
  // ej.xp = 9999;
  // ej.team = 'blue';

  var ej = Player(name: 'ej', xp: 9797, team: 'green')
    ..name = 'jj'
    ..xp = 9999
    ..team = 'blue';

  // 이렇게 불러와서도 가능
  var newjj = ej
    ..name = 'newjj'
    ..xp = 10000
    ..team = 'red';
}

 

 

22. enums


+) 출력하는 경우 Team.red 등으로 String 형태가 아니라 enum.xx 형태로 출력됨

+) 값만 출력하고 싶은 경우 String 가공할 것

enum Team { red, green, blue } // "" 사용하지 않아도 됨

enum XP { beginner, intermediate, advanced }

class Player {
  String name;
  XP xp;
  Team team;

  Player({
    required this.name,
    required this.xp,
    required this.team,
  });

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

void main() {
  var ej = Player(name: 'ej', xp: XP.intermediate, team: Team.blue)
    ..name = 'jj'
    ..xp = XP.advanced
    ..team = Team.green;

  var newjj = ej
    ..name = 'newjj'
    ..xp = XP.beginner
    ..team = Team.red;
}

 

 

23. abstract classes

 

다른 클래스들이 직접 구현해야 하는 메소드를 모아 놓은 blueprint라고 생각하면 됨

extends로 사용한다

abstract class Human {
  void walk();
}

// Human을 extend 한 경우 walk 메소드를 구현해야 함
class Player extends Human {
  void walk() {
    print('player is walking');
  }
}

class Coach extends Human {
  void walk() {
    print('coach is walking');
  }
}

void main() {}

 

 

24. Inheritance 상속

class Human {
  final String name;

  //Human(this.name);  // positional arguments 사용
  Human({required this.name}); // named arguments 사용

  void sayHi() {
    print('Hi, I\'m $name!');
  }
}

enum Team { red, green, blue }

class Player extends Human {
  final Team team;

  Player({
    required this.team,
    required String name,
    //}) : super(name); // name을 super 생성자에 전달 (OOP)
  }) : super(name: name); // super: 확장한 부모 클래스

  @override
  void sayHi() {
    super.sayHi();
    print('I\'m on the ${team} team!');

    //Hi, I'm ej!
    //I'm on the Team.green team!
  }
}

void main() {
  var player = Player(team: Team.green, name: 'ej');
  player.sayHi();
}

 

 


25. mixin

 

mixin: 생성자(constructor)가 없는 class
class에 property 추가할 때 등 사용함

with로 사용한다

mixin Strong {
  final double level = 100;
}

mixin Runner {
  void run() {
    print('Run!');
  }
}

mixin Tall {
  final double height = 186;
}

enum Team { red, green, blue }

class Player with Strong, Runner, Tall {
  final Team team;

  Player({required this.team});
}

class Horse with Strong, Runner {}

class Kid with Runner {}

void main() {
  var player = Player(team: Team.green);
}

 

 

 


 

Dart 온라인 실습 환경: https://dartpad.dev/

 

DartPad

 

dartpad.dev

 


 

 

https://nomadcoders.co/dart-for-beginners  를 참고하였습니다 🤓

 

Dart 시작하기 – 노마드 코더 Nomad Coders

Flutter 앱 개발을 위한 Dart 배우기

nomadcoders.co

 

 

728x90