Dart 시작하기

hodu·2024년 3월 10일
0
post-thumbnail

🎈 Dart 입문기

Flutter에 대한 관심이 오래전부터 있었지만, 정작 공부를 시작하지는 못했습니다.
그래서 이번을 계기로 조금이나마 알아가보고자 Dart에 관한 공부를 결심했습니다.

저는 새로운 기술을 배울 때 노마드코더의 강의를 선호합니다. 노마드코더의 Dart 시작하기 강의를 선택했습니다.

노마드코더의 강의는 항상 흥미롭고 재밌습니다.👍

이 글을 통해, 강의를 듣고 공부한 내용을 정리해보려고 합니다.


🚀 Dart 소개: Flutter의 핵심 언어

Dart는 Flutter 앱 개발의 핵심 언어로, 뛰어난 성능과 효율성을 자랑합니다. 이 언어는 개발의 유연성과 신속성을 위해 두 가지 컴파일러 방식을 제공합니다.

Dart의 컴파일러:

  • AOT (Ahead Of Time) 컴파일러: 이 컴파일러는 애플리케이션을 네이티브 코드로 변환하여 최적화된 바이너리를 생성합니다. 배포 시 활용되며, 모든 플랫폼에 맞추어 변환되기 때문에, 컴파일에 많은 시간이 걸립니다.
  • JIT (Just In Time) 컴파일러: 개발 과정에서 빠른 반복 작업과 테스트를 가능하게 합니다. Dart VM에서 실행되며, 코드의 변경 사항을 실시간으로 반영하고 다양한 디버깅 옵션을 제공합니다.

😀 Dart 특징

main 함수

모든 Dart 프로그램은 main 함수로 부터 시작합니다. 이 함수는 프로그램의 진입점으로서, Dart 컴파일러가 자동으로 찾아 실행합니다.

변수 선언

Dart에서 변수 선언은 var 키워드를 사용하여 이루어집니다. 타입 추론을 통해 Dart 컴파일러가 변수의 타입을 결정합니다.

명시적 타입 선언을 활용하면 코드의 가독성을 높일 수 있습니다. Dart 스타일 가이드에 따르면, 일반적으로 지역 변수에는 var를 사용하고, 클래스 변수나 프로퍼티 선언에는 명시적 타입을 사용하는 것이 좋습니다.



😎 Dart의 강력한 변수 관리

Dart에서 변수 관리는 코드의 가독성과 안정성을 결정짓는 핵심 요소입니다.

  • final 키워드: 변수에 final을 사용하면 해당 변수는 한 번만 값을 할당받을 수 있으며, 그 후에는 변경이 불가능합니다. 이는 변수의 불변성을 보장하며, 프로그램의 예측 가능성을 높여줍니다.

    void main() {
      final name = "pizza"; // 'name'은 한 번 할당 후 변경 불가
      final String username = "tom"; // 명시적 타입과 함께 사용
    }
  • late 키워드: 초기 데이터 없이 먼저 변수를 생성하고 추후에 데이터를 넣을 때 주로 사용한다. 이는 특히 비동기 작업 이후 값을 할당할 때 유용합니다.

    void main() {
      late final String name; // 초기화를 나중으로 미룸
    }
  • const 키워드: 컴파일 시점에 값이 결정되는 상수를 정의할 때 const를 사용합니다. const로 선언된 변수는 프로그램 실행 동안 절대 변경되지 않습니다.

    void main() {
      const name = "tom"; // 컴파일 시점 상수
    }
  • Null Safety: Dart에서 변수가 null 값을 가질 수 있는 경우, 해당 변수 타입 뒤에 ?를 붙여야 합니다. 이는 해당 변수가 null 값을 가질 수 있음을 명확히 하며, 개발자가 의도치 않은 null 참조 오류를 예방할 수 있게 합니다.

    void main() {
      String? name = "jisoung"; // 'name'은 null 값을 가질 수 있음
      name = null; // Null Safety로 인해 명시적으로 null 할당 가능
    }
  • 변수 참조: Dart에서는 $ 기호를 사용하여 문자열 내에서 변수를 직접 참조할 수 있습니다. 복잡한 표현식의 경우 ${} 구문을 사용하여 계산할 수 있습니다.

void main() {
  var name = "tom";
  var age = 10;
  var greeting = "hello $name, I'm ${age + 5}"; // 문자열 내 변수 사용
}

✨ 기본 데이터 타입 및 컬렉션 활용

  • 기본 데이터 타입: String, bool, int, double, num 등 Dart의 기본 타입들은 다양한 상황에서 데이터를 표현할 수 있게 해줍니다.

    void main() {
      String name = "tom";
      bool isPlay = true;
      int age = 10;
      double money = 52.55;
      num x = 12; // 정수와 실수 모두 가능
    }
  • 리스트와 컬렉션 연산: Dart의 리스트는 유연하게 데이터를 관리할 수 있는 구조를 제공합니다. collection ifcollection for는 조건에 따라 리스트를 동적으로 조작할 수 있는 기능입니다.

    void main() {
      var numbers = [1, 2, 3, 4, 5];
      var dynamicList = [
        'start',
        if (true) 'middle', // 조건부 포함
        for (var number in numbers) 'number $number', // 동적 요소 추가
        'end',
      ];
    }

😉 Dart에서 함수 사용하기

Dart는 객체 지향 언어로, 함수도 객체입니다. 함수는 변수에 할당되거나 다른 함수의 인자로 전달될 수 있습니다.

  • 함수 정의 및 사용
String sayHello(String name) => "Hello $name, nice to meet you."; // 단축 구문 사용
num plus(num a, num b) => a + b; // 계산 결과 반환

void main() {
  print(sayHello("sugar")); // 함수 호출
}
  • Named Parameters

Named Parameters는 함수 호출 시 인자의 의미를 명확히 하여 가독성을 높입니다.
dart는 null safety로 인해 null을 막는 두가지 방법으로 사용할 수 있다.

  1. default value 사용하여 해결할 수 있습니다.
String sayHello({String name = 'anon',
int age = 99,
String country = 'wakanda',
}) {
return "Hello $name, you are $age, and you come from $country;
}

void main() {
print(sayHello()); // 아무것도 전달하지 않아도 default value가 이미 있으므로 null safety에 걸릴 일이 없음
}
  1. required 사용하여 필수 매개변수를 지정할 수 있습니다.
String sayHello({required String name, required int age, required String country}) {
  return "$name / $age / $country"; 
}

void main() {
  print(sayHello(name: "sugar", age: 10, country: "Korea")); 
}

Dart의 클래스

1. 클래스와 프로퍼티 선언

Dart에서 클래스를 정의할 때 프로퍼티는 타입과 함께 선언합니다. new 키워드는 선택적입니다.

class Player {
  final String name = 'jisoung';
  final int age = 17;

  void sayName() {
    print("Hi, my name is $name"); 
  }
}

void main() {
  var player = Player(); // 'new' 키워드 생략 가능
}

2. 생성자(Constructor) 정의와 사용

기본 생성자
Dart에서 클래스의 생성자는 클래스 이름과 동일해야 합니다. 생성자 내에서 this 키워드를 사용하여 인스턴스 변수를 초기화할 수 있습니다.

class Player {
  late final String name;
  late final int age;

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

생성자 간소화

위 문법에서 Dart는 생성자 인자를 직접 인스턴스 변수에 할당하는 간소화한 문법을 제공합니다.

class Player {
  final String name;
  final int age;

  Player(this.name, this.age);
}

Named Parameters를 이용한 생성자

클래스의 생성자에서 Named Parameters를 사용하면, 인자의 순서에 구애받지 않고, 각 인자의 역할이 명확해져 코드의 가독성이 향상됩니다.

class Team {
  final String name;
  final int age;
  final String description;

  Team({required this.name, required this.age, required this.description});
}

Named Constructor

Dart는 동일한 클래스 내에 여러 개의 Named Constructor를 정의할 수 있습니다.

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

  Player.createBlue({required this.name, required this.xp, this.team = 'blue'});
  Player.createRed({required this.name, required this.xp, this.team = 'red'});
}

Cascade Notation

Dart에서는 'cascade notation'(..)을 사용하여 동일한 객체의 프로퍼티를 연속적으로 수정할 수 있습니다. 이는 코드를 간결하게 만들어 줍니다.

void main() {
  var jisoung = Player(name: "jisoung", age: 17, description: "Happy coding is end coding")
    ..name = "nico"
    ..age = 20
    ..description = "Best project is end project";
}

Dart의 enum 활용

enum은 한정된 값의 집합을 정의할 때 사용되며, 코드의 가독성과 안전성을 향상시킵니다.

enum Team {
  red,
  blue,
}

class Player {
  String name;
  int age;
  Team team;

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

void main() {
  var jisoung = Player(name: "jisoung", age: 17, team: Team.red);
  var sushi = jisoung
    ..name = "sushi"
    ..age = 12
    ..team = Team.blue;
}

abstract

추상 클래스는 특정 메서드를 하위 클래스에서 구현하도록 요구하는 템플릿입니다.

abstract class Human {
  void walk(); // 구현은 제공하지 않고 선언만 함
}

class Player extends Human {
  
  void walk() {
    print("Walking!"); // 'Human'을 상속받은 'Player'에서 'walk' 구현
  }
}

여기서 Humanwalk 메서드의 구현을 강제함으로써, 모든 하위 클래스가 일관된 인터페이스를 갖도록 합니다.

extends

상속을 통해 클래스는 부모 클래스의 속성과 메서드를 재사용할 수 있으며, super를 사용하여 부모 클래스의 생성자를 호출할 수 있습니다.

class Human {
  final String name;
  
  Human(this.name);
  
  void sayHello() {
    print("Hello! $name");
  }
}

class Player extends Human {
  String team;

  Player({required this.team, required String name}) : super(name);
  
  
  void sayHello() {
    super.sayHello();
  }
}

Player 클래스는 Human 클래스로부터 name 속성과 sayHello 메서드를 상속받습니다. @override를 사용하여 sayHello 메서드를 재정의할 수 있습니다.

Mixin

Mixin은 생성자를 갖지 않는 클래스로, 여러 클래스에 코드를 재사용하기 위해 사용됩니다.

mixin Tall {
  final double height = 190.00;
}

class Human with Tall {
  // 클래스 구현
}

Human 클래스는 Tall Mixin을 with 키워드를 사용하여 포함시켜 height 속성을 재사용합니다. Mixin은 다중 상속의 혜택을 제공하며, 코드의 유연성과 재사용성을 높입니다.


abstract/extends/Mixin 정리

Dart에서 abstract, extends, 및 Mixin은 클래스 설계와 코드 구성에 핵심적인 역할을 합니다. 이들은 각각의 상황에 따라 코드의 재사용성, 확장성, 그리고 유연성을 개선하기 위해 사용됩니다.

키워드적용 시기사용 예시
abstract하위 클래스에서 반드시 구현해야 하는 메서드를 정의할 때 사용합니다.게임 캐릭터의 공통 walk() 메서드를 Human 추상 클래스에 정의합니다.
extends클래스가 다른 클래스의 속성 및 메서드를 상속 받아 재사용하려 할 때 사용합니다.Player 클래스가 Human 클래스의 sayHello() 메서드와 name 속성을 상속 받습니다.
Mixin재사용 가능한 기능을 여러 클래스에 추가하고 싶을 때 사용합니다.Tall Mixin의 height 속성을 다양한 클래스에 적용합니다.

이 기능들을 이해하고 활용함으로써 체계적이고 효율적인 프로그램을 개발할 수 있습니다.


🎇 끝으로

Dart를 공부하며 새로운 시각으로 볼 수 있어서 흥미로웠습니다.

Dart에 대한 간략한 정리를 통해 기초를 다졌고, Flutter를 사용하면서 Dart의 심화 학습을 계속해 나갈 계획입니다.

감사합니다 😀

출처 :
https://nomadcoders.co/dart-for-beginners
https://dart.dev/guides

profile
잘부탁드립니다.

0개의 댓글