Foggy day

[Flutter] null safety에 대하여 본문

Flutter/Flutter document

[Flutter] null safety에 대하여

jinhan38 2023. 3. 24. 22:37

 

 

 

이번 포스팅에서는 Dart null-safety와 null 사용법에 대해 알아보겠습니다.

 

일반적으로 변수는 특정한 값을 가지고 있습니다.

String name = "홍길동";

 

하지만 프로그래밍 언어에는 null이라는 개념이 있습니다. 모든 개발자들을 무섭게 하는 개념입니다. 

그럼 null이 무엇이냐? null은 값이 존재하지 않거나 아예 할당되지 않았다는 의미입니다. 변수가 null인지 모르고 사용하다보면 Null Point Exception라는 심각한 오류가 발생합니다.

 

그렇다고 null 자체가 문제인 것은 아닙니다. 특정 변수에 null이라는 값을 넣어서 아직 할당하지 않거나 비어있는 값이라는 것을 나타낼 수도 있습니다. 하지만 null로 인해 발생되는 오류는 매우 치명적입니다. 때문에 대부분의 개발자와 프로그래밍 언어에서는 null 오류를 줄이기 위해 노력해왔습니다.  

 

 

Dart에서는?,  Null-safety

Dart는 Null-safety를 통해 null의 오류의 위험을 줄이고 있습니다. 다시 말해 앱을 컴파일 하기 전에 null 오류를 방지할 수 있다는 의미입니다. 실제로 개발자가 코드를 작성하면서(edit-time) null 관련 오류를 찾을 수 있습니다. 

 

일단 Dart에서 사용하는 변수는 기본적으로 null이 올 수 없습니다. null이 아니라고 가정을 하고 있기 때문에 변수에 null을 할당할 수 조차 없습니다.

int age = 30;   // 정상
String name = "홍길동";   // 정상
int age2 = null;     // 입력 불가
String name2 = null;   // 입력 불가

사진처럼 IDE에서 null을 사용할 수 없게 합니다.

 

 

 

 

변수에 null을 할당하기 위해서는 [?] 기호를 사용해야 합니다. 변수 타입의 끝에 [?] 기호를 넣으면 변수에 null을 할당할 수 있습니다. 물론 변수 타입에 해당하는 값도 넣을 수 있습니다. 또한 [?] 기호를 사용하면 변수를 초기화하지 않아도 됩니다. 이럴 경우에 해당 변수는 null입니다. 

int? age1;
int? age2 = null;  // 정상
int? age3 = 30;   // 정상
String? name1;
String? name2 = "홍길동";  // 정상
String? name3 = null;   // 정상

위의 예제에서 [int? age1;]은 age1에는 int나 null이 올 수 있다는 의미입니다. 변수를 선언한 후에 다른 값을 입력하지 않았다면 age1은 null입니다. [int? age3 = 30;]은 age3에 int나 null이 올 수 있다는 의미고, 변수를 선언할 때 30이라는 초기 값을 입력했기 때문에 age3은 30입니다. 

 

위의 예제처럼 [?]기호가 있어서 null을 할당할 수 있는 변수는 nullable type, [?] 기호가 없어서 null을 할당할 수 없는 변수는 non-nullable type이라고 합니다. 

 

 

 

null 사용법

null이 위험한 오류를 발생 시킨다고 하지만 그럼에도 nullable 변수를 많이 사용합니다. 또한 변수 뿐만 아니라 함수와 클래스에도 많이 사용합니다. 중요한 것은 null인지 아닌지를 잘 체크해서 오류를 최소화하는 것입니다. 그래서 변수, 함수, 클래스에서 nullable type 변수를 어떻게 사용하면 될지 알아보겠습니다. 

 

먼저 Address 클래스를 만들고, 프로퍼티 중에 price만 nullable 타입으로 선언했습니다. 

class Address {
  String location = "";
  int phone = 0;
  double? price;
}

 

그리고 nullable 변수 2개와, Address 클래스를 nullable로 선언했습니다. 

  int? age;
  String? name;
  Address? address;

 

 

위와 같은 조건에서 ? 기호를 사용하지 않은 non-nullable타입처럼 변수를 사용해보겠습니다.

그러면 아래와 같이 컴파일을 하기도 전에 오류가 발생하고 있습니다. 

 

코멘트 창을 보니 int? 타입에는 int 값이 올 수 없다고 합니다. 그러면 int를 int?로 변경해주던가 반대로 int?를  int로 변경해서 두 변수의 타입을 맞춰줘야 합니다. 

 

int를 int?로 변경하는 것은 쉽습니다. 똑같이 ?기호를 추가하면 됩니다. 

 

하지만 address는 여전히 오류가 발생합니다. phone 프로퍼티가 null이 아니라 address 클래스 null 타입이기 때문입니다. 이럴 때는 두 가지 방법이 있습니다. 

 

1. address?.phone : phone은 null이 아니지만 address가 null인 경우 phoneValue에는 null 값이 할당됩니다.

2. address!.phone : [!]기호는 address가 nullable 타입이지만 null이 아니라는 의미입니다. address 변수를 다른데서 초기화를 해서 null이 아니라는 것이 확실할 때 사용합니다. 그런데 만약에 address가 null인데 [!] 기호를 사용했다면 오류가 발생합니다. 

 

 

생성할 변수의 타입을 non-nullable로 하고 싶을 경우 아래와 같이 사용할 수 있습니다.

[??] 기호는 age 값이 null이라면 30을 입력하라는 의미입니다.

두번째는 name이 null이 아니라는 것을 체크한 후 name에 [!] 기호를 붙여줬습니다. 

int ageValue = age ?? 30;

if(name != null) {
  String nameValue = name!;
}

double priceValue = address?.price ?? 500.5;

 

 

함수의 사용법은 약간 다릅니다. 

먼저 typedef키워드를 이용해 NullFunction이라는 타입의 함수를 만들었습니다. 

typedef NullFunction = void Function();

그리고 NullFuction타입에 [?]기호를 이용해 nullable 타입으로 선언했습니다. 

NullFunction? aaa;

선언한 함수를 호출했습니다. 

aaa();

그랬더니 아래와 같이 오류가 발생했습니다.

그 이유는 aaa 함수가 null일 수도 있기 때문입니다. 이런 경우에도 null 체크를 해줘야 합니다. 

 

먼저 if문으로 null 체크를 해준 후 [!] 기호를 사용하는 방법이 있습니다.

다른 방법으로는 ?키워드와 call()을 사용하는 것입니다. 이 경우에는 aaa가 null이 아닐 경우에만 aaa 함수를 실행시킵니다.

if(aaa != null) {
  aaa!();
}

aaa?.call();

 

 

함수의 인자로 null을 넣을 수도 있습니다. 

void nullFunction(String? value) {}

...

nullFunction(null);

 

 

 

'Flutter > Flutter document' 카테고리의 다른 글

[Flutter] Constraints 이해하기  (0) 2023.03.29