Ruby의 Data
클래스는 별도의 외부 라이브러리 없이 핵심 Ruby 클래스로 제공됩니다. Data.define
메서드를 사용하여 간단하게 값 객체를 정의할 수 있으며, 정의된 객체는 new
또는 []
메서드를 통해 인스턴스화할 수 있습니다. 예를 들어, MarsRover = Data.define(:name, :x, :y)
와 같이 정의하면 MarsRover.new('Curiosity', 0, 0)
형태로 객체를 생성할 수 있습니다. Data
객체의 가장 중요한 특징은 불변성입니다. 한 번 생성된 Data
인스턴스의 속성은 변경할 수 없으며, 변경을 시도할 경우 NoMethodError
가 발생합니다. 또한, Data
객체는 기본적으로 ==
연산자를 통해 값에 의한 동등성 비교를 지원합니다. 즉, 서로 다른 인스턴스라도 내부 값이 동일하다면 true
를 반환합니다. 이는 값 객체의 핵심 원칙 중 하나입니다. 더 실용적인 예시로, Location = Data.define(:x, :y)
와 같이 위치를 나타내는 값 객체를 정의하고, Rover
클래스에서 Location
객체를 사용하여 로버의 위치를 관리할 수 있습니다. 로버의 위치를 업데이트할 때는 기존 Location
객체를 변경하는 것이 아니라 새로운 Location
객체를 생성하여 할당하는 방식으로 불변성을 유지합니다. Data
인스턴스는 기본적으로 ==
및 eql?
을 지원하지만, <
, >
, between?
와 같은 비교 연산자는 기본적으로 제공하지 않습니다. 이러한 비교 기능을 활성화하려면 Comparable
모듈을 포함하고 <=>
연산자를 직접 정의해야 합니다. 예를 들어, Location
클래스에 include Comparable
을 추가하고 def <=>(other); [x, y] <=> [other.x, other.y]; end
와 같이 구현하면 두 Location
객체 간의 크기 비교가 가능해집니다. Data
클래스는 자체적으로 유효성 검사(validation) 기능을 내장하고 있지 않지만, initialize
메서드를 오버라이드하여 사용자 정의 유효성 검사 로직을 추가할 수 있습니다. 예를 들어, x
와 y
가 숫자인지 확인하는 로직을 initialize
내부에 구현하여 유효하지 않은 값에 대한 ArgumentError
를 발생시킬 수 있습니다. 만약 더 정교한 타입 강제(type coercion) 및 유효성 검사 기능이 필요하다면, dry-struct
와 같은 외부 라이브러리를 활용하는 것이 좋은 대안이 될 수 있습니다. dry-struct
는 스키마 기반의 타입 정의와 강력한 유효성 검사 기능을 제공하여 복잡한 도메인 객체를 모델링하는 데 유용합니다.
Ruby Data 클래스: 값 객체 정의 및 Comparable 모듈 활용
Ruby `Data` Class – A Convenient Way to Create Value Objects
작성자
발행일
2025년 08월 14일
핵심 요약
- 1 Ruby 3.2에 도입된 `Data` 클래스는 불변하며 값에 의한 동등성을 가지는 값 객체(Value Object)를 간편하게 정의할 수 있도록 지원합니다.
- 2 값 객체는 도메인 모델에서 의미론적 명확성과 동작을 캡슐화하며, 원시 타입의 한계를 극복하고 코드의 가독성과 정확성을 높입니다.
- 3 `Comparable` 모듈을 포함하고 `<=>` 연산자를 정의함으로써 `Data` 인스턴스 간의 비교 기능을 확장할 수 있으며, 필요에 따라 커스텀 유효성 검사 로직을 추가할 수 있습니다.
도입
Ruby 3.2에서 새롭게 선보인 `Data` 클래스는 마틴 파울러(Martin Fowler)와 에릭 에반스(Eric Evans)가 주창한 값 객체(Value Object) 개념을 루비 언어 차원에서 구현하기 위해 도입되었습니다. 값 객체는 좌표(x, y)나 자동차의 속도와 같이 여러 원시 값들의 조합으로 표현되는 속성들에 의미론적 명확성과 고유한 동작을 부여하여, 단순히 정수나 문자열 같은 원시 타입을 사용하는 것보다 더 풍부한 도메인 모델링을 가능하게 합니다. 기존의 `Struct` 클래스도 유사한 역할을 수행할 수 있지만, `Struct`가 가변(mutable) 객체인 반면 `Data` 클래스는 불변(immutable) 객체를 지향한다는 점에서 차이가 있습니다. 이러한 불변성은 값 객체의 핵심적인 특성 중 하나로, 예측 가능한 동작과 안정적인 코드를 작성하는 데 기여합니다. 본문에서는 Ruby의 `Data` 클래스가 값 객체의 주요 특성인 식별자 없음(No Identity), 불변성(Immutable), 값에 의한 동등성(Equality by Value), 작고 단순함(Small and Simple), 재사용성(Reusable)을 어떻게 지원하는지 심층적으로 탐구합니다.
결론
결론적으로, Ruby의 `Data` 클래스는 Ruby 3.2부터 제공되는 강력한 기능으로, 최소한의 코드로 불변하고 값에 의한 동등성을 가지는 값 객체를 정의할 수 있도록 돕습니다. 이는 도메인 모델의 의미론적 명확성을 높이고 코드의 안정성과 예측 가능성을 향상하는 데 크게 기여합니다. 비록 `Data` 클래스가 기본적으로 유효성 검사를 지원하지는 않지만, `initialize` 메서드 오버라이드 또는 `Comparable` 모듈 활용과 같은 확장 메커니즘을 통해 필요한 기능을 추가할 수 있습니다. 또한, `dry-struct`와 같은 전문적인 라이브러리는 더 엄격한 타입 시스템과 고급 유효성 검사가 요구되는 복잡한 시나리오에서 훌륭한 대안이 될 수 있습니다. 위치, 금액 또는 기타 복합 데이터를 모델링할 때 값 객체를 활용함으로써 코드는 더욱 명확해지고 정확성을 확보할 수 있습니다. `Data` 클래스는 Ruby 개발자가 더욱 견고하고 표현력 있는 애플리케이션을 구축하는 데 필수적인 도구입니다.