반응형
    • 객체지향
    • 구글
    • User Interface 만드는 데에 최적화
    • 모든 플랫폼에서 빠르다
    • 많은 플랫폼에 컴파일가능

    0. INTRODUCTION

    0-1. Why Dart

    두 개의 컴파일러 (DART WEB/NATIVE)

    1. WEB: dart 코드 > javascript 변환 컴파일러
    2. NATIVE: dart 코드 > 여러 cpu 아키텍쳐에 맞게 변환 컴파일러 (ARM32, ARM64, 데스크탑 등)

    사물인터넷에도 사용 가능

    어떻게 컴파일 되는가 JIT / AOT(: c, c+, rust, go)

    c++ 프로그램 만들었다 > win에 배포하고싶다 > win 바이너리로 컴파일해서 바이너리 제공해야 함

    UI 만들다가 버튼 변경 > 다시 컴파일? 화면에서 바뀐 걸 바로 봐야하는데 전체 프로젝트가 컴파일 되면 좋은 개발 경험X, JIT 컴파일이 필요하다

    dartVM사용, 바로 화면에 보여줌. 가상머신에서 작동하고 있다는 거라서 조금 느리지만 오직 개발중일때만. >> During development << dart 가상머신이 jit 컴파일러 제공 with 풍부한 디버깅 자원

    앱을 배포하고 싶다면 dart VM이 아닌 aot 컴파일러인 것!

    ⇒ 개발도중: dart vm에서 실행(jit) 느리지만 바로바로 피드백

    ⇒ 배포: dart야 aot로 컴파일해줘 → 기계로 변환 → 앱 빨리 돌아감

    null safety

    안정된 프로그램

    why flutter - dart?

    1. jit/aot 둘 다 있기 때문 모바일 개발에 아 주 좋은 언어라는 것. 빠른 피드백을 원하면서도 빠른 실행
    2. dart/flutter 모두 구글이 만들어 최적화 가능 = 다른 프레임워크는 거의 불가능 flutter를 위해 dart 언어를 최적화. 실례로 원래 DART에도 native 바이너리 지원하는 aot 툴체인 없었음. 추후에 dart에 추가됨. flutter 더 빠른 실행을 위해!

    0-2. How to learn

    설치 없이 dart 연습하기: dartpad.dev ⇒ dart 에디터

    documentation 패널도 열리고, 에러도 보임, 실행도 가능

    이미 dart/flutter가 있다면: 프로젝트 하나 파서 main.dart, vs코드는 익스텐션 깔기

    잼있겠땅~


    1. VARIABLES

    1-0. Hello World

    핵심: 모든 코드는 main 함수 안에 들어가야 한다!

    main함수는 모든 dart 프로그램의 entry point

    세미콜론은 아주 중요하다! 없으면 오류가 난다.

    js/ts 등은 auto formatter가 달아주지만 dart는 아니다.

    왜일까

    세미콜론을 일부러 안 쓸 때가 있기 때문인데 나중에 배운다 [Cascade Operator]

    void main() {
    	print('hello world');
    }
    

    1-1. The Var Keyword

    **핵심:

    1. 변수를 선언할 때 var를 사용할 수도, 타입을 지정할 수도 있다.
    2. 타입만 유지된다면 변수는 업데이트가 가능하다.**
    void main() {
    	var name = 'sori'; // (O) String 추측
    	name = 1; // (X) String <> int
    	name = true; // (X) String <> bool
    }
    
    void main() {
    	String name = 'sori'; // (O) Type 지정도 가능
    }
    

    var, type 명시 각각 언제 사용해야 할까?

    1. var 관습적으로 함수나 메서드 내부에 지역변수를 선언할 때
    2. type 명시 class에서 변수나 property를 선언할 때

    ⇒ dart 스타일 가이드에 따르면, var를 가능한 많이 사용하는 게 권장되고, 타입을 지정하는 방식은 class property를 작성할 때 권장된다.

    1-2. Dynamic Type

    핵심: dynamic = 여러가지 타입을 가질 수 있는 변수에 사용하는 키워드(어쩔 수 없을 때) 이상적으로는 쓰는 걸 피해야 하는 타입

    void main() {
    	dynamic name; // var name도 되지만 dynamic이라고 명시해줄 수 있다.
    	name = 'sori'; (O)
    	name = 12; (O)
    	name = true; (O)
    }
    

    변수가 어떤 타입인지 알기 어려운 경우, 특히 flutter, json과 작업하면 유용한 경우가 있다.

    위 코드블록에서 name. 해보면(자동완성 기능) 
    dart는 어느 타입인지 모르기 때문에 메서드 추천을 해주지 않는다.
    
    하지만 타입이 확인되는 순간,
    if (name is String) {
    	name. >> String 옵션들이 나타남 (contains, replaceAll ...)
    }
    

    1-3. Nullable Variables

    핵심: Null Safety = 어떤 변수/데이터가 null이 될 수 있음을 명시하는 것

    ⇒ 어떤 데이터가 null일 때 참조하지 않도록 dart가 우릴 도와줘~

    기본적으로 모든 변수는 non-nullable → ?를 붙이면 nullable

    1. **null safety**하지 않은 코드
    bool isEmpty(String str) => str.length == 0;
    
    main() {
      isEmpty(null);
    }
    // => Runtime Error (NoSuchMethodError)
    // str이 null이다 -> length라는 메서드 찾을 수 없는 것, Runtime 오류!
    
    2. **null safety**
    void main() {
      String example = 'ex';
      example = null; (X)
    
      // null도 될 수 있다는 것을 **'?'**로 명시해주자
      String**?** example = 'ex';
      example = null; (O)
      example.isNotEmpty; (X) => Complie 단계에서 에러 잡아준다. might be null
    
      2-1) 
      if (example!= null) { // 이제 바로 String이라는 것을 안다
        example.isNotEmpty; (O)
      }
    
    	2-2) // 단축문법
    	example?.isNotEmpty; (O)
    
    }
    

    개발자가 null값을 참조할 수 없도록 한다. ⇒ 컴파일 시에 잡아내면서 런타임 에러 방지!

    이또한 dart의 초기 버전부터 있었던 기능은 아니다.

    1-4. Final Variables

    핵심: 한 번 정의된 변수를 수정할 수 없게 만들려면 final 변수를 만든다.

    void main() {
    	final name = 'sori';
    	name = 'sory'; (X)
    }
    

    JS/TS의 const와 같다.

    꼭 그럴 필요는 없지만 타입을 명시해줄 수도 있다. ex) final String

    ┗>어차피 바뀔 일 없기 때문일까??

    1-5. Late Variables

    핵심: final이나 var 앞에 붙여줄 수 있는 수식어로, 초기 데이터 없이 변수를 선언할 수 있게 해준다.

    void main() { 
    	late final String name;
    	// Do something Maybe go to api;
    	name = 'sori'; // └> 데이터를 받아서 넣어주겠지
    	name = 'yesori'; (X) final은 수정 불가.
    }
    

    값을 넣기 전에는 접근하지 말아야 한다. 예를 들어

    void main() {
    	late final str;
    	print(str); (X) 
    		// => 'The late local variable 'str' is definitely unassigned at this point.'
    }
    

    flutter로 data fetching 할 때 아주 유용하다. ↓

    Create something (late variable)

    ⇒ Going to hit the API

    ⇒ When the API comes back

    ⇒ put the data on the late variable

    1-6. Constant Variables

    핵심: dart의 const는 compile-time constant를 만들어준다.

    ⇒ const는 compile-time에 알고 있어야 하는 것!!

    void main() {
    	const name = 'sori';
    	name = 'yesori'; (X)
    }
    

    dart의 const <> JS/TS의 const ⇒ 이건 dart의 final과 비슷함. 오호~

    수정이 안 된다 = 상수이다. 이긴 하지만 API/화면 입력 등에서 정해지는 값이 아니라,

    앱스토어에 올리기 전 이미 알고 있는 value인 것

    void main() {
    	const API = 'a123ads134'; // compile-time constant (O)
    	const API = fetchApi()~ // compile-time constant (X)
    	 └> final로 사용하는 게 맞다.
    }
    

    2. DATA TYPES

    2-1. Basic Data Types

    String name = 'a'; // "a"
    bool arive = true;
    int age = 1;
    double weight = 1.25;
    

    대부분의 자료형은 Object이다.

    이 중, int와 double은 extends num

    num x = 1;
    num y = 1.1;
    

    2-2. Lists

    void main() {
    	var numbers = [1, 2, 3, 4];
    	List<int> numbers = [1, 2, 3, 4]; // same Effect
    	// 참고: 맨 끝에 ,를 붙이면 자동으로 formatting 됨
    	=> List<int> numbers = [
     			 1,
    			 2,
    			 3,
    			 4,
    		 ]
    
    	numbers.add('a'); (X)
    	numbers.add(1); (O)
    }
    

    first/last, add, addAll, clear, contains, (…)

    dart에서의 멋진 점: collection if / collection for를 지원한다.

    🧡 collection if에 대해 간략히:

    List를 만들 수 있는데, 존재할 수도 안 할 수도 있는 요소를 가지고 만들 수 있다. WHAT?

    ↓ example

    void main() {
    	var giveMeFive = true;
    	var numbers = [1, 2, 3, 4, if (giveMeFive) 5,];
    													// 만약 giveMeFive가 true => List에 5 추가
    
    	print(numbers); => [1, 2, 3, 4, 5]
    }
    

    2-2. String Interpolation

    핵심:

    String Interpolation = text에 $변수를 추가하는 방법

    void main() {
    	var name = 'sori';
    	var greeting = 'Hello, my name is $name, nice to meet you!';
    
    	print(greeting); => Hello, my name is sori,  nice to meet you!
    
    ========================================================================
    	var name = 'sori';
    	var age= 10;
    	var greeting = 'Hello, my name is $name, I\\'m ${age + 1}.'; //escape!
    
    print(greeting); => Hello, my name is sori,  I'm 11.
    }
    

    single/double quote 상관 없고, $ 뒤 변수를 사용해주기만 하면 끝

    단순 변수 값을 넣고 싶은 경우 ⇒ $변수명

    무언가를 계산하고 싶은 거라면 ⇒ ${변수명 with 연산}

    변수가 이미 존재할 때 사용하는 방식

    2-3. Collection For

    핵심:

    향상된 for문 같지만 내부 블록 없이 List 안에서 바로 사용 가능!!

    void main() {
    	var oldFriends = ['은별', '연주'];
    	var newFriends = [
    		'왕자', 
    		'공주',
    		for (var friend in oldFriends) "💖$friend",
    	];
    
    	print(newFriends); => [왕자, 공주, 💖은별, 💖연주]
    }
    

    2-4. Maps

    JS/TS의 object, Python의 dictionary 같은 것

    void main() {
    	var player = { // 컴파일러가 정한 자료형은: Map<String, Object>
    		'name': 'sori',
    		'xp': 19.99,
    		'superpower': false,
    	};
    
    	Map<int, bool> player2 = { // 직접 타입 지정할 때
    		0: false,
    		1: false,
    		2: false,
    	};
    
    	Map<List<int>, bool> player2 = { // 복잡한 타입의 key
    		[0]: false,
    		[1, 3]: false,
    		[2, 4, 5]: false,
    	};
    
    	List<Map<String,Objet>> players = { // List와 혼합
    		{'name': 'sori', 'xp': 19.99, 'superpower': false,},
    		{'name': 'soru', 'xp': 23.99, 'superpower': true,},
    	}
    	// 하지만 이와 같이 API 구조 등과 같은 데이터(특정 형태)라면 
    	// Map보다 class 사용을 권장하지!
    }
    

    2-5. Sets

    void main() {
    	var numbers = {1, 2, 3, 4,};
    	Set<int> numbers = {1, 2, 3, 4};
    
    	numbers.add(1);
    	numbers.add(1);
    	numbers.add(2);
    	print(numbers) => {1, 2, 3, 4};
    }
    

    모든 값이 Unique하고, 순서 존재

    Dart List = Python List | Dart Set = Python Tuple


    3. FUNCTIONS

    3-0. Defining a Function

    void sayHello(String name) {
    	print("Hello $name nice to meet you!");
    }
    
    void main() {
    	sayHello('sori');
    }
    

    곧바로 return되는 함수를 정의할 때

    String sayHello(String name) {
    	return "Hello $name nice to meet you!";
    } 
    를 Fat Arrow Syntax를 사용하여 바꿀 수 있다.
    
    String sayHello(String name) => "Hello $name nice to meet you!";
    
    함수가 복잡한 경우에는 물론 아래와 같이 정석대로 정의
    String sayHello(String name) {
    	// Call API
    	// Perform calculate
    	// ...
    	return "...";
    }
    
    + num 타입 추가
    num plus(num x, num y) => x + y;
    

    (arguments) 받아서 => 곧바로 return 하는 것과 같은 의미.

    코드가 한 줄밖에 없을 때, 위와 같이 사용하면 된다.

    3-1. Named Parameters

    Dart의 Function은 Named Parameter를 지원한다.

    String sayHello(String name, int age, String country) {
    	return "Hello $name, you are $age, and you come from $country!";
    }
    
    void main() {
    	print(sayHello('sori', 20, 'korea'));
    }
    
    

    Positional Parameter, 위 코드의 문제점:

    요소의 순서를 잊을 수 있고, 가독성이 좋지 않다(파라미터의 의미 알 수 없어)

    두 개 정도까지는 괜찮지만 세 개부터 복잡해지는,, 클린 코드 뭐시기법 위반!

    Named Parameter를 사용해보기

    호출 시에는 순서와 관계 없이 argument의 이름(name, age, country)들을 적어준다.

    String sayHello({
    						String name, 
    						int age, 
    						String country}) { // 중괄호 추가!
    	return "Hello $name, you are $age, and you come from $country!";
    }
    
    void main() {
    	print(sayHello(
    		age: 20, 
    		country: 'korea',
    		name: 'sori'
    	));
    }
    

    하지만 이 상태라면 null safety로 인해 컴파일 오류가 뜬다.

    해결하기 위한 방법 두 가지

    1. default값 지정
    String sayHello({
    						String name = 'anonymous', 
    						int age = 99, 
    						String country = 'wakanda'}) {
    	return "Hello $name, you are $age, and you come from $country!";
    }
    
    void main() {
    	print(sayHello());
    	// => Hello anonymous, you are 99, and you come from wakanda!
    }
    
    1. ‘required’ modifier사용
    String sayHello({
    						required String name, 
    						required  int age, 
    						required  String country}) {
    	return "Hello $name, you are $age, and you come from $country!";
    }
    
    void main() {
    	print(sayHello()); // !!Compile Error!!
    }
    

    실험해봤는데 Positional Parameter과 required는 함께 사용 불가하다. (그러고보니 이미 required…)

    3-2. Optional Positional Parameters

    Named Parameter는 적용하고 싶지 않은데, country 는 required하지 않게 하고싶다면?

    String sayHello(String name, int age, [String? country = 'wakanda']) => 
    	return "Hello $name, you are $age, and you come from $country!";
    
    void main() {
    	print(sayHello('sori', 20)); // => Hello sori, you are 20, and you come from wakanda!
    }
    

    [ ] 와 ?, default값 지정을 통해 걸러낼 수 있다.

    3-3. QQ Operator(Question Question Operator)

    핵심:

    ‘??’ | 좌항 ?? 우항 ⇒ 좌항이 null이면 우항을 return

    ‘??=’ | 좌항이 null 이면 우항을 대입

    🧡 이름을 대문자로 리턴하는 함수를 만들어보자.

    String capitalizeName(String name) => name.toUpperCase();
    

    사용자가 null도 입력할 수 있으면 좋겠다. 그래서 파라미터를 String? name 으로 바꿔준다.

    String capitalizeName(String? name) => name.toUpperCase();
    

    ‼하지만 이러면 toUpperCase를 사용할 수 없다.

    해결방안은 세 가지가 있다.

    1. name에 대해 null check 수행
    String capitalizeName(String? name) {
    	if (name != null) {
    		return name.toUpperCase();
    	}
    	return 'ANONYMOUS';
    }
    
    1. 삼항 연산자 사용
    String capitalizeName(String? name) => 
    	name != null ? name.toUpperCase() : 'ANONYMOUS';
    
    1. QQ 사용
    String capitalizeName(String? name) =>
    	name?.toUpperCase() ?? 'ANONYMOUS';
    

    💚 QQ equals (혹은 QQ asignment operator) 알아보기

    ⇒ null이라면 값을 할당해준다.

    void main() {
    	String? name;
    	name ??= 'sori';
    	name = null;
    	name ??= 'yesori';
    
    	print(name) => // yesori
    
    	String? name;
    	name ??= 'sori';
    	name ??= 'yesori'; // 해당 줄은 실행될 일 NEVER
    
    	print(name) => // sori
    }
    

    3-4. Typedef

    자료형이 헷갈릴 때 도움이 될 alias를 만드는 방법

    긴 코드를 만났을 때 유용할 것!

    🧡 1. 숫자로 된 List를 반대로 뒤집어서 return 하는 함수를 만들어보자.

    List<int> reverseListOfNumbers(List<int> list) {
    	var reversed = list.reversed; // => List와는 조금 다른 iterable이 되어 toList() 필요
    	return reversed.toList();
    }
    
    1. List<int>에 대한 alias를 지정해보자.
    typedef ListOfInts = List<int> // ‼
    
    ListOfInts reverseListOfNumbers(ListOfInts list) {
    	var reversed = list.reversed;
    	return reversed.toList();
    }
    

    원하는 만큼의 typedef를 생성할 수 있다.

    💚 Map도 만들어보자. 사용자에게 인사하는 함수를 Map type의 alias와 함께 만들어보자.

    typedef UserInfo = Map<String, String>;
    
    String sayHi(UserInfo userInfo) { // == String sayHi(Map<String, String> userInfo) {
    	return "Hi ${userInfo['name']}";
    }
    

    이전에도 정리했지만 구조화된 data 형태를 지정하고 싶다면 class를 사용하는 것이 좋다!


    4. CLASSES 💫

    4-0. Your First Dart Class

    1. class 작성해보기

    ✅ class에서 property 선언할 때는 clear하게 타입 정의하기

    class Player {
    	String name = 'sori';
    	int xp = 1500;
    }
    
    1. Player class의 Instance 생성해보기(Object)
    void main() {
    	var player = Player(); // **‘new’ 키워드는 쓸 수는 있지만 쓸 필요 없다.**
    	// Do Something with player ...
    }
    
    1. variables modifier에 대해 배운 것을 토대로 class에 적용해보자
    class Player {
    	final String name = 'sori';
    	int xp = 1500;
    }
    
    void main() {
    	var player = Player();
    	player.name = 'ddori'; (X) // final 변수 변경 불가
    }
    
    
    1. player가 인사하는 메서드를 생성해보자.
    class Player {
    	final String name = 'sori';
    	int xp = 1500;
    
    	void sayHello() {
    		print("Hello my name is $name"); **// this를 쓸 수는 있지만 쓸 필요 없다.**
    	}
    }
    
    void main() {
    	var player = Player();
    }
    

    ✅ Class method에서의 this는 사용하지 않도록 권고되어있다. variable과 class property의 이름이 겹치는 게 아니라면… ↓example

    class Player {
    	final String name = 'sori';
    	int xp = 1500;
    
    	void sayHello() {
    		var name = 'example';
    		print("Hello my name is ${this.name}");
    		// 이 때 player의 이름에 접근하고 싶다면 this를 사용한다.
    	}
    }
    

    4-1. Constructors

    class Player {
    	**late** final String name;
    	**late** int xp;
    
    	Player(String name, int xp) {
    		this.name = name; // final 변수라서 late를 붙이지 않으면 안 됨! Advantage of 'late'
    		this.xp = xp;
    	}
    }
    
    void main() {
    	var player = Player('sori', 1500);
    	var player = Player('ddori', 1800);
    }
    

    Java 문법이랑 아주 유사하다.

    하지만위 코드에서 Dart는 이미 name과 xp의 타입을 알고 있다. 너무 반복적인 작업을 하고 있다.

    🧡 Constructor 코드를 작성하는 더 나은 방법 How cool is that~

    1. late 지워
    2. this.property = argument 할당 지워
    3. 들어온 parameter가 어디에 할당될지 바로 명시 ↓example
    class Player {
    	final String name;
    	int xp;
    
    	Player(this.name, this.xp);
    }
    

    Beautiful!

    4-2. Named Constructor Parameters

    4-1에서 작성한 Constructor를 통한 객체 생성 ⇒ Using Positional Parameters (depends on the position)

    class Player {
    	final String name;
    	int xp;
    +) 추가
    	String team;
    	int age;
    
    	Player(this.name, this.xp, this.team, this.age);
    }
    
    void main() {
    	var player = Player('sori', 1500, 'red', 20);
    	//   ⇒ 생성자 호출 시 도무지 의미를 알 수 없는 parameter들!!
    }
    

    🧡 Let’s Use Named Constructor Parameters

    class Player {
    	final String name;
    	int xp;
    	String team;
    	int age;
    
    	Player({this.name, this.xp, this.team, this.age}); // **{ }로 감싸기**
    	// 하지만 이렇게 되면 위 argument들은 might be null >> ERROR!
    }
    
    void main() {
    	var player = Player(
    		name: 'sori', 
    		xp: 1500, 
    		team: 'red', 
    		age: 20
    	);
    }
    
    ⇒ Looks much much better **BUT**
    

    null safety에 걸리는 생성자 argument들을 어떻게 잡아줘야 할까? (복습)

    1. default
    2. required
    class Player {
    	final String name;
    	int xp;
    	String team;
    	int age;
    
    	Player({
    		required this.name, 
    		required this.xp, 
    		required this.team, 
    		required this.age
    	}); // 💖 HOW NICE IS THIS!
    }
    
    void main() {
    	var player = Player(
    		name: 'sori', 
    		xp: 1500, 
    		team: 'red', 
    		age: 20
    	);
    }
    

    4-3. Named Constructors

    Flutter에서 constructor를 만들 때 많이 사용하는 패턴 중 하나

    🧡 Named Constructor 예시

    • 객체가 생성되었을 때 xp는 기본 1500, 팀은 각각 blue/red로 정해주고 싶다.
    • 이름과 나이는 입력받는다.
    class Player {
    	final String name;
    	String team; // 이것두 합치면 둘 다 final이 되니 주의
    	int xp, age; // 한 번에 선언 가능
    
    	Player({
    		required this.name, 
    		required this.xp, 
    		required this.team, 
    		required this.age
    	});
    
    	// [1] BLUE: Named Constructor with Named Syntax Parameter
    	Player.createBluePlayer({   // 1. 생성자 추가
    		required this.name,       // 2. 입력 부분은 동일
    		required this.age, 
    	}) **:** this.age = age,        // 3. 콜론 뒤에서 초기화 해주기
    			 this.name = name, 
    			 this.team = 'blue', 
    			 this.xp = 1500;
    		 ↑ ❗ ❗ ❗ 
    
    	// [2] RED: Named Constructor with Positional Syntax Parameter
    	Player.createRedPlayer(String name, int age) 
    		**:** this.age = age,
    			this.name = name, 
    			this.team = 'red', 
    			this.xp = 1500;
    
    	// [3] 댓글에서 읽었음 !
    	Player.createWhitePlayer(
        this.name,
        this.age,
        [this.team = 'white', this.xp = 1500]
      );
    }
    
    void main() {
    	var bluePlayer = Player.createBluePlayer(name: 'sori', age: 20); // Named
    	var redPlayer = Player.createRedPlayer('ddori', 21); // Positional
    	var whitePlayer = Player.createWhitePlayer('ddoddoriri', 30);
    }
    
    1. 생성자 추가: 기본 생성자 Player() 외에 또다른 생성자를 추가한다. 이름은 각각 Player.createBluePlayer( ) / Player.createRedPlayer( )
    2. 입력 부분은 동일: 파라미터를 입력받는 부분은 동일 (name, age)
    3. 콜론 뒤에서 초기화 해주기: 기본값 지정을 위해서는 특이한 문법이 사용된다. Player.createBluePlayer({ 입력 파라미터 }) : this. … (위 코드블럭 참조)

    4-4. Recap

    우리가 맞닥뜨릴 일.

    🧡 API로부터 받은 데이터(Maybe Json format data)를 받아서, Dart Class로 바꾸기

    1. Json Data를 받아왔다고 가정해보자: var apiData [{…}]
    class Player {
    	final String name;
    	String team;
    	int xp;
    }
    
    void main() {
    	var apiData = [
    		{
    			"name": "sori",
    			"team": "blue",
    			"xp": 1000,
    		},
    		{
    			"name": "ddori",
    			"team": "blue",
    			"xp": 1000,
    		},
    		{
    			"name": "yesori",
    			"team": "blue",
    			"xp": 1000,
    		},
    	];
    }
    
    1. ‘fromJson’이라는 Named Constructor 생성 많은 Flutter 앱이 사용하는 패턴이다. 사용해보자.
    class Player {
    	final String name;
    	String team;
    	int xp;
    
    	Player.fromJson(Map<String, dynamic> playerJson) :
    		name = playerJson['name'],
    		team= playerJson['team'],
    		xp = playerJson['xp'];
    
    	void sayHello() {
    		print('Hello! my name is $name');
    	}
    }
    
    1. 2번에서 만든 생성자로 데이터를 확인해보기
    void main() {
    	var apiData = [
    		{
    			"name": "sori",
    			"team": "blue",
    			"xp": 1000,
    		},
    		{
    			"name": "ddori",
    			"team": "blue",
    			"xp": 1000,
    		},
    		{
    			"name": "yesori",
    			"team": "blue",
    			"xp": 1000,
    		},
    	];
    
    	apiData.forEach((playerJson) {
    		var player = Player.fromJson(playerJson);
    		player.sayHello();
    	});
    }
    
    /* 
    	main 실행 시
    	=>  Hello! my name is sori
    			Hello! my name is ddori
    			Hello! my name is yesori
    */
    

    4-5. Cascade Notation

    다시 기본적인 Named Syntax로 작성했던 코드로 진행한다.

    class Player {
    	String name;
    	String team;
    	int xp;
    
    	Player({
    		required this.name, 
    		required this.team, 
    		required this.xp,
    	});
    
    	void sayHello() {
    		print('Hello! my name is $name');
    	}
    }
    
    void main () {
    	var sori = Player(name: 'sori', team: 'blue', xp: 1200);
    	sori.name = 'yesori';
    	sori.xp = 12000;
    	sori.team = 'white';
    }
    

    위 코드에서 sori. sori. sori. 가 계속 반복된다 …

    syntax sugar를 뿌려보자🍭

    void main () {
    	var sori = Player(name: 'sori', team: 'blue', xp: 1200) // 세미콜론 떼고!
    	..name = 'yesori'
    	..xp = 12000
    	..team = 'white';
    } **MUCH LESS CODE!**
    
    void main () {
    	var sori = Player(name: 'sori', team: 'blue', xp: 1200);
    	var potato = sori
    	..name = 'yesori'
    	..xp = 12000
    	..team = 'white';
    
    	print(sori.name); // => yesori
    	print(potato.name); // => yesori 이해가 잘 ... 깊은 복사?
    }
    

    4-6. Enums

    Enum은 텍스트 형태로 작성하지 않는다.

    enum Team { red, blue }
    enum XPLevel { beginner, medium, pro }
    
    class Player {
    	String name;
    	Team team;
    	XPLevel xp;
    
    	Player({
    		required this.name, 
    		required this.team, 
    		required this.xp,
    	});
    }
    
    void main () {
    	var sori = Player(name: 'sori', team: Team.blue, XPLevel : XPLevel.beginner);
    }
    

    4-7. Abstract Classes

    추상화 클래스 = 객체 생성 불가, blueprint

    extends

    abstract class Human {
    	void walk();
    }
    
    enum Team { red, blue }
    enum XPLevel { beginner, medium, pro }
    
    class Player extends Human {
    	// 생략 ...
    	**void walk() {**
    		print('I\\'m walking');
    	}
    }
    
    class Coach extends Human {
    	**void walk() {**
    		print('The coach is walking');
    	}
    }
    
    void main () {
    }
    

    4-8. Inheritance

    상속 ⇒ 부모 생성자 호출 필요, 얘도 : 문법 사용

    메서드 Overriding 가능: 부모 메서드는 호출 하든말든 상관 X

    class Human {
    	final String name;
    
    	Human({required String this.name});
    	// Human(this.name);
    
    	void sayHello() {
    		print('Hello my name is $name.');
    	}
    }
    
    enum Team {blue, red}
    
    class Player extends Human {
    	final Team team;
    
    	Player({
    		required this.team,
    		required String name // <- 얘를 Human 생성자로 전달해줘야함.
    	}) : **super(name: name)**; // 맨 위 주석 해제 시 => super(name); (Positional)
    
    	@override
    	void sayHello() {
    		super.sayHello();
    		print('and I play for ${team}.');
    	}
    
    }
    
    void main () {
    	var player = Player(name: 'sori', team: Team.blue);
    	player.sayHello();
    }
    

    4-9. Mixins

    생성자가 없는 클래스

    상속은 부모클래스를 확장하고, super를 통해 부모에 접근하며, 부모 클래스의 인스턴스가 되지만,

    Mixin은 with 키워드를 통해 단순히 내부의 속성과 메서드를 뺏어온다. Super Super Useful

    1. 여기 Player라는 클래스가 있다.
    enum Team {blue, red}
    
    class Player {
    	final Team team;
    
    	Player({
    		required this.team,
    	});
    }
    
    1. Mixin 클래스를 세 개 만들어본다.
    2. ** 버전 상승에 따라 class Strong (X) ⇒ mixin Strong || mixin class Strong (O)
    mixin Strong {
    	final double strengthLv = 1399;
    }
    
    mixin QuickRunner {
    	void runQuick() {
    		print('ruuuuun~~~!');
    	}
    }
    
    mixin class Tall {
    	final double height = 189;
    }
    
    1. Mixin 클래스를 활용해본다. ‘with
    mixin Strong {
    	final double strengthLv = 1399;
    }
    
    mixin QuickRunner {
    	void runQuick() {
    		print('ruuuuun~~~!');
    	}
    }
    
    mixin class Tall {
    	final double height = 189;
    }
    
    enum Team {blue, red}
    
    class Player with Strong, QuickRunner, Tall {
    	final Team team;
    
    	Player({
    		required this.team,
    	});
    }
    
    class Horse with Strong, QuickRunner {}
    class Kid with QuickRunner {}
    
    void main () {
    	var player = Player(team: Team.blue);
    	player.runQuick();
    }
    

    ⇒ 다른 클래스의 property, method를 그냥 긁어오는 것으로, 상속과는 다르다.

    FINISHED!

    반응형

    댓글