Languages/Dart

[글또] JavaScript 개발자와 Dart 찍먹하기

성중 2023. 2. 11. 13:48

여러분, Flutter 프레임워크에서 사용되는 언어 Dart에 대해서 들어보셨나요?

 

개발자라면 JavaScript, Python, Java 등 다른 언어를 이미 접해 보셨을 거라고 생각합니다

 

저는 그 중에 JavaScript를 주로 사용해 왔고 이번에 Dart를 처음 접하게 되었는데요, JavaScript를 이미 알고 있는 개발자의 시선에서 본 Dart를 정리하면서 학습 콘텐츠를 만들면 재미있겠다는 생각이 들어 이 글을 작성해 보았습니다

 

특히 Flutter에 흥미가 있는 프론트엔드 개발자라면 재미있게 읽으실 것 같아요 😉

 

이 글은 JavaScript를 이미 알고 Dart는 처음 접하는 개발자들을 대상으로 작성하였으며, 말 그대로 찍먹이기 때문에 깊이 있는 내용이 아닐 수 있습니다. 물론 JavaScript에 대한 지식이 없다면 이해 불가능한 수준도 아닙니다.

 


Dart

Dart모든 플랫폼에서 빠른 앱을 위한 클라이언트 최적화 객체 지향 언어입니다

 

공식 문서에서는 다음 3가지 특징을 소개하고 있습니다

  • UI 최적화: 사용자 인터페이스 생성 요구에 특화된 구조로 개발
  • 생산적인 개발: 핫 리로드로 실행 중인 앱에서 즉시 결과 확인 가능
  • 모든 플랫폼: 모바일, 데스크톱 및 백엔드용 ARM 및 x64 머신 코드, 웹용 JavaScript로 컴파일

 

모든 플랫폼 개발? 웹용 JavaScript로 컴파일? 🙄

 

이 글은 Dart를 중점적으로 다루지만 Flutter에 대한 설명을 빼놓을 수는 없는데요, Dart로 구동되는 Flutter인기 있는 다중 플랫폼 UI 툴킷으로, iOS, Android, macOS, Windows, Linux 및 웹에서 실행되는 UI를 빌드하기 위한 도구 및 라이브러리를 제공하는 프레임워크입니다

 

Dart(+ Flutter)로 거의 모든 플랫폼 개발 가능

즉, Flutter로 웹을 대상으로 하는 앱을 개발한다면 웹 컴파일러는 Dart를 JavaScript로 변환합니다!

 

벌써 Dart와 JavaScript가 많은 특징을 공유할 것이라는 예감이 들지 않나요? 🤓

 

Variable

DartPad에서 다음 내용을 실습해 볼 수 있습니다.

 

가장 먼저 Dart의 변수에 대해서 알아보겠습니다!

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

JavaScript와 달리 모든 Dart 프로그램은 main 함수라는 Entry point에서 출발해야 합니다

 

void main() {
  var food1 = "pizza"; // 방법1
  String food2 = "chicken"; // 방법2
  food1 = "hamburger";
}

기본적으로 var 키워드를 사용할 수 있고, 타입을 명시해서 선언할 수도 있습니다

 

JavaScript의 var 키워드는 블록 스코프와 TDZ가 적용되지 않아 사용이 지양되는데요, Dart에서는 그럴 걱정 없이 var 남발이 가능합니다! 재할당도 가능하기 때문에 Dart의 var가 JavaScript의 let과 비슷하다고 볼 수 있겠네요

 

그렇다면 JavaScript의 const 변수는 Dart에서 어떻게 선언할까요?

void main() {
  final name = "pizza";
  name = "ham"; // 수정 불가
}

var 대신 final 키워드를 사용해주면 됩니다!

 

void main() {
  const name = "tom"; // 컴파일 시점에 바뀌지 않는 값
  final username = fetchAPI(); // 컴파일 시점에 바뀌는 값
}

주의할 점은, Dart에서도 const 키워드가 지원되는데 이는 컴파일 이전에 이미 확정된 상수 값만 취급하기 때문에 JavaScript의 const 키워드와는 다릅니다. API로부터 받아오거나 사용자 입력 값은 final로 선언해줍니다

 

void main() {
  String? name = "hello";
  name = null;
  
  if (name != null) name.isNotEmpty; // 방법1
  name?.isNotEmpty; // 방법2
}

추가적으로 Dart는 개발자의 null 값 참조를 방지하는 null safety가 기본적으로 지원됩니다

 

모든 변수는 non-nullable하며 별도의 조건(null 값이 될 수도 있음)을 확인해 주어야 사용이 가능합니다

TypeScript의 Non-null assertion operator와 비슷하네요 🤔

 

void main() {
  late final String name;

  print(name); // name 변수에 접근 불가
}

이 외에도 선언한 변수에 값을 나중에 할당하겠다는 의미인 late 키워드도 있습니다

 

비교적 최근에 생긴 언어인 만큼 세련된 기능을 많이 가지고 있다는 생각이 드네요 😎

 

Data Type

데이터 타입들을 조금 더 자세히 알아볼까요?

Dart 역시 JavaScript와 비슷한 객체 기반의 언어로, 다양한 메소드가 지원됩니다

 

void main() {
  String name = "tom";
  
  bool alive = true;
  
  int age = 10; // 정수
  double money = 52.55; // 실수
  
  num x = 12;
  num y = 1.2;
}

기본 타입은 JavaScript와 거의 비슷하지만 숫자 타입의 정수/실수 구분이 가능합니다

 

void main() {
  var giveMeFive = true;

  var item = [
    1,
    2,
    3,
    4,
    if (giveMeFive) 5,
  ];

  List<int> numbers = [
    1,
    2,
    3,
    4,
  ];
}

JavaScript의 배열과 비슷한 List 타입입니다. 내부에 if/for 문 사용이 가능하며, 타입 명시가 가능한 차이점이 있습니다

 

void main() {
  var player = {
    "name": "yee",
    "age": 23,
  };

  Map<String, String> player2 = {
    "name": "yee",
    "age": "23",
  };
}

Dart는 Map 타입이 JavaScript의 객체와 비슷하며, 마찬가지로 타입 명시가 가능합니다

Dart에서 Object 타입은 TypeScript의 any와 비슷한 최상위 타입 개념이라고 하네요 👀

 

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

단순히 중괄호로 감싸면 모든 값이 유일한 Set 타입을 선언할 수 있습니다

 

Function

다음으로 Dart의 함수를 간단하게 알아보겠습니다

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

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

파라미터/반환 값의 타입을 명시에 함수를 선언할 수 있으며, arrow function이 지원됩니다!

 

더 나아가 Dart의 함수 파라미터는 두 가지 옵션이 있습니다 🙃

String sayHello(String name, int age, [String? country = "korea"]) =>
    "$name / $age / $country";

void main() {
  print(sayHello("yee", 23));
}

첫 번째로 순서대로 인수를 입력하는 방식인 positional parameter입니다. null safety를 위해 기본적으로 required 상태이며, 초기 값을 지정해준다면 optional positional parameter 선언도 가능합니다! 일반적이지만, Dart에서 주로 사용되는 방식은 아니라고 하네요

 

String sayHello(
        {required String name, int age = 23, required String country}) =>
    "$name / $age / $country";

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

다음으로 함수 선언부에서 매개변수를 중괄호로 묶어 인수에도 이름을 붙일 수 있는 named parameter입니다. null safety를 위해 초기값을 지정하거나 인수를  넣도록 required를 붙여줘야 하며, Dart에서 주로 사용되는 방식입니다!

 

Class

JavaScript 역시 클래스가 있지만, Dart의 클래스는 거의 필수적으로 사용되기 때문에 중요도가 더 높은 편입니다

Flutter 개발에 위젯과 API 모델을 클래스로 분리하는 패턴이 권장되기 때문이기도 합니다 🥸

 

class Player {
  final String name = "yee";
  int age = 23;
  
  void sayHi() {
    print("Hi my name is $name");
  }
}

void main() {
  var player = Player();
  
  player.sayHi();
}

new 키워드를 생략하는 등 약간의 차이가 있지만, JavaScript의 클래스를 알고 있다면 이해가 어렵지 않습니다

 

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

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

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

void main() {
  var player1 = Player(
    name: "yee",
    age: 23,
    team: "blue",
    xp: 1200,
  );
  var player2 = Player(
    name: "봙봙",
    age: 3,
    team: "red",
    xp: 150,
  );

  player1.sayHi();
  player2.sayHi();
}

constructor method(생성자 함수)의 이름은 클래스 이름과 같아야 하며, constructor 역시 함수이기 때문에 named parameter 활용이 가능합니다!

 

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

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

  Player.createBluePlayer({required String name, required int age})
      : this.name = name,
        this.age = age,
        this.team = "blue",
        this.xp = 0;

  Player.createRedPlayer(String name, int age)
      : this.name = name,
        this.age = age,
        this.team = "red",
        this.xp = 0;

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

void main() {
  var player1 = Player.createBluePlayer(
    name: "yee",
    age: 23,
  );
  var player2 = Player.createRedPlayer("봙봙", 3);

  player1.sayHi();
  player2.sayHi();
}

Dart에서 constructor를 여러 개 만들고 싶다면 위와 같이 named constructor를 선언해줄 수 있습니다. 콜론(:) 이후 원하는 매개변수만 할당 및 기본 값을 초기화 해줍니다

 

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 my name is $name from $team team");
  }
}

void main() {
  var apiData = [
    {
      "name": "A",
      "team": "blue",
      "xp": 0,
    },
    {
      "name": "B",
      "team": "red",
      "xp": 0,
    },
    {
      "name": "C",
      "team": "blue",
      "xp": 0,
    },
  ];

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

    player.sayHi();
  });
}

named constructor는 API fetching 및 데이터 모델링에서 이렇게 fromJson 패턴으로 활용됩니다

 

Dart에도 추상 클래스나 상속의 개념이 존재하지만 Flutter에서는 잘 활용되지 않는다고 하네요 🫥

class Strong {
  final double strengthLevel = 1500.99;
}

class QuickRunner {
  void runQuick() {
    print("ruuuuun!!");
  }
}

enum Team { blue, red }


class Player with Strong, QuickRunner {
  Team team;

  Player({
    required this.team,
  });

  void sayHi() {
    String team = this.team.name;

    print("I'm from $team team (strength: $strengthLevel)");
  }
}

class Horse with QuickRunner {}

void main() {
  var yee = Player(
    team: Team.blue,
  );  
  var horse = Horse();

  yee.sayHi();
  yee.runQuick();
  horse.runQuick();
}

마지막으로 소개 드리고 싶은 개념은 mixin입니다. 생성자가 없는 클래스를 의미하며, Flutter에서 프로퍼티나 메소드 모음을 여러 클래스에 전달해 재사용하려는 목적으로 활용됩니다!

 

이 외에도 QQ OperatorCascade Notation, Typedef, Enum 등 편리한 기능들이 많지만..

이 이상은 찍먹이 아닌 것 같아 이만 줄이도록 하겠습니다 🫠

 


긴 글 읽어 주셔서 감사합니다!! 🙇

궁금하거나 더 자세한 내용은 Dart 공식문서를 참고해주세요! 은근슬쩍 영업

 

재미있게 보셨다면 블로그나 글또 Slack에 감상을 남겨주세요!

새로운 의견이나 오류 정정, 피드백은 언제나 환영입니다 😽

 

가능하다면 다음은 React 개발자와 Flutter 찍먹하기로 돌아오겠습니다~!

'Languages > Dart' 카테고리의 다른 글

[노마드코더] #5 Classes  (0) 2023.01.12
[노마드코더] #4 Functions  (0) 2023.01.09
[노마드코더] #3 Data Types  (0) 2023.01.09
[노마드코더] #2 Variables  (0) 2023.01.08
[노마드코더] #1 Introduction  (0) 2023.01.04