Dart로 불변성(immutability) 시작하기

Dart로 불변성(immutability) 시작하기

프로그래밍, 특히 프론트엔드 프로그래밍에서 불변성(immutability)이 큰 화두라는 건 부인할 수 없어요. Immutable.js 같은 라이브러리나 단방향 데이터 흐름 같은 개념들은 데이터가 자신도 모르는 사이에 변하지 않을 때 더 쉽게 추론할 수 있다고 주장해 왔죠. 그럼 Dart는 어떨까요? Dart에는 불변성과 불변 객체에 잘 맞는 개념들이 내장되어 있어서, 이 글에서 하나씩 살펴볼게요.

출처: An intro to immutability with 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나 불변성에 대해 더 배우고 싶다면 댓글이나 트위터로 알려 주세요.

더 알아보기