들어가며
소프트웨어는 다양한 데이터를 다루는 복잡한 시스템이다. 이러한 데이터는 단순히 숫자나 문자열로 표현되는 것이 아니라, 그 자체로 의미를 가지며, 이를 잘 이해하고 활용하는 것이 소프트웨어 개발의 핵심이다. 이번 글에서는 이러한 데이터의 양이나 크기를 정량적으로 표현하는 방법인 측도(Measure)에 대해 알아보겠다.
먼저 측도라는 단어가 생소하게 느껴질 수 있다. 측도는 데이터의 양이나 크기를 정량적으로 표현하는 방법을 말한다. 수학적으로는 개수, 길이, 넓이, 부피 등의 개념을 추상화한 것을 말한다. 예를 들어, 마리, 권, 그루, 다스와 같은 수량 개념부터 cm, kg, mL와 같은 단위, 달러나 엔과 같은 화폐까지 모두 측도의 일종이다. 좀 더 나아간다면 바퀴, 번과 같은 횟수 개념도 측도로 볼 수 있다.
이러한 측도는 비즈니스 관점에서 중요하다. 물건을 판매한다면 가격과 수량을 표현해야 하고, 글로벌 서비스를 한다면 다양한 화폐 단위를 다룰 수 있어야 한다. 또한, 단위 변환과 연산에 대한 고려도 필요하다. 예를 들어, 미국에서 판매되는 제품의 가격을 한국 원화로 변환해야 하거나, 세금이나 수수료 등을 고려하여 계산해줄 수도 있다.
추상화된 것을 구체화하기
우리는 숫자를 코드로 표현하고 싶을 때 Int
나 Float
와 같은 기본 자료형을 사용한다. 하지만 이러한 기본 자료형은 단순히 숫자일 뿐, 그 자체로 의미를 가지지 않는다. 예를 들어, 100
이라는 숫자는 단순한 정수일 뿐이라 무게인지 길이인지 알 수 없다. 그래서 보통 다음과 같이 변수명에 의존하여 의미를 부여한다.
val weight: Int = 100 // 무게
val length: Int = 100 // 길이
하지만 이러한 방식은 실수할 여지가 많다. Int
라는 타입은 아무 숫자나 받을 수 있기 때문에, 잘못된 값을 넣을 수도 있다. 이를 방지하기 위해서 코드 리뷰나 테스트를 통해 확인할 수 있지만, 이런 단순한 문제로 생산성을 떨어뜨리는 것은 바람직하지 않다. 따라서 실수를 방지하기 위해 별도 타입을 만들 수 있다. 예를 들어, 무게를 표현하기 위해 Weight
라는 클래스를 만들고, 이를 사용하여 무게를 표현할 수 있다.
class Weight(val value: Int) {
init {
require(value >= 0) { "Weight must be positive" }
}
}
val weight = Weight(100) // 무게
위와 같이 Weight
라는 클래스를 만들면, 무게를 표현할 때는 항상 Weight
타입을 사용해야 하므로 실수를 방지할 수 있다. 또한, 클래스에 다양한 연산을 추가하여 무게에 대한 연산을 쉽게 수행할 수 있다.
class Weight(val value: Int) {
init {
require(value >= 0) { "Weight must be positive" }
}
operator fun plus(other: Weight): Weight {
return Weight(value + other.value)
}
operator fun minus(other: Weight): Weight {
return Weight(value - other.value)
}
operator fun times(scalar: Int): Weight {
return Weight(value * scalar)
}
operator fun div(scalar: Int): Weight {
return Weight(value / scalar)
}
}
val weight1 = Weight(100)
val weight2 = Weight(200)
val totalWeight = weight1 + weight2 // 300
val halfWeight = weight1 / 2 // 50
이러한 객체를 DDD(도메인 주도 설계)에서는 값 객체(Value Object)라고 부른다. 값 객체는 단순히 숫자나 문자열을 감싸는 것이 아니라, 그 자체로 의미를 가지는 객체를 말한다.
측도에 대한 모델링은 이런 개념에서 시작된다. 추상화된 값을 구체화하여 의미를 부여하고, 이를 통해 코드의 가독성을 높이고 실수를 방지하며, 비즈니스에 필요한 다양한 연산을 추가할 수 있다.
측도 모델링
앞서 구현한 값 객체를 이용할 수도 있지만, 조금 더 범용적으로 모델을 만드는 것도 가능하다. 단순히 무게나 길이와 같은 개별 타입을 만드는 것이 아니라, 모든 측도에 공통으로 적용할 수 있는 추상화된 모델을 구현해보자.
값과 단위
측도 시스템을 구현할 때 가장 기본이 되는 것은 '값(Value)'과 '단위(Unit)'의 개념이다. 모든 측도는 값과 단위의 조합으로 표현할 수 있다. 예를 들어 "5kg"는 값이 5이고 단위가 킬로그램인 측도이다.
그리고 단위는 연관성이 있는 단위들끼리 변환이 가능하다. 예를 들어 킬로그램(kg)과 그램(g)은 서로 변환이 가능하다. 이러한 단위 간의 변환을 지원하기 위해서는 별도로 단위계를 정의할 수 있어야 한다. 이와 같은 요구사항을 기반으로 다음과 같이 다이어그램을 그려볼 수 있다.

우리는 전체 측도 모델에 대한 기본 구조를 위와 같이 정의할 것이다. Measure
는 모든 측도를 표현하는 모델이며, Unit