민프

[Flutter] Dart언어에서 알면 좋은 점 (Feat. 객체지향언어) 본문

[Flutter]

[Flutter] Dart언어에서 알면 좋은 점 (Feat. 객체지향언어)

민프야 2023. 8. 7. 16:51

 

Main 함수

Dart에서 Main함수는 모든 Dart 프로그램의 Entry point이기 때문에 중요하다

만약 main을 다른 함수명으로 바꾼다면 아래 사진과 같이 Run | Debug 내용이 없어지게 되고, 실제로 이 코드를 강제로 실행하려고 하면

'main'이 없어서 에러가 나게 된다. 


세미클론

세미클론을 끝에 꼭 붙여줘야한다. 


변수 - 선언

변수를 선언하는 방식에는 2가지가 있는데

결론적으로 Dart 스타일가이드에서는 메소드 안에서 지역변수를 선언하는 상황이라면 var을 사용하고,

class에서 변수나 property를 선언할 때에는 타입을 지정해주는 것을 권장하고 있다.

1. Var 변수

TypeScript와 같은 것 같은데 아래 코드와 같이 변수의 타입을 꼭 구체화 할 필요는 없다.

void main(){
  var id = "민프";
}

 

왜냐하면 Dart 컴파일러는 id가 String(문자열)이라는 것을 알고 있기 때문이다. 

아래 사진을 보면 'id' 에 마우스 커서를 대면 현재 인식 된 타입을 보여주는데 String이라고 인식하고 있는 것을 확인할 수 있다.

하지만 그 이후에 다른 타입의 값을 넣으면 에러가 날 수 있다

 

 

2. 명시적 선언

아래 사진과 같이 명시적으로 앞에 변수를 넣어서 선언을 해줄 수 있는데, 똑같이 String으로 잘 인식 된 것을 확인할 수 있다. 

2-1. Dynamic 타입

TypeScript에서 any타입과 같은건데 결론적으로 여러가지 타입을 가실 수 있는 변수에 쓰는 키워드이다.

아래 사진과 같이 선언 할 때 변수에 아무것도 지정해주지 않으면 dynamic으로 선언이 되게 되는데

사진에서와 같이 dynamic으로 선언 된 id에다가 String값과 number값을 넣어줬는데 에러가 나지 않는 것을 확인할 수 있다.

 

아래 코드와 같이 var로 선언하지 않고 dynamic으로 선언하고 사용해도 된다.

void main(){
  dynamic id;
  id ="민프";
  id =123;
}

만약 하나의 변수에 여러가지 타입이 사용되는데 그에 따른 분기 처리를 하고싶다면 

아래 코드와 같이 if문을 사용해서 분기 처리를  해주면 된다. (하지만. .이렇게 작업하는건 추천하지 않는다.)

void main(){
  dynamic id;

  if(id is String){
    id.toString();
  }

  if(id is int){
    id.compareTo(other);
  }
}

 

변수 - Null Safety

https://dart-ko.dev/null-safety/understanding-null-safety

 

Understanding null safety

A deep dive into Dart language and library changes related to null safety.

dart-ko.dev

 

null Safety란 어떤 변수, 데이터가 null이 될 수 있음을 명시하는 걸 말하는데

실 서비스를 하는 입장에서는 필수적으로 대응을 해야하는 부분이다.

 

기본적으로 변수를 선언하면 거의 모든 변수는 Non-nullable인데 여기에서 변수 앞에 '?'를 붙이면 null이 될 수 있다는 것을 알려주는 의미를 부여한다.

void main(){
  String? id = '민프';
  id = null;
  if(id != null){
    // Null 이 아닐 때 실행
  }
}

이것을 아래 코드와 같이 사용할 수 있다. 

void main(){
  String? id = '민프';
  id = null;
  id?.isNotEmpty; // null이 아니라면 isNotEmpty 속성을 요청
}

변수 - Const

javascript, Typescript의 const는 Dart의 final과 비슷한데

Dart의 const는 JavaScript, Typescript와 다르다. 

 

dart에서의 const는 compile-time constant를 만들어준다. 

https://dart-tutorial.com/useful-information/final-vs-const-in-dart/

 

Final Vs Const

Learn Dart Programming

dart-tutorial.com

위 링크를 참고해서 compile-time constant가 뭔지 말해보자면

결론적으로 단순히 final이 변수가 다시 할당되지 않음을 의미하고,

const는 컴파일타임에 상수 값을 평가하여 변수에 할당하는 것을 의미하는데,

프로그램이 실행되기 전에 다음 개체가 인스턴스화될 수 있고 인스턴스화될 것임을 의미한다.

 

정리하자면  프로그램이 실행되기 전 고정값을 지정해주는 건 const

프로그램이 실행되고 나서 값을 고정 시켜주는건 final이라는 것이다.

 

쉽게 말하자면 

프로그램이 실행되고 나서 키보드로 id값을 입력해서 그 id값을 변경되지 않도록 선언하는 건 final

프로그램이 실행되기 전 id값을 하드코딩을 해서 변경되지 않도록 선언하는건 const 이라는 것이다.

 

const와 final변수에 대해서는 아래 링크에서 알아보았으니 참고해주세요

https://minf.tistory.com/226

 

[Flutter] const, final의 공통점과 차이점 (feat. Runtime, Compile Time)

https://minf.tistory.com/223 [Flutter] Dart언어에서 알면 좋은 점 (Feat. 객체지향언어) Main 함수 Dart에서 Main함수는 모든 Dart 프로그램의 Entry point이기 때문에 중요하다 만약 main을 다른 함수명으로 바꾼다

minf.tistory.com

 

DataTypes - Set 과 List의 차이점

set에 속한 모든 아이템들은 유니크하고 
List에 속한 아이템들은 유니크 하지 않는다. 

 

 

 


함수 - Named Parameters, Name Argument

TypeScript에도 있는 부분인데 함수에다가 파라미터를 지정할 때 순서를 기억하는게 아니라 파라미터 변수 명을 입력하게 해서 직관적으로 볼 수 있게 하고, 필요한 필수 파리미터 값과 옵션 값을 나눌 수 있다.  


// Name Argument + Name Parameters => defalut 값 지정
String userInfo2({String name ="defalut", int age= 0}){
  return "userName : $name, userAge: $age";
}
// Name Argument + Name Parameters => required 필수 값 지정
String userInfo3({required String name, required int age}){
  return "userName : $name, userAge: $age";
}
// 옵션 값 지정
String userInfo4(String name, int age, [String? contry ="Seoul"]){
  return "userName : $name, userAge: $age";
}


void main(){
  print(userInfo2(name: "민프", age: 10));
}

 


클래스(Classes) - 생성자(Constructor)

여러 객체지향언어에서 사용되는 클래스와 생성자를 사용해서 로직을 설계하는데 코드는 아래와 같다.

 

class User{
  late String name; // 초기값을 설정해주던지 아니면 defalut값을 설정해놓던지
  late int age; // 초기값을 설정해주던지 아니면 defalut값을 설정해놓던지

  User(String name, int age){
    this.name = name;
    this.age = age;
  }

  void welcomeUser(){
    print("Hello $name Welcome To Our Service" );
  }
}

class Master{
  String name; 
  int age; 

  Master(this.name, this.age); //이런식으로 하면 late를 사용하지 않아도 된다.

  void welcomeMaster(){
    print("Hello $name Welcome To Our Service" );
  }
}

void main(){
  var user1 = User("민프",10);
  user1.welcomeUser();
}

나는 주로 Master에서 선언 된 생성자 코드를 사용한다. 

클래스(Classes) - 생성자(Constructor) - Named Counstructor  Parameters

생성자에서도 순서를 기억 할 필요없이 파라미터를 이름으로 넣어줄 수 있다.

class Chif{
  String name; 
  int age; 

  Chif({required this.name,required this.age}); // 기본값을 주던지, required를 붙이던지

  void welcomeMaster(){
    print("Hello $name Welcome To Our Service" );
  }
}

void main(){
  var chif1 = Chif(name: "민프치프",age: 20);
}

클래스(Classes) - 생성자(Constructor) - Named Counstructor

기본적으로 호출되는 생성자가 있고, 조금은 다르게 동작하는 또 다른 생성자를 가지고 싶으면 어떻게 할까?

아래 코드를 보면 Player에서 생성자 기본 호출을 하는 부분이 있고, 다르게 동작하는 또 다른 생성자가 있다. 

이렇게도 생성자를 사용할 수 있다. 

class Player{
  String name,grade; 
  int age; 

  Player(this.name, this.age, this.grade); // 생성자 기본 호출


  // 다르게 동작하는 또 다른 생성자
  Player.createGoodPlayer({
    required String name,
    required int age,
  }) : this.name=name,
  this.age = age,
  this.grade="Good Player";

  void welcomePlayer(){
    print("Hello $name Welcome To Our Service" );
  }
}

void main(){
  var user1 = User("민프",10);
  user1.welcomeUser();
  var chif1 = Chif(name: "민프치프",age: 20);
  var goodPlayer = Player.createGoodPlayer(name:"민프", age: 10);
}

클래스(Classes) - Cascade Notation

클래스에 대한 객체의 값을 수정할 때 방법이 2가지가 있는데 아래 코드와 같다.

  var chif1 = Chif(name: "민프치프",age: 20);
  chif1.name="오프치프";
  chif1.age=20;
  
  // Cascade Notation
  var chif2 = Chif(name: "민프치프",age: 20) //Cascade Notation는 세미클론을 마지막에 붙임
  ..age=20
  ..name="민프";

chif1에서와 같이 변경할 수도 있고, 

chif2에서와 같이 Cascade Notation을 사용해서 변경할 수 있다.

 

클래스(Classes) - enum Class

Enum은 실제 개발에서도 많이 사용되는데 오타확률을 적게 해주고, 유지보수에도 도움을 준다.

https://ko.wikipedia.org/wiki/%EC%97%B4%EA%B1%B0%ED%98%95

 

열거형 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration), 이넘(enum), 팩터(factor ← R 프로그래밍 언어와 통계학의 범주형 변수에서 부르는 명칭)는 요소, 멤버라 불

ko.wikipedia.org

Enum은 단순히 열거형이라고 부른다. 

즉, 서로 연관된 상수들의 집합을 의미한다. 

 

Dart뿐만 아니라 다른 언어에서도 많이 사용 되는 enum class이다.

 

예들 들어서)
자동차 타입 : SUV, CUV, Sedan이 있다고 해보자

클래스를 만들거나 해당 타입을 사용할 때 마다 직접 타이핑을 해서 넣게 되면 아래와 같이 오타가 날 가능성이 생기게 되는데

Car(type:"SUV")
Car(type:"CIV")
Car(type:"Saden")

아래와 같이 enum을 지정하면 자동완성 기능도 제공되고 오타가 날 가능성이 현저하게 줄여질 수 있다. 

enum CarType{SUV, CUV, Sedan}

Car(type:CarType.SUV)
Car(type:CarType.CUV)
Car(type:CarType.Sedan)

 

클래스(Classes) - 추상 클래스(Abstract Classes)

추상화 클래스는 자바, 코틀린에서 사용해봤는데 

클래스와 추상 클래스의 가장 큰 차이점은

추상 클래스는 반드시 오버라이딩, 상속 해야만 사용할 수 있다는 점이다.

 

즉, 클래스는 중복이 되는 부분, 공통적인 부분을 만든 것이라고 생각하면

추상 클래스는 기존 클래스에서 뭔가 더 필요한 부분을 재정의하여 사용하는 클래스라고 생각하면 된다. 

 

아래 코드를 참고하자

abstract class Human{
  void walk();
}

class User extends Human{
  late String name; // 초기값을 설정해주던지 아니면 defalut값을 설정해놓던지
  late int age; // 초기값을 설정해주던지 아니면 defalut값을 설정해놓던지

  User(String name, int age){
    this.name = name;
    this.age = age;
  }

  void walk(){
    print("This is abstract class");
  }

  void welcomeUser(){
    print("Hello $name Welcome To Our Service" );
  }
}

 

클래스(Classes) - 상속 (Inheritance)

상속은 객체지향언어라면 자주 사용하게 되는데

부모클래스에서 자식 클래스의 변수 및 메서드를 사용할 수 있고, 재정의를 할 수 있도록 해주는 것을 의미한다. 

사용 방법은 자식 클래스에서 extends를 넣어서 사용해준다.

아래 코드를 참고하자

class Human {
  final String name;

  Human({required this.name});

  void sayHello(){
    print("hello $name");
  }
}

enum Team {blue,red}

class Player extends Human{
  final Team team;

  //Human이라는 부모 클래스를 상속 받았으니 super를 사용해서 Human안에 있는 파라미터도 넣어줘야해
  // super는 부모 클래스와 상호작용 할 수 있게 해준다.
  // 즉 super에 name을 전달해주면 이 클래스에 전달 된 name과 함께 호출하게 되는거다
  Player({required this.team, required String name}):super(name: name);

  // 부모에서 선언 된 sayHello 메서드를 override로 커스텀 할 수 있다. 
  @override 
  void sayHello(){
    super.sayHello(); // super를 통해서 부모에서 선언 된 부분들을 가지고 올 수 있다. 
    print("override SayHello ${team}");
  }

}

void main (){
  var player1 = Player(team: Team.red,name: "민프");
  player1.sayHello();
}

 

 

클래스(Classes) - 믹스인 (Mixin or Mix-in)

https://ko.wikipedia.org/wiki/%EB%AF%B9%EC%8A%A4%EC%9D%B8

 

믹스인 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. -->

ko.wikipedia.org

Mixin은 객체지향언어라면 사용하게 되는 건데

Mixin은 생성자가 없는 클래스를 의미한다.

그래서 보통 클래스에 프로퍼티들을 추가할 때 사용하게 된다.

 

사용 방법은 이번에는 extends를 사용하는게 아니라 with를 사용한다.

아래 코드를 참고하자

// Mixin 클래스 선언
mixin Strong {
  final double strenghtLevel = 1500.99;
}

// Mixin 클래스 선언
mixin QuickRunner {
  void runQuick(){
    print("rumn");
  }
}


class Player with Strong, QuickRunner{
  final Team team;
  Player({required this.team});
}

void main (){
  var player1 = Player(team: Team.red);
}

위 와 같이 Mixin클래스를 선언하면 사용하는 클래스에게 다중상속을 하기 쉬워진다.

지금은 Player클래스만 있지만 여러 클래스들이 있게 된다면 유지보수에 용이해질 것 이다.


참고링크

https://dart.dev/effective-dart/style

 

Effective Dart: Style

Formatting and naming rules for consistent, readable code.

dart.dev

https://dart-ko.dev/language/variables

 

변수

Dart의 변수에 대해 학습합니다.

dart-ko.dev

https://dart-tutorial.com/useful-information/final-vs-const-in-dart/

 

Final Vs Const

Learn Dart Programming

dart-tutorial.com

https://www.reddit.com/r/dartlang/comments/mo7239/compiletime_vs_runtime_constants/

 

From the dartlang community on Reddit

Explore this post and more from the dartlang community

www.reddit.com

https://ko.wikipedia.org/wiki/%EB%AF%B9%EC%8A%A4%EC%9D%B8

 

믹스인 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. -->

ko.wikipedia.org


 

Comments