Ruby의 타입 정의 및 타입 오류 이해
-
타입의 역사적 의미: C 계열 언어에서 타입은 컴파일러의 메모리 관리 지시 역할을 했습니다.
-
Ruby에서의 타입: Ruby에서는 타입이 데이터에 대한 동작 방식(behavior)을 결정하는 ‘큐’ 역할을 합니다.
-
정적 타입 언어 사용자의 관점: 이들은 타입을 ‘유효 값의 부분집합’으로 이해하며, Ruby가 기본적으로 이러한 값 제한을 제공하지 않는 것에 혼란을 느낍니다.
-
타입 오류: 예상치 못한 타입의 객체로 인해 런타임에 발생하는 오류로, 특히
nil값으로 인한NoMethodError가 가장 흔합니다.
타입 오류 방지를 위한 4가지 전략
### 1. Blow Up (오류 발생)
-
개념: 예기치 않은 입력에 대해 즉시 오류를 발생시켜 문제를 명확히 알립니다.
-
장점: 문제 조기 발견.
-
단점: 프로그램 흐름 중단, 비관용적인 사용자 경험. ### 2. Coerce (강제 변환)
-
개념: 들어오는 데이터의 형태를 예상하는 형태로 변경합니다.
-
예시:
nil을0이나 빈 배열로 기본값 설정, 문자열/심볼 키를 정규화(`Hash
with_indifferent_access` 등).
-
장점: 유연한 입력 처리.
-
단점: 문제 발생을 숨길 수 있어 디버깅을 어렵게 만들 수 있습니다. ### 3. Minimize (최소화)
-
개념: 타입 오류의 일반적인 원인에 대한 노출을 줄입니다.
-
예시:
Array()와 같이nil을 빈 컬렉션으로 변환하여nil체크를 줄입니다.nil의 의미를 명확히 하고, 명시적인 실패 심볼이나Result객체 사용을 고려합니다. -
핵심:
nil사용 시 의도적으로 접근하며, ‘Null Object Pattern’도 한 방법입니다. ### 4. Objectify (객체화) -
개념: 도메인 개념을 클래스로 만들어 Ruby의 타입 시스템을 적극 활용합니다.
- 예시:
Experience클래스:kind와years를 가진 객체로 만들어 자체 유효성 검사를 수행합니다.SetOfExperiences클래스: 경험 컬렉션을 래핑하여 중복 처리, 필터링 등 추가 동작을 캡슐화합니다.Duration서브클래스(Years,Days): ‘년도’와 ‘일’처럼 동일한 정수형이지만 의미가 다른 추상적인 타입 오류(단위 불일치)를 방지합니다.
- 장점:
initialize메서드에서 데이터 유효성 검사를 수행하는 ‘경계(boundary)’를 설정하여 코드의 복잡성을 줄이고 제어력을 높입니다.
정적 타이핑과 데이터 유효성 검사
-
정적 타이핑은 데이터 유효성 검사의 부분집합에 불과하며, 복잡한 시스템에서는 항상 추가적인 데이터 유효성 검사가 필요합니다.
-
Ruby의 동적 특성은 이러한 데이터 유효성 검사를 통해 간접적으로 타입 안전성을 확보할 수 있습니다.
literal Gem을 통한 유연한 타입 검증
-
개념: Joel Drapper의
literalGem은properties를 사용하여 객체의 속성과 타입을 선언하고, 런타임에===(triple equals) 연산자를 통해 타입 검사를 수행합니다. -
===의 유연성:Proc,Range,Regexp,Class등 다양한 객체에 대해 유연하게 동작하므로, 짝수, 양수, 특정 범위, 이메일 형식 등 복잡한 유효성 규칙을 타입처럼 정의할 수 있습니다. -
장점: 모든 클래스를 ‘경계’로 활용하여 유연하고 강력한 런타임 타입 검증을 구현하며, Ruby의 동적 특성을 유지하면서도 안정성을 확보합니다.