kym8821 님의 블로그
한 페이지로 공부하는 Dart 기초 : 변수와 타입 추론 본문
Dart 시작하기
환경은 dartpad.dev를 사용했다.
따로 개인 PC 환경에 Dart 환경을 설정할 필요가 없기에 간단한 실습에 유용하다.
DartPad
dartpad.dev
기본적으로 Dart의 entry point는 main 함수이다.
JS나 TS와는 달리 각 코드의 마지막에 세미콜론을 붙여야 한다는 점에서 C++와 비슷하다.
void main(){
print("Hello World");
}
변수 선언하기
Dart의 변수 선언에 대한 개념들에 대해서 알아보자
var 키워드를 활용한 변수 선언
var 키워드를 사용하면 아래와 같이 변수를 선언할 수 있다.
var 키워드를 사용한다면, Dart 컴파일러가 자동으로 타입을 추론한다.
void main(){
var v = 1; // int
var s = "String"; // string
}
var는 일반적인 상황에서는 한 번에 여러개의 타입을 담을 수 있는 변수가 아니다.
따라서 아래와 같이 서로 다른 타입의 값을 같은 변수에 할당할 수는 없다.
// 이 코드는 정상적으로 작동하지 않는다.
void main(){
var a = 1;
a = "String"; // 에러 발생 : int 타입인 var 변수에 String 타입 값을 넣었기 때문
}
사용처 : var 키워드는 관습적으로 메서드나 함수 내부에서 지역변수를 선언할 때 사용한다
명시적 타입 지정을 통한 변수 선언
var을 쓰지 않고, 대신 아래와 같이 명시적으로 변수의 타입을 지정하여 변수를 선언할 수 있다.
void main(){
int number = 1; // int : 정수
String string = "String"; // String : 문자열
double float_number = 1.23; // double : 실수
List<int> number_list = [1,2,3,4,5]; // List<T> : T 타입을 담을 수 있는 리스트 혹은 배열
}
사용처 : 명시적 타입 지정은 관습적으로 클래스의 변수나 프로퍼티 선언 시 사용한다.
dynamic 키워드
여러 타입을 갖는 변수를 선언할 수 있는 키워드이다. 아래와 같이 사용한다.
아래 코드에서는 dynamic 키워드를 활용하여 변수를 선언하였다. dynamic은 변수의 값에 따라 여러 타입을 가질 수 있다.
void main(){
dynamic dv;
dv = 1.0;
print(dv.runtimeType); // res : int
dv = "hello";
print(dv.runtimeType); // res : String
}
위 코드에서 dynamic 키워드를 사용했을 때, 변수에 여러 타입을 담을 수 있음을 확인할 수 있다.
그런데 위 코드에서 dynamic 대신 var 키워드를 사용하면 이상하게도 정상적으로 동작한다.
void main(){
var dv;
dv = 1.0;
print(dv.runtimeType); // res : int
dv = "hello";
print(dv.runtimeType); // res : String
}
var에는 서로 다른 타입의 값을 담을 수 없다고 했는데, 왜 dynamic 대신 var를 사용해도 동작하는걸까?
dart는 초기화되지 않은 var 변수를 dynamic으로 추론하기 때문이다. 따라서 여러 타입의 변수를 담을 수 있다.
final 키워드
어떠한 변수의 값을 변경할 필요가 없거나 변경하면 안되는 경우 사용한다.
아래와 같이 한번 값이 결정되면, 추후 수정할 수 없다.
// 이 코드는 정상적으로 작동되지 않는다.
void main(){
final fv;
fv = 1; // 성공 : 아직 final 변수에 값이 없으므로 1이 대입됨
fv = 2; // 에러 발생 : 이미 1이 대입된 final 변수에 새로운 값을 할당 시도했기 때문
}
final도 var와 마찬가지로 기본적으로 자동으로 타입을 체크해주지만, 명시적으로 타입을 지정해줄 수 있다.
void main() {
final String a = "Hello";
final int b = 1;
}
final 변수는 주로 함수의 결과, 외부 데이터 등 주로 런타임 동안 결정되는 값을 갖는다. 의무는 아니다.
그럼 컴파일 동안에 결정되는 불변값은? : 주로 const 키워드를 사용한다.
const 키워드
const는 final와 마찬가지로 한 번 값이 결정되면, 해당 변수의 값을 수정할 수 없다.
차이가 있다면, final와 달리 const는 컴파일 시점에 값이 결정되어야 한다는 것이다.
// 이 코드는 정상적으로 작동하지 않는다.
// func : 정수 1을 반환하는 간단한 사용자 지정 함수
int func(){
return 1;
}
void main() {
const pos1 = 1+2;
const pos2 = "Hello";
const impos = func(); // Error : 함수의 결과는 const에 할당 불가
}
const에 할당할 수 있는 값은 크게 정적인 값, 정적인 값의 연산 결과, const 생성자로 생성한 객체가 있다.
그 외 대부분의 외부 API 데이터, 함수 연산 결과는 런타임에 결정되기에 사용할 수 없다.
null safety
일반적인 상황에서 dart의 모든 변수는 non-nullable, 즉 null 값을 가질 수 없다.
만약 null 값을 가져야 하는 상황이 있다면, null safety 개념을 통해 null값이 들어올 수 있음을 표기해야 한다.
예외적으로 dynamic 타입에는 null이 들어갈 수 있다.
dart의 모든 방식은 non-nullable이다.
따라서 아래와 같이 타입 뒤에 ?를 붙여 null이 들어올 수 있음을 명시적으로 나타내야한다.
// 이 코드는 정상적으로 동작하지 않는다.
void main() {
String? a;
String b;
a = null; // 정상동작
b = null; // Error : String이 non-nullable이기에 에러 발생
}
아래와 같은 상황에서는 코드가 정상 동작한다.
그런데 dart의 모든 변수들은 null을 가질 수 없는 것이 아니었나? 왜 아래의 코드는 정상 동작할까?
void main() {
var a;
a = null;
a = "String";
}
dynamic 섹션에서 설명했듯, 초기화되지 않은 var은 dynamic 타입을 갖고, dynamic은 nullable이기에 예외적으로 동작한다.
late 키워드
어떠한 변수에 값을 나중에 넣어줘야 하는 상황에 사용한다.
late로 선언한 변수는 초기화되지 않는다면 컴파일 에러가 발생한다. 따라서 런타임에서 null이 사용되는 것을 막는다.
// 이 코드는 정상적으로 동작하지 않는다.
void main() {
late var a;
print(a); // Complile Error : a가 초기화되지 않았기에 오류 발생
a = 1;
print(a); // a가 1로 초기화되었기에 정상 동작
}
a가 초기화되지 않은 시점에서 a를 사용했을 때 컴파일 에러가 발생했음을 확인할 수 있다.
Dart 변수 : 정리
1. Dart에서는 var 키워드를 통해 자동으로 타입을 추론해준다.
2. 변수 선언시 String, double, int 등 명시적으로 타입을 지정해줘도 된다.
3. dynamic 키워드는 null을 포함한 다양한 타입을 가질 수 있는 변수를 선언한다.
4. 초기화되지 않은 var 변수는 내부적으로 dynamic 타입으로 추론된다.
5. const와 final은 모두 한 번 초기화 된 후 값을 수정할 수 없다. const는 상수, 상수연산 등 컴파일 시간에 결정되는 값을, final은 주로 런타임에 결정되는 값을 가질 수 있다.
6. 일반적으로 Dart의 모든 타입은 null 값을 가질 수 없다. 만약 필요하다면 dynamic 타입을 사용하거나 명시적 타입 뒤에 "?"를 붙여 null을 가질 수 있음을 표기해야 한다.
7. late 키워드는 추후 초기화할 수 있는 변수를 선언해준다. 컴파일 단계에서 초기화 여부를 확인해주기에 잘못된 값이 사용되는 것을 방지해준다.
'Dart' 카테고리의 다른 글
한 페이지로 공부하는 Dart 기초 - 클래스(class) (0) | 2025.01.09 |
---|---|
한 페이지로 공부하는 Dart 기초 - 타입과 함수 (0) | 2025.01.08 |