Study 1주차 :: Dart 공부하기

김모디·2023년 2월 25일
0

Flutter Study

목록 보기
1/3

개요

Flutter를 공부하기 전에 Flutter의 언어인 Dart를 먼저 공부해보고자 NomadCoder의 "Dart 시작하기" 영상을 보게 되었습니다.




본론

Introduction

1. WhyDart?

[다양한 변환]

  • Dart Web은 Dart -> js로 변환해줌.
  • DArt Native는 Dart -> 여러 CPU에 맞게 변환해줌(ARM32, ARM64 ...).
  • 그외에도 Iot, 자동차 등에도 사용할 수 있다.

[UI개발에 적합:: 개발과 배포시의 컴파일 방법이 다르다.]

  • 개발중 :: JIT(Just in time)
    • JIT 컴파일러(dart VM)을 사용해 내가 쓴 코드를 바로 보며 개발할 수 있다.
    • 개발하면선 JIT 컴파일러를 다양한 디버깅 자원을 이용하며 개발할 수 있다.
    • 가상머신이다보니 동작이 느릴 수 있지만 바로바로 확인이 가능하다.
  • 배포 시 :: AOT(Ahead of Time)
    • 앱을 배포했을 땐 빠르게 동작되는걸 바라기 떄문에 앱을 배포할떄는 AOT으로 컴파일하여 실제 기계어가 되어 빠르게 동작할 수 있게한다.

[null safety]

  • java나 c++등에서 문제가 되는 null 에러를 방지한다.

2. Why Flutter Choose Dart?

  • AOT와 JIT 둘다 가능하다.

    • 개발 시 빠른 체크와 배포 시 빠른 동작이 가능하다.
  • 둘다 구글이 만듦.

    • 플러터가 언어를 쓰며 필요한 기능을 같은 구글이니 빠르게 수정 및 추가하여 사용할 수 있다. -> Flutter에 친화적인 언어이다.
    • EX AOT Toolchain

3. 개발 준비

  • DartPad를 사용해 줍니다.
  • void main(){} 안에서 시작해야한다.
  • 세미콜론을 꼭 달아줘야한다. (았느는 경우도 있기에 format을 누른다고 추가되지 않는다.)



Variables

1. 변수 선언

Var

  • 변수를 선언하는 기본적인 방식
  • 같은 타입 내에서 업데이트 가능
  • String, int 등으로 타입을 직접 선언할 수도 있다.
    • 그러나 Dart는 var를 사용하길 권장한다.(컴파일러가 스스로 알아낼 수 있기 때문이다)
  • var name 으로만 선언해두고 할당을 해두지 않으면 dynamic이다.
    • dynamic이더라도 if(name is String) { ... } 안에서는 name이 String이란걸 알기에 함수 추천 시 String 관련된 함수를 보여준다.

final

Final을 사용하면 해당 변수는 수정할 수 없어진다.

final name ="nico";
name = 'niconiconi";

위의 경우엔 에러가 발생한다.

Late Variables

  • final이나 var, String 앞에 late를 붙여 사용한다.
  • 초기 데이터 없이 변수를 선언할 수 있게 해준다.
  • data Fetching에 용이하다.(필요한 데이터가 뭔지는 알지만 데이터가 없을 경우)
late final String name;
// do Something
print(name); // 컴파일에러난다. (할당전에 name에 접근하지말것)
name="nico";
name ="ninicoco"; // 컴파일에러난다. (한번만 할당가능함)

Constant Variables

  • compile-time constant를 만들어준다. (java의 static같음)
  • 수정할 수 없다.
  • 선언과 할당을 함께 한다.
  • 상수 선언해두듯이 사용한다.
  • js의 const는 final과 비슷한 것이므로 헷갈리지 않도록 주의할 것.
const name="nico";



2. nullable Variables

null Safety

null safety언어다 보니 런타임 중 발생할 수 있는 null관련 에러를 컴파일 과정에서 막아버린다. (ex. noSuchMethodError)

nullable

=> null이 필요한 경우엔 어떡하나요?
?를 사용하면 됩니다.

String nico = "nico";
nico=null;

에서는 에러가 나지만,

String? nico = "nico";
nico = null;

는 에러가 나지 않습니다.

null chain (?.)

nico?.isNotEmpty; 를 통해 nico가 null이 아니라면 isNotEmpty 속성을 반환하란 의미



Data Types

1. Basic Data Types

 String str = "String";
 bool b = false;
 int i = 3;
 double d = 12.12;
 num n = 14; // (많이 쓰진 않지만, int double의 부모클래스임)
  • 위의 모든 자료형은 다 Object다. => Dart는 OOP였음.
  • bool, String, int, double 다 class로 이뤄져있다.

String interporation

: text에 변수를 추가하는 방법 (js랑 ``쓰는거랑 비슷하다)

  • "" 를 써도 되고, ''를 써도 된다.
  • ''을 썼는데 '가 안에 들어가면 I\'m을 쓰면 된다.
var name = "nico";
var age = 12;
var greeting = 'hello everyone, my name is $name';
var greeting = 'I'm ${age+2} years old'; // 계산시엔 {}로 감싸줘야한다.

2. Lists

선언하기

var numbers = [1,2,3,4,5];
List<int> numbers2 = [1,2,3,4];
  • List도 class로 이뤄진 Object다.

collection if

var giveMeFive = true;
var numbers = [1,2,3,4, if(giveMeFive) 5]; 
  • giveMeFive가 true면 5를 넣는다는 의미다.

collection for

var oldFriends = ['nico', 'lynn'];
var newFriends = [
  'lewis','ralph','darren', for(var friends in oldFriends)  "of $friend"
  ];

  • 이런식으로 사용할 수 있다.

Maps

  • js나 ts의 object, pyhton의 dictionary같은 존재다.
  • string과 object 쌍으로 모든 자료형이 Object인 dart에서는 어떤 값이든 저장할 수 있따.
var player = {
	'name' : 'nico',
    'xp': 19.99,
    'super': false,
 };

다양한 자료형 쌍을 만들떈 아래와같은 방법을 써도 된다.

Map<int, bool> player = {
  1: true,
  2: false,
  3: true,
}
  
Map<List<int>, bool> player = {
  [1,2,3]: true,
  [3,4]: false,
  [5]: true,
}

Sets

Set<int> numbers = {1,2,3,4};
numbers.add(1);
  • 각 값이 유니크 (List와 다른 점)
    • 중복을 허용하지 않는 자료형.
    • 같은 내용을 더하면 변화가 없다.
  • 순서가 있다. (List와 동일한 점)



Functions

void sayHello(String name){
  print("hello $name nice to meet you!");
}
num plus (int a, int b) => a+b;
  • 자료형(반환값이 없으면 void) 함수명(파라미터자료형 파라미터명){ ... } 형식을 갖는다.
  • void main() {} 내외 상관없이 선언해둘 수 있다.

1. arrow Functions

String sayHello (String potato){
  return "Hello $potato";
}

String sayHello (String potato) => "Hello $potato";

2. Named Parameter

// before
String hello(String name, int age, String country){ ... }
...
hello("sujeong", 25, "korea");

// after
String hello(String name, int age, String country){ ... }
...
hello(name: "sujeong", country:"korea", age: 25);
  • 단순히 순서를 기억해서 넣는 대신 이름을 명명하며 넣을 수 있다.
  • null safety problem
    • named Parameter를 쓰면 필요한 값을 다 안넣는 경우가 발생할 수 있다.
    • 해결법
      • default를 넣는다.
        String hello({String name ="dname", int age=0, String country="korea"}){ ... }
      변수를 다 적지 않아도 default값이 들어간다.
      • required modifier를 추가한다.
        String hello({required String name, required int age,  required String country}){ ... }
      이 경우 변수를 다 적지 않으면 컴파일 에러가 발생한다.

3. Optional Positional Parameter

String hello(String name, int age, [String? country = "cuba"]){ ... }
  • 대괄호로 감싸고 ?를 넣어 nullable로 설정한뒤 default값을 넣어준다.
  • country가 없더라도 default를 넣어 잘 작동하게 한다.
  • 자주 사용하진 않는 문법이다.

4. QQ Operator (js랑 동일함)

??

String nameUpperCase(String? name){ 
  return name.toUpperCase(); // null safety 위반
}

void main(){
  nameUpperCase("nico");
  nameUpperCase(null);
}
  • name이 null이 될 수 있는 상황에서 문제없이 값을 반환하고자 할때 qq Operator를 사용한다.

     // 이렇게 해결할 수도 있지만,
    String nameUpperCase(String? name){ 
      return name!=null ? name.toUpperCase() : '없다';
    }
    // 이게 더 짧다. (js랑 동일함)
    String nameUpperCase(String? name){ 
      return name.toUpperCase() ?? '없다';
    }

??=

String? name;

// name이 null이면 nico를 할당하란 의미
name ??= "nico";
name ??= "이젠 할당되었으니 이건 실행될 일 없지";
  • 변수가 null이라면 값을 할당하란 의미로 사용한다.

Typedef

typedef ListOfInts = List<int>;

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

void main(){
  reverseListOfNumbers([1,2,3]);
}
  • typedef는 자료형에 alias를 붙이는 기능을 말한다.



Class

class Player {
  late final String name;
  int xp = 1500;
  final String familyName = "못바꾸는이름"; // 밖에서 바꿀 수 없다.
  
  // 기본형 constructor
  Player(String name, int xp){
  	this.name = name;
    this.xp = xp;
  }
  
  // 타입선언된 전역변수일 경우
  Player(this.name, this.xp);
  
  void sayHello(){
  	var xp = 2000;
  	print("Hi my name is $name, global vairable is ${this.xp} and local variable is $xp"); 
    
  }
}
void main(){
	var plater = Player("뿌뿌", 3000);
    print(player.name);
    player.sayHello();
}
  • class는 위와같은 형태로 생겼다.
  • class는 프로퍼티와 함수를 가질 수 있다.
  • class 내의 전역변수는 타입을 지정해줘야한다.
  • 생성자는 위와 같이 작성할 수 있다.

1. Named Constructor Parameters

  • 프로퍼티가 엄청많아지면 constructor에 넣어야하는 값을 추가로 작성해야할텐데 이 순서를 기억하는게 쉽지는 않다.
  • function에서와 동일하게 constructor에서도 {}로 감싸 named Constructor Parameters를 적용할 수 있다.
class Player{
	String name;
    int xp;
    String team;
    int age;
    
    // required를 넣어줘야 null-safety를 유지할 수 있다.
	Player({required this.name, required this.xp, required this.team, required this.age});
    
    
    ...
}

void main(){
	// Flutter에선 해당방식을 많이 사용한다.
	var player = Player(name:"nico", xp:2000, team:"blue", age:21)
    });
    var player2 = Player("lynn", 2500, "blue", 12);
}

2. Named Constructors


class Player {
	String name;
    int xp;
    String team;
    int age;
    
	Player({required this.name, required this.xp, required this.team, required this.age});
    
    // named Constructor (named)
    Player.createBluePlayer({
    	required String name, 
        required int age
    }) : this.age = age, 
    this.name = name,
    this.team = "blue",
    this.xp = 0;
    
    // named Constructor (positional)
    Player.createRedPlayer(String name, int age) 
    : this.age = age,
    this.name= name,
    this.team = "red",
    this.xp = 0;
    
    // enhanced case
    Player.fromJson(Map<STring, dynamic> playerJson)
    : name = playerJson['name'],
    xp = playerJson['xp'],
    team = playerJson['team'];
    
  	void sayHello(){
      	print("Hi my name is $name and xp is $xp"); 
    }
}

void main() {
    // use named Constructor (named)
	var redPlayer = Player.createBluePlayer(name:"susu", age: 23);
    // used named Constructor (positional)
    var redPlayer = Player.createRedPlayer("titi", 30);
    // enhanced case
    var apiData = [
    	{
          "name": "dodo",
          "team": "blue",
          "xp":0,
        },
        {
          "name": "bubu",
          "team": "red",
          "xp":0,
        },
        {
          "name": "babe",
          "team": "red",
          "xp":0,
        },
    ];
    
    apiData.forEach((playerJson) {
    	var p = Player.fromJson(playerJson);
        p.sayHello();
    });
    
        

}
  • Constructor의 특정값을 고정한 새로운 constructor를 만들 수 있다.

3. cascade Notation

// 기본 모양
void main(){
	var nico = Player({name: 'nico', xp:1200, team: 'red');
    nico.name="las";
    nico.xp = 1200000;
    nico.team = "blue";
 }
    

위와 같은 기본모양의 코드가 다음과 같은 코드의 모양으로도 표현될 수 있다.

// cascade notation 모양 : Player선언에서 ;을 지우고, nico.name => ..name으로 변경된것이다.
void main(){
	var nico = Player({name: 'nico', xp:1200, team: 'red')
    ..name="las"
    ..xp = 1200000
    ..team = "blue";
 }

덕분에 데이터 저장을 위한 변수 생성을 피하고, 간결하고 축약된문법으로 값을 할당할 수 있다.
근데 왜 cascade notation일까?
cascade라고하면 css(Cascade Style Sheet)가 생각나는데, 이와 연관된 것일까?

  • 순간 의문
    chatGPT에게 물어봤더니, "연속적으로 ..을 이용해 값을 할당하는 것이 마치 소폭포(cascade)처럼 보이기 때문에, 이를 Cascading Notation이라고 부릅니다." 라는 해답을 얻었다.
// cascade notation 모양 : Player선언에서 ;을 지우고, nico.name => ..name으로 변경된것이다.
void main(){
	var nico = Player({name: 'nico', xp:1200, team: 'red')
    ..name="las"
    ..xp = 1200000
    ..team = "blue";
 }

꼭 인스턴스를 생성할 때 되는게 아니다. 인스턴스를 참조하는 변수에 위와같이 이어붙이더라도 정상적으로 값을 할당하고 실행할 수 있다.

4. Enum

이넘은 자바에서도 보았던 개념으로, 오타를 방지하여 작성하는데 크게 도움이 된다. 또한 선택의 폭을 줄여 개발자가 어떤 값을 넣을지 빠르게 알아챌 수 있게 한다.

enum Team { red, blue }

class Player {
	String name;
    Team team;
    
    Player ({required this.name, required this.team})
    
}
...
void main(){
	var player = Player({name="nico, team=Team.red})
}

Color같은 경우에 많이 사용되고, 생성보단 이미 만들어진 enum을 가져오는 경우가 많을 것 같다.

5. Abstract Classes

abstract class Human {
	void walk();
}

class Player extends Human {
	// 기존 Player 클래스 그대로 사용하되, walk라는 함수가 없으면 에러가 난다.
   	void walk(){
    	print("I'm walk")
    }
}

class Coach extends Human {
	...
    void walk(){
    	print("the coach is walking");
    }
        
}

abstract class는 만들 클래스의 청사진 정도로 보면 된다.

6. Inheritance

class Human{
	final String name;
    Human(this.name);
    
    void sayHello(){
    	print("Hi my name is $name");
    }
}

class Player extends Human {
	final Team team;
    
    // 아래와 같이 super를 이용해 부모 클래스의 생성자를 부를 수 있다.
    Player({required this.name, required String name}) : super(name)
}

void main(){
	var p = Player(team: Team.red, name'nico');

Human 클래스에 있는 내용을 Player에 그대로 가져오고자 사용되는 개념을 말한다. 이제 Human을 상속받은 Player에서는 sayHello함수가 없지만 함수를 실행할 수 있다.

...
class Player extends Human {
	final Team team;
    
    // 아래와 같이 super를 이용해 부모 클래스의 생성자를 부를 수 있다.
    Player({required this.name, required Team team}) : super(name)
    
    // 생성자를 아래와 같이 좀 더 이쁘게 적을 수도 있다.
    Player({
    	required this.team,
        required String name,
    }):super(name);
    
    @override
    void sayHello(){
    	// super를 통해 부모의 sayHello를 부르고 이후작업을 실행할 수도 있다.
        super.sayHello();
  		print("and I play for $team");
    }
}
...

만약 상속받은 함수를 override하고 싶다면 어떻게 해야할까? 정답은 notation mark를 이용하여 @override를 적고, super를 사용하면 부모의 함수를 호출한뒤 이후 내 작업을 기술할 수 있고, 그렇지 않다면 완전히 오버라이드하여 내 작업을 실행할 수도 있다.

7. Mixins

class Strong {
	final double strengthLevel = 1500.99;
}
class QuickRunner {
	void runQuick(){
    	print("ruuuuuuuun");
    }
}

// `with` 뒤에 Mixin으로 만든 생성자가 없는 클래스들을 적어주면 된다.
class Player with Strong, QuickRunner {
	final TEam 
  • Mixin은 생성자가 없는 클래스를 의미한다. (Vue의 Mixin과 비슷하다.)
  • 클래스에 프로퍼티들을 추가할 때 사용한다.
  • 하나의 클래스에 단 한번만 사용하는 거면 크게 의미가 없고, 공통된 특성을 가진 여러 클래스들에 넣어줄 때 비로소 높아진 유지보수성을 느낄 수 있다.

후기

  • Flutter는 odd, even등 가끔 몇 언어에선 작성해야했던 내용을 내장함수로 마련해줘서 코드의 길이는 짧고 가독성은 높힌게 좋았다.
  • collection for, collection if는 길어지는 코드를 줄일 수 있게 되었다.
  • Mixin을 볼 떈 Vue가 생각났고, Inheritance나 Abstract Class, Enum 등을 볼땐 자바가 생각났다. Dart라는 언어가 Java, C등을 개발하던 개발자들에 의해 만들어졌다는 이야기를 들었는데, 그래서인지 여러 언어가 섞인 기분이 들었다. 학교를 다니면서 정말 많은 언어와 프레임워크를 경험한 덕분에 새로운 개념을 배운다기보단 어떤 개념이 Dart라는 곳에 섞여있는지를 체크하며 공부할 수 있었다. (개인적으론 간략히 작성하는 문법은 Kotlin을 닮았고, 기본 개념은 Java, void main으로 시작하는 부분은 C, Mixin은 요즘 공부하고 있는 Vue가 생각났다.)
  • 이제 Dart공부를 시작으로 Flutter도 공부하고 이후에는 내가 만들고 싶은 앱을 언어의 장벽없이 빠르고 쉽게 만들어내고 싶다. 이전에 React-native를 공부할 때에는 라이브러리로 애를 많이 먹었는데, 최근 주목받았던 Flutter에서도 어려움을 겪을지 아니면 새로운 최애언어가 될지 궁금하다.
profile
안녕하세요. 1년차 (실상은 병아리 n개월차) 웹 프론트엔드 개발자입니다. tistory와 oopy를 거쳐 새로운 블로그에 공부한 내용을 기록해보고자 합니다.

0개의 댓글