핵심 요약
var는 추론된 타입을 유지하지만 dynamic은 정적 검사를 미루며, 알 수 없는 값의 경계에서는 Object?와 타입 검사를 먼저 고려할 수 있습니다.
검수 범위
var가 dynamic으로 변한다는 오해를 바로잡고, 초기값 없는 선언과 외부 데이터 처리의 차이를 확인했습니다.
Dart(Flutter)에서 var는 “타입을 자동으로 정해주는 키워드”로 알려져 있습니다. 그래서 많은 초보자가 var를 “타입을 안 써도 되는 편한 문법” 정도로만 이해하고 넘어가는데, 이 과정에서 자주 생기는 오해가 하나 있습니다. var는 보통 안전하게 타입을 추론해 고정해주지만, 작성 방식이 조금만 어긋나면 결과적으로 dynamic처럼 느슨해지거나, 혹은 Object?로 다뤄야 하는 상황이 생긴다는 점입니다. 이 글에서는 “var를 잘못 쓰면 왜 dynamic/Object?처럼 취급되는 느낌이 나는지”를 정확히 정리해드리겠습니다.
var 초기값이 있으면 타입이 고정
var는 초기값을 보고 타입을 추론합니다. 그리고 그 타입은 이후에 고정됩니다. 즉, var는 타입이 없는 게 아니라 “컴파일러가 타입을 정해준 변수”입니다.
var x = 1; // int로 추론되어 고정 x = 2; // OK x = "a"; // 컴파일 에러 (int에 String 대입 불가)
이 경우 var는 오히려 실수를 줄여줍니다. 다른 타입을 넣는 순간 컴파일 단계에서 바로 막아주기 때문입니다.
var이 dynamic 처리되는 경우
문제가 되는 패턴은 아래처럼 초기값 없이 var를 선언하는 경우입니다.
var x; // 초기값 없음 → 추론 불가 → dynamic처럼 동작 x = 1; // OK x = "a"; // OK
초기값이 없으면 추론할 근거가 없고, 이때 지역 변수는 사실상 dynamic 구간으로 들어갑니다.
즉, “var를 잘못 쓰면 dynamic이 된다”는 말은 보통 var x; 패턴을 경고하는 표현입니다.
이 상태에서는 컴파일러가 타입 관련 실수를 강하게 잡아주기 어렵고, 실수는 런타임에서 문제로 드러날 가능성이 커집니다.
dynamic 위험한 이유
dynamic은 타입 검사와 멤버 검사를 느슨하게 만들어, 컴파일 단계에서 잡힐 오류가 실행 중으로 넘어가기 쉽습니다.
dynamic d = "hi"; d.notARealMethod(); // 컴파일은 통과, 실행 중 NoSuchMethodError 가능
초보자가 var x;를 자주 쓰면, 코드 전체가 이런 위험한 성격을 띠기 쉬워집니다. 타입이 안정적으로 굳지 않기 때문이죠.
Object? 언급되는 이유
많은 사람이 “여러 타입이 들어올 수 있는 값”을 다루려 할 때 dynamic을 떠올립니다. 하지만 실무에서는 종종 Object?를 더 선호합니다. 이유는 안전하게 막아주기 때문입니다.
-
Object?는 “여러 타입이 들어올 수 있다”를 표현하면서도, -
그 값을 사용할 때는 캐스팅을 강제해서, 실수 가능성을 줄입니다.
Object o = "hi"; // o.length; // 컴파일 에러: Object에는 length가 없음 (o as String).length; // 캐스팅 후 OK dynamic d = "hi"; d.length; // 컴파일 OK (대신 런타임 위험)
-
dynamic: 쓰기는 편하지만, 잘못 쓰면 런타임 에러로 이어지기 쉬움 -
Object?: “확실히 확인하고(캐스팅하고) 쓰자”를 강제하는 더 안전한 선택
그래서 var를 설명할 때 dynamic과 Object?를 같이 묶어 “타입이 불명확한 값 처리”의 관점에서 비교하는 경우가 많습니다.
실전에서 안전하게 쓰는 기준
var가 위험해지는 순간은 거의 정해져 있습니다.
-
초기값 없는
var x;는 피한다 -
타입이 불명확한 값(특히 JSON/Map/API 응답)을 받을 때는
dynamic을 남발하지 않는다 -
“뭐가 들어올지 모르는 값”이면
Object?로 받고, 사용할 때 캐스팅/검증을 한다 -
“타입이 확실한 값”이면
var를 써도 안전하다(초기값이 있는 형태로)