Value Objects (VO) 값 객체
Value Object란?
Value Object는 값 그 자체를 의미하는 객체입니다. 일반적으로, Value Object는 단순한 원시 값(primitive value) 대신 해당 값을 감싸는 객체로 표현됩니다. 이러한 객체는 주로 도메인 모델에서 중요한 역할을 하며, 값의 의미를 명확히 하고, 실수를 방지하는 데 도움을 줍니다.
Value Object를 어떻게 적용할 것인가?
값을 명확하게 정의 Value Object는 값을 단순한 원시 타입이 아닌 의미 있는 타입으로 정의하는 데 사용됩니다. 이로 인해 값이 무엇을 의미하는지 명확히 알 수 있습니다.
예시:
String
→ProductId
,Address
,Name
등Int
/Double
→Duration
,Money
,Color
등
메소드 파라미터로 의미 있는 타입 사용 메소드에서 값을 사용할 때, 단순한 숫자나 문자열 대신 Value Object를 사용하여 값의 의미를 확실하게 전달합니다. 좋지 않은 예:
좋은 예:
Value Object를 왜 사용해야 하는가?
타입의 명확한 정의 Value Object는 데이터를 더 구체적이고 의미 있는 타입으로 변환하여, 코드의 가독성을 높이고 실수를 줄일 수 있습니다.
컴파일 시 실수 방지 잘못된 타입이나 값에 대한 오류를 컴파일 시에 발견할 수 있습니다. 이를 통해 런타임에서 발생할 수 있는 오류를 예방할 수 있습니다.
예시:
Money
Value Object는 반드시 양수여야 한다는 규칙을 포함할 수 있습니다. 잘못된 값이 들어가면 컴파일 오류가 발생합니다.
일관된 값 검증 Value Object는 값을 생성할 때 유효성 검증을 내장하여, 특정 조건을 만족하는 값만 생성되도록 보장합니다. 이렇게 하면 애플리케이션 전체에서 일관된 데이터 검증을 유지할 수 있습니다.
예시:
ProductId
는 특정 형식을 따라야 하며,Money
는 음수가 될 수 없습니다.
불변성(Immutable) Value Object는 불변 객체로 설계됩니다. 즉, 한 번 생성된 값은 변경되지 않으며, 새로운 값을 가지려면 새로운 객체를 생성해야 합니다. 불변성 덕분에 상태 변경 추적이 용이하고, 코드에서 발생할 수 있는 부수 효과를 최소화할 수 있습니다.
예시:
Money
객체의 값은 한 번 설정되면 변경되지 않으며, 새로운 값은 새 객체로 생성됩니다.
부수 효과(Side Effect) 제거 Value Object는 불변성 덕분에 함수 내에서 상태 변경을 하지 않으므로 부수 효과를 제거합니다. 이는 예기치 않은 오류를 줄이고, 함수의 결과를 예측 가능하게 만듭니다.
예시:
Money
객체를 사용하는 함수는 외부에서 값이 변경되지 않으므로, 함수의 실행 결과가 예측 가능합니다.
Flutter 에서 ValueObject
구현 예시
ValueObject
구현 예시 Flutter 에서 ValueObject
구현은 아래처럼 추상클래스 / Equatable 를 사용한 2가지 방식이 있을 수 있습니다.
두 방식의 비교
특징
ValueObject
(수동 구현)
Equatable (패키지 활용)
코드 간결성
중복 코드가 필요
props로 간결한 정의 가능
유연성
다양한 타입 확장 가능
props에만 제한됨
의존성
외부 라이브러리 필요 없음
Equatable 패키지가 필요
동등성 비교
직접 ==
와 hashCode
구현 필요
자동으로 동등성 비교 구현 가능
복잡한 객체 지원
직접 구현해야 함
props를 통해 간단히 비교 가능
Inline Class 를 사용하여 더 간결하게 하기
새로운 타입을 안전하고 최적화된 형식으로 정의하는 방법
Kotlin Inline Class
Kotlin에서 inline class
는 원시 타입을 감싸는 타입 안전한 래퍼를 생성할 수 있는 기능으로, 런타임 오버헤드 없이 사용할 수 있습니다. 아래 예시는 비밀번호를 안전하게 다루는 Password
값을 객체로 감싸는 방법을 보여줍니다.
Kotlin Inline Class 예시
주요 특징
@JvmInline
과value
키워드를 사용하여 값을 감싼inline class
를 정의합니다.생성자에서 값을 초기화하고,
init
블록에서 값에 대한 검증을 합니다.length
속성처럼 추가적인 속성과 메서드를 포함할 수 있습니다.inline class
는 컴파일 시에 인라인 처리되어 성능이 개선됩니다.
핵심 장점
타입 안전성:
Password
는 단순한 문자열이 아니라, 비밀번호를 나타내는 명확한 타입을 가집니다. 이를 통해 값의 의미를 코드에서 명확히 구별할 수 있습니다.검증 로직 포함:
Password
객체의 값이 비어 있지 않도록require
를 사용하여 검증합니다.성능 최적화:
inline class
는 컴파일 시에 인라인으로 처리되어, 메모리 오버헤드 없이 효율적으로 동작합니다.
Flutter/Dart Inline Class
Dart에서 inline class
는 Kotlin과 유사한 개념을 제공하지만, inline
키워드를 사용하여 값 객체를 정의합니다. UserId
클래스는 사용자 ID를 감싸고, 이를 통해 타입 안전성과 성능을 확보할 수 있습니다.
Dart Inline Class 예시
주요 특징
inline
키워드를 사용하여 클래스 정의.생성자에서 값을 초기화하고, 유효성 검사 메서드인
isValid()
를 추가합니다.toString()
메서드를 오버라이드하여 출력 형식을 정의합니다.
핵심 장점
타입 안전성:
UserId
는 단순한 문자열이 아니라, 사용자 ID를 나타내는 명확한 타입을 제공합니다.유효성 검사:
isValid()
메서드를 통해 값이 유효한지 검사할 수 있습니다.성능 최적화:
inline class
는 컴파일 시에 인라인 처리되어 메모리 오버헤드가 없으며, 성능에 영향을 미치지 않습니다.
Kotlin과 Dart의 차이점
Kotlin:
@JvmInline
과value
키워드를 사용하여inline class
를 정의합니다. JVM에서 동작하며,@JvmInline
어노테이션이 필요합니다.Dart:
inline class
라는 키워드를 사용하여 값을 감쌉니다. Kotlin과 달리 특별한 어노테이션 없이inline
키워드만 사용합니다.공통점: 두 언어 모두 값 객체를 사용하여 타입 안전성을 제공하고, 성능을 최적화합니다.
inline class
는 컴파일 시에 인라인 처리되어 런타임 오버헤드를 줄입니다.
Last updated