문제점 및 “새로운 방식” 소개
기존에는 Billing::Plan::Factory.find_or_create_by!를 사용하여 각 플랜(standard, pro, enterprise)과 기간(monthly, yearly)별로 개별적으로 속성을 정의했습니다. 이는 코드가 비대하고 반복적이며, 노트북 화면의 절반 이상을 차지하는 등 개발 효율성을 저해하는 요인이었습니다.
이러한 문제점을 해결하기 위해 “프렌들리 속성 패턴”은 다음과 같은 간결한 구문을 제안합니다.
ruby
Billing::Plan.find_or_create_all_by_attrs!(
1.month => {standard: 10, pro: 50, enterprise: 100},
1.year => {standard: 100, pro: 500, enterprise: 1000}
)
이 방식은 :name, :interval, :amount와 같은 중복되는 속성 키를 제거하여 코드의 양을 대폭 줄이고 가독성을 높였습니다. 이는 표준 가격 페이지의 구조(기간 토글, 플랜-가격 컬럼)를 직관적으로 모델링합니다.
구현 원리 및 유연성
프렌들리 속성 패턴의 핵심은 다양한 입력 구조(배열, 해시, 단일 값)를 표준 키-값 속성으로 변환하는 FriendlyAttrs.new(attrs).resolve 인터페이스입니다.
- 변환(Conversion): 입력된 값의
유형(Types)을 기반으로 속성을 추론합니다.- 정수(Integers)는
amount로 변환됩니다 (예:50->{amount: 50}). - 심볼(Symbols) 또는 문자열(Strings)은
plan names로 변환됩니다 (예::pro->{name: :pro}). ActiveSupport::Duration객체는interval로 변환됩니다 (예:1.month->{interval: 1.month}).
- 정수(Integers)는
-
값 조회(Value lookup): 특정 문자열이나 심볼을 추가로 파싱하여
currency와 같은 특정 속성으로 매핑할 수 있습니다. -
혼합(Mixing): 프렌들리 속성과 표준 속성을 함께 사용할 수 있습니다 (예:
[1.month, {name: :pro, amount: 50}]). -
상위 집합(Superset): 표준 속성 해시도 유효한 입력으로 간주되어 기존 코드와의 호환성을 유지합니다.
- 객체 트리(Object tree): 중복을 줄이기 위해 중첩된 해시 구조를 활용하여 속성 집합을 정의합니다. 예를 들어,
{1.month => {standard: 10, pro: 50, enterprise: 100}}는 내부적으로 여러 개의 속성 배열로 분해됩니다.
이 패턴은 billing_plans(1.month => [:standard, :pro, :enterprise])와 같은 테스트 헬퍼나 Billing::Plan.find_sole_by_attrs(:pro, 1.month)와 같은 단일 플랜 조회 등 다양한 사용 사례에서 간결함을 제공합니다. 입력 순서의 유연성도 특징입니다.
제한 사항 및 기타 사용 사례
이 패턴은 주로 개발자가 수동으로 속성을 입력하는 시나리오에 적합하며, JSON이나 YAML과 같은 데이터 저장 또는 API 통신에는 권장되지 않습니다. IoT 스마트 도어 잠금 시스템과 같은 다른 도메인에도 적용될 수 있는 잠재력을 가지고 있지만, 모든 애플리케이션이나 모델에 강제로 적용할 필요는 없습니다.