Dart로 불변성(immutability) 시작하기
Dart로 불변성(immutability) 시작하기
프로그래밍, 특히 프론트엔드 프로그래밍에서 불변성(immutability)이 큰 화두라는 건 부인할 수 없어요. Immutable.js 같은 라이브러리나 단방향 데이터 흐름 같은 개념들은 데이터가 자신도 모르는 사이에 변하지 않을 때 더 쉽게 추론할 수 있다고 주장해 왔죠. 그럼 Dart는 어떨까요? Dart에는 불변성과 불변 객체에 잘 맞는 개념들이 내장되어 있어서, 이 글에서 하나씩 살펴볼게요.
본문
불변성(immutability)이 프로그래밍, 특히 프론트엔드 프로그래밍에서 큰 화두라는 건 부인하기 어려워요. Immutable.js 같은 라이브러리와 단방향 데이터 흐름 같은 개념들은, 데이터가 당신도 모르는 사이에 변하지 않는다면 데이터를 추론하기가 더 쉬워진다고 주장해 왔어요.
객체 지향 프로그래밍과 함수형 프로그래밍에서, 불변(immutable) 객체(변경 불가능한 객체)란 생성된 후에는 상태를 수정할 수 없는 객체를 말해요. 반대로 가변(mutable) 객체(변경 가능한 객체)는 생성된 후에도 수정할 수 있어요.
그럼 Dart는 어떨까요? 불변성과 불변 객체에 잘 맞는 개념이 몇 가지 내장되어 있어요. 그 시작은 const 수정자(modifier)와 const 생성자(constructor)예요. ES6의 const(단순히 재할당이 불가능한 바인딩일 뿐)와 혼동하지 않도록 주의해 주세요:
// Define USER as a constant and give it an initial value.
const USER = { name: 'Joe'; }
// This will throw an error.
USER = {};
// But this will not.
USER.name = 'Jill';
Dart에서 const는 불변 바인딩(immutable binding) 이면서 불변 객체(immutable object)예요:
main() {
const user = const {'name': 'Joe'};
// Static error: "Constant variables cannot be assigned a value".
user = {};
// Runtime error: "Unsupported operation: Cannot modify unmodifiable Map".
user['name'] = 'Jill';
}
모든 리터럴(Null, String, int, double, num, bool, Map, List, Symbol)은 const가 될 수 있고, const 생성자로 사용자 정의 타입을 만들 수도 있어요:
class User {
final String name;
const User(this.name);
}
main() {
const user = const User('Joe');
}
정리해 볼게요 — const 인스턴스는 불변 바인딩이면서, 언어 차원에서 강제로 깊은 불변성을 갖게 되고, 컴파일 타임에 *정규화(canonicalized)*돼요. 즉 서로 다른 두 인스턴스도 동등하게 취급되며, 실행 시에는 단일 인스턴스로만 표현된다는 뜻이에요. 예를 들어 다음 코드는 아주 저렴한데, 런타임에 단 하나의 인스턴스만 할당하기 때문이에요:
class User {
final String name;
final List<String> cars;
User(this.name, {this.cars});
}
main() {
for (var i = 0; i < 100; i++) {
const users = const {
'Matan': const User(
'Matan Lurey',
cars: const [
'Truck',
'Jeep',
'GoKart',
],
),
};
}
}
더 배우고 싶다면? Dart 언어 투어에서 final과 const에 대해 읽어 보세요.
package:meta로 더 정밀한 정적 검사하기 (Further static checking with package:meta)
물론 const는 다소 제약이 있어요 — 클래스를 컴파일 타임에 만들어야 하니까 런타임에 데이터베이스를 읽어 const 객체를 만들 수는 없어요. 그래서 최근 package:meta에 “immutable” 애너테이션(annotation)을 도입했어요:
import 'package:meta/meta.dart';
// Error: This class inherits from a class marked as @immutable, and therefore
// should be immutable (all instance fields must be final).
@immutable
class User {
String name;
}
이 애너테이션을 쓰면 개발자들이 클래스를 깊은 불변성으로 유지하도록 강제하는 데 도움을 줄 수 있어요. const처럼 정규화되지는 않지만, 개발자들에게 꽤 유용하게 쓰일 수 있답니다.
이 글이 불변성에 대한 좋은 입문이 되었길 바라요. Dart나 불변성에 대해 더 배우고 싶다면 댓글이나 트위터로 알려 주세요.