Mobile App/Flutter | Dart

다트[Dart] | Flutter가 Dart를 사용하는 이유 + Dart 기본(1)

jaeyeong 2023. 4. 25. 17:40

Dart 공식문서와 노마드코더 Dart 시작하기 강의를 보고 내용을 정리하였습니다.

 

Dart 개요

이미지 출처: Dart Overview

 

Dart로 개발을 하면 다양한 방법으로 코드를 실행할 수 있다.

  1. 웹 플랫폼 개발
    웹을 대상으로 하는 앱의 경우 웹 컴파일러는 Dart를 JavaScript로 변환한다.
  2. 기본 플랫폼 개발
    모바일 및 데스크탑을 대상으로 하는 앱의 경우 JIT(Just In Time) 기능이 있는 Dart VM,
    기계어로 변환하기 위한 AOT(Ahead of Time) 컴파일러를 제공한다.
    JIT, AOT는 프로그래밍 언어의 소스 코드를 컴파일하여 기계어 코드로 변환하는 과정 중 하나이다.

    - JIT: 개발할 때 사용된다. 코드의 결과를 바로 반영하여 보여준다.
    (Next.js에서 파일 수정 후 저장하면 바로 반영되는 것과 같은 기능으로 이해했음)
    - AOT: 최종 배포할 때 사용된다. (기본 ARM 또는 x64 머신 코드로 컴파일됨)

Dart는 null safety 언어이다.

null safety란 null을 참조하면 오류가 발생하는 현상을 방지해 주는 것이다.

 

왜 Flutter는 Dart를 사용할까?

[관련 내용 공식문서 링크]

 

Dart를 사용하기로 했을 때 언어에는 Native 바이너리를 지원하는 사전 도구 체인(AOT)이 없었지만,

Dart 팀이 Flutter용으로 만들어줬기 때문에 지금은 AOT를 가지고 있다.

덕분에 최적화되어 고성능 실행이 가능해졌다.

 

(Native 바이너리란? 컴퓨터의 프로세서가 직접 실행할 수 있는 기계어로 변환된 실행 파일

컴퓨터 시스템의 아키텍처와 운영체제에 따라 다르게 생성된다.)


이처럼 Flutter가 발전하기 위해 Dart 언어를 수정할 수 있다.
React의 경우 페이스북에서 만들었는데, React를 개선시키기 위해 자바스크립트를 수정할 수 없다.
하지만 플러터는 Dart팀에 수정을 요청했고 반영되었다.

 

공식 문서에서는 이러한 부분을 이유로 설명하고 있다.

 

main 함수

void main() {
  for (int i = 0; i < 3; i++) {
    print('hello ${i + 1}'); // hello 1, hello 2, hello 3
  }
}

 

dart는 main method가 있어야 코드를 실행할 수 있다.

(main은 모든 Dart 프로그램의 Entry point이다.)

따라서 기능이 포함된 코드는 반드시 main 내부에 넣어줘야 한다.

 

만약 main 이름을 바꾼 뒤 실행하면 오류가 발생한다.

void hello() {
  for (int i = 0; i < 5; i++) {
    print('hello ${i + 1}');
  }
}

main 함수의 이름을 변경했을 때

 

세미콜론의 중요성

코드의 끝에 세미콜론을 꼭 붙여야 한다.
자바스크립트, 타입스크립트의 경우 auto format 기능으로 세미콜론을 붙이지 않아도 자동으로 붙여준다.


하지만 dart에서는 포맷팅을 해도 세미콜론을 자동으로 붙여주지 않는다.
(일부러 세미콜론을 안 쓸 때가 있기 때문에)
그렇기 때문에 세미콜론을 빠뜨리는 것을 주의해야 한다.

 

세미콜론 없이 코드를 실행했을 때 오류 발생

 

변수 만드는 방법

1. var 키워드 사용하기

void main() {
  var name = '재영';
  print(name); // 재영
}


따로 타입을 지정해 줄 필요가 없다. dart가 알아서 namestring이라는 것을 알고 있다.
이후 name에 다른 타입으로 수정한다면 오류가 발생한다.

변수를 수정할 땐 같은 타입으로 해줘야 한다.

void main() {
  var name = '재영';
  name = 100;
}

string 타입의 name에 number&nbsp; 타입의 값으로 수정했을 경우 발생하는 오류

 

2. 명시적으로 변수의 타입을 작성하기

void main() {
  String name = '재영';
  print(name); // 재영
}


함수나 메서드 내부에 지역 변수를 선언할 땐 var 키워드를 사용한다.
class에서 변수나 프로퍼티를 선언할 땐 타입을 지정한다.

 

dynamic 타입

여러 타입을 가질 수 있는 타입이다.
변수가 어떤 타입인지 알기 어렵거나 dynamic 타입으로 두었을 때 유용할 경우 사용한다.
사용할 땐 타입을 확인하고 변수를 사용하면 된다.

(타입스크립트의 타입가드와 같이 '00타입이라면 00을 해줘라'라는 것을 알려주기)

void main() {
  var name; // dynamic type
  if (name is String) {
    // name이 문자열일 때 실행 할 코드
  }
}
void main() {
  dynamic age = 10; // dynamic type
}


변수를 생성할 때 아무 값도 지정하지 않거나 dynamic 타입을 명시한다면 dynamic 타입을 가진다.
dynamic 타입인 변수를 활용할 때 타입이 정해진다면 (nameString일 때 조건문 내부 같은 경우)

사용할 수 있는 메서드를 추천해 준다.
(타입 가드를 하지 않았을 땐 사용할 수 있는 게 거의 없음)
하지만 타입스크립트에서 any 남발을 지양하듯이, dart에서도 dynamic 타입의 사용은 되도록 지양한다.

 

null safety

개발자가 null 값을 참조할 수 없도록 하는 것
참조하게 될 경우 런타임 에러를 발생시킨다.


어떤 변수 및 데이터가 null이 될 수 있음을 명시함으로써 오류가 발생하는 상황을 방지해 주는 것이다.

void main() {
  String name = '재영';
  name = null;
}

 

하지만 null이 필요한 경우도 있다.
이럴 땐 ?를 넣어주면 해결된다.

void main() {
  String? name = '재영';
  name = null;
  // ?를 붙임으로써 null이 올 수 있음을 명시했기 때문에,
  // 변수를 사용하기 전에 타입 확인을 해야한다.
  if (name != null) {
    name.isNotEmpty;
  }
  // 위 타입 확인을 더 간단히 하면 다음과 같이 수정할 수 있다.
  name?.isNotEmpty;
}

void main() {
  String? name = '재영';
  name = null;
}

 

final 변수

변경할 수 없는 데이터를 만들 때 사용
const 역할을 한다.

void main() {
  final name = '재영';
}

 

late 변수

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

void main() {
  late final name;
  print(name); // Error. late변수이기 때문에 값을 넣기 전 접근하면 안된다고 알려줌
  name = '재영';
}


final은 상수를 만드는 건데, late 변수와 함께 사용하면 나중에 값을 할당할 수 있다.
함수 내에서는 final이나 late final이나 똑같은 기능을 해서 차이가 별로 없지만,

클래스에서는 유용하게 사용된다.
클래스 내의 인스턴스 변수가 final이면 만들면서 바로 할당해야 하기 때문에 오류가 발생하는데,
late final이면 만들고 난 후에 할당해도 된다.

 

const

JS, TS의 const와는 다르다. 오히려 JS, TS의 const는 final과 비슷하다.
dart에서 const는 compile-time constant를 만들어준다.


만약 어떤 값인지 모르고, 그 값이 API로부터 오거나 사용자가 화면에서 입력해야 하는 값이라면

그건 const가 아닌 final이나 var가 되어야 한다.
const는 컴파일할 때 알고 있는 값이어야 한다.

void main() {  
    const name = "tom"; // 컴파일 시점에 바뀌지 않는 값  
    final username=fetchAPI(); // 컴파일 시점에 바뀌는 값  
}
  • const: 컴파일 시점에 바뀌지 않는 값 (상수)
    (환경변수(env) 같은 생각이 들었음)
  • final: 컴파일 시점에 바뀌는 값 (API에서 받아온 값, 사용자 입력값)

 

Data Types

void main() {
  String name = '재영';
  bool alive = true;
  int age = 100;
  double money = 70.99;
  num x = 1;
  x = 20.12;
  
  print(name, alive, age, money, x); // 이러면 오류가 남
  print(name); // 하나씩 해줘야 오류가 안남
  print(alive);
  print(age);
  print(money);
  print(x);
}


위에 작성되어 있는 데이터 타입들은 object로 이루어져 있다. functionobject이다.
Dart가 객체 지향 언어라고 불리는 이유? 모두 클래스로 구현되어 있기 때문이다.


numintdouble을 모두 수용하는 자료형이다.
int일 수도 있고 double일 수도 있는 수는 num으로 정의하자.