[flutter] Null safety

giyeon·2021년 5월 26일
2

flutter-null safety

목록 보기
1/1
post-thumbnail

이 프로젝트는 Youtube '더 코딩파파'의 'Null Safety 깔끔하게 이해하고 가세요!! flutter2.0 #더코딩파파 ' 강의와 '깡샘의 토마토' - 'Flutter 2.0 -Null Safety'를 참고했습니다.

Null safety?

변수 내부에 아무 값이 들어있지 않은 상태를 null상태라고 표현합니다.

많은 프로그램에서 런타임 에러 중 가장 큰 비중을 차지하는 에러가 null 입니다.
null safety가 적용된 코드는 null에 의한 에러를 runtime이 아닌 edit-time에 체크해서 사용자에게 알려주겠다는 의미입니다.

그래서 코드작성 시 null 에러가 발생할 수 없는 코드를 작성하게끔 합니다.
이렇게 되면 runtime시 null에 관한 에러는 발생하지 않겠죠 ?

그런 이유로 다양한 언어들이 null safety를 적용하는 추세입니다.

Null safet가 적용된 코드는 규칙이 있어요.

  1. 모든 변수는 null이 될 수 없고, null이 들어가지 못하는 변수에는 null 값을 할당할 수 없습니다.
  2. null값이 들어갈 수 없는 변수에 따로 null check는 필요없습니다.
  3. "Class 내의 변수" 반드시 선언과 동시에 초기화를 시켜야 합니다.

핵심만 말하자면...

null 값이 들어갈 수 있는 변수, (선언시 '?' 필요)
null 값이 들어가면 안되는 변수를 구분하는 거죠. (선언시 초기화 필요)

var i = 42; 

String name = getFileName() ; 

위 코드를 보면
i 변수는 42로 초기화 되었기 때문에 정확히 null이 아님을 알 수 있어요.

하지만 name 변수에는 method가 String 값을 받아와서 넣어주는데,
getFileName method가 null 값을 받아올지, null 이 아닌 값을 받아올지는 아무도 모릅니다.

하지만 null safety가 적용된 코드 안에서는 method가 null 값을 줘서는 절대 안됩니다.

다시말해서 String type인 Name 변수 내부에는 null 값이 들어갈 수 없어요.

하지만 null이 들어갈 수 있게 지정할 수 있어요.
type 옆에 '?'를 추가로 입력해주는 거예요.

dart pad에 String type인 name 변수를 선언해주고,
null을 넣어봤더니 에러가 떠요.

String type인 name변수에는 null을 넣어줄 수 없대요.
dart pad는 null safety가 적용된 상태이기 때문에 기본적으로 변수에 null이 들어갈 수 없어요.

하지만 '?' keyword를 통해 null이 들어갈 수 있게 지정해줄 수가 있어요.

위 코드처럼 String 옆에 '?' keyword를 붙여주니 name 변수에 null값을 넣어줄 수 있게 됐어요.

null값을 가지고 있었던 name 변수에 String값을 다시 넣어주는 것도 당연히 가능해요.
print 해보면 'YEAH!' 가 나오게 됩니다. :)

저 '!' 느낌표 keyword는 어떤거냐면요
'이 변수는 null이 아니야!' 라는걸 dart에게 알려주는 역할을 해요.
null값이 절대 아니야. 라고 확신이 드는 변수에만 사용하는 것을 권해요.
'!' 느낌표를 써줬는데 null 값이 들어가면 가차없이 런타임 에러가 나기 때문에...


https://dart.dev/codelabs/null-safety
위 주소로 가보면 아래의 null safety 예제들을 살펴볼게요.

Exercise: Nullable type parameters for generics

마찬가지로 null safety가 적용된 dart pad 입니다.
에러를 지우려면 어떻게 해야될까요? :)

정답은 !
aNullableListOfStrings List를 빈 상태로 초기화 시켜주고,
aListOfNullableStrings List 내부에 String값들이 들어가는데 중간에 null값이 들어가죠!
List 내부의 String 값들은 null이 될 수 없으니 !
null이 가능한 String값을 포함한다는 의미로 String type 옆에 '?'를 붙여주게 되면 해결이 돼요.
(처음에 List앞에 '?'를 붙여주는 건가 했는데 아니더라구요 .. ㅎ)

The null assertion operator (!)

'!' 키워드는 null이 아닌것을 확신할 때 쓰는 것이라고 했었죠.

다음 예제를 볼게요.

마찬가지로 null safety관련 에러가 나고 있어요.

line8을 보면 null을 가지고 있는 List listThatCouldHoldNulls 에서 값을 가져와서 변수 'b'에 저장하려고 했더니 에러가 나고 있는 상황이에요.
이상하죠. 분명 List의 First 값은 null이 아닌 int 값인데 에러를 내고 있어요.

우리는 List 내부의 첫번째 값이 확연하게 '2'라는 것을 알아요. 하지만 dart 는 모르는 것 같아요.
'이 값은 null일 가능성이 있기 때문에 변수 b에 저장할 수 없어' 라고 말하는 에러예요.

이럴 때는 확실하게 null 값이 아니라는걸 dart에게 알려주어야 합니다.

이럴 때 '!' 키워드를 쓰면 좋겠죠 ?
마지막에 '!'를 붙여주면서 이 값은 절대 null 이 아니니 저장해도 돼. 라고 말해줘요.

int b = listThatCouldHoldNulls.first!;

똑같이 변수 c에 저장하려는 것도 명시해주어야 해요.

int? couldReturnNullButDoesnt() => -3;

couldReturnNullButDoesnt method는 null을 반환할 가능성이 있긴하다 라고 명시해주고 있지만 정확히 '-3' 인 int 값을 반환하죠.

절대 null이 아닌것을 알 수 있긴하지만
dart에게 '!' 키워드로 미리 알린 후 변수에 저장합니다.

int c = couldReturnNullButDoesnt()!.abs(); 

Type promotion

다음예제로 가볼게요.

위코드에서 text는 null값을 가질 수 없는것은 이제 알겠죠 ?

에러가 발생하는 위치를 보면 text 변수를 선언할 때가 아니라 text 변수를 사용할 때 에러를 내고 있어요.

초기화를 안해줬으니 text에는 아무값도 들어있지 않은상태예요.

void main() {
  String text;

  if (DateTime.now().hour < 12) {
   text = "It's morning! Let's make aloo paratha!";
  } else {
   text = "It's afternoon! Let's make biryani!";
  }

  print(text);
  print(text.length);
}

주석을 풀어주고 if 문으로 text변수에 값을 지정해주고 있어요.
text 변수에 확실하게 값이 들어갔으니 dart는 더이상 에러를 내지 않아요.

Null safety exercise

null safety를 연습하기 아주 좋은 예제가 있어요.

위 코드를 보면 String 파라미터를 받고 String의 길이를 출력하는 getLength라는 method가 있어요.
str는'?' 키워드를 통해 str 값에 null이 들어갈 수 있는 가능성이 있다는 걸 알 수 있어요.

str이 만약 null 이라면 length를 반환할 수 없으니 에러를 내고 있는 모습이에요.

그렇기 때문에 추가적으로 조건을 걸어 null일경우에는 따로 처리를 해주어야 해요.

아래처럼 null일경우 0을 return 하도록 하면
error가 사라져요 :)

int getLength(String? str) {
  // Try throwing an exception here if `str` is null.
   if (str ==null ) {
      return 0;
   }
  return str.length;
}

void main() {
  print(getLength(null));
}

var type - null safety

dart에서 변수선언할 때 var로 선언을 하게되면 dart가 알아서 type을 유추해요.
즉 대입이 되는 값에 따라서 type이 결정되는 거예요.

여기서 중요한건 !
var로 type을 선언하면 Nullable, Non-Nullable 또한 자동 유추된다는 점이에요.
코딩을 할 때 var type은 '이건 nullable이야.' 라고 말을 안해줘도 돼요.

그 래 서
var type 뒤에는 '?'를 추가할 수 없어요.

var a1=10;		//ok
var a2=null; 		//ok
var a3; 		//ok

var? a4=null;//error

그럼 dynamic type은 ?

var는 nullable, non-nullable 구분이 필요없기 때문에 '?' 키워드 사용이 안된다고 했죠 ?

그럼 dynamic은 어떨까요?

dynamic은 nullable (?) 설정이 가능합니다.
에러가 안나요.

하지만 의미가 없어요.
dynamic type은 대입되는 값을 한정하지 않겠다는 의미이기 때문에 모든 type의 데이터가 들어갈 수 있어요.
즉 이미 nullable 상태라는거죠.

Local Variable 내에서는 Non-Nullable 초기화가 필요없다 ?

맞아요.
Local Variable은 초기화를 안해줘도 돼요.

Non-Nullable은 null대입이 불가능하기 때문에 초기화가 필요한거 아닌가요?
맞아요. 하지만 Top-Level 에 선언이 된 변수와 class 멤버 변수의 규칙이에요.

method 내에 선언되는 Local Variable은 변수가 Non-Nullable이라고 하더라도 선언과 동시에 초기화가 꼭 필요하지는 않아요.
(Non-nullable이기 때문에 null값은 들어가지 못해요.)

int a1 ;  	//error

class User{
    int a1 ; 	//error
}

textFunc(){
    int a1;     //Not error 
    a1 = null ; //error
}

late keyword

반복해서 언급했듯이 null 값이 들어갈 수 없는 non-nullable변수는 일반적으로 선언과 동시에 초기화가 되어야한다고 했죠!

하지만 이런경우가 있을 수 있어요.
non-nullable이기는 하지만 선언과 동시에 초기값을 줄 수 없는경우.
즉, 앱이 실행되면서 초기값이 정해지는 경우가 있을 수 있어요.

이럴 때 late keyword를 쓸 수 있어요.
late keyword는 말그대로 '초기화 시점을 뒤로 미루겠다'라는 뜻이에요.

late int a2;  //ok 

main() { 
    // print('${a2 + 10}');  //runtime error 
    a2=10; 
    print('${a2 + 10}');  //ok 
}

끝!

profile
Web , App developer wannabe 🧑🏻‍💻

1개의 댓글

comment-user-thumbnail
2022년 1월 12일

안녕하세요? 사이드 프로젝트 개발 포지션 제안드리고 싶습니다!shyun.bae@gmail.com으로 연락주세요:)

답글 달기