#Backend

코드 품질 개선 기법 28편: 제약 조건에도 상속세가 발생한다

코드 품질 개선 기법 28편: 제약 조건에도 상속세가 발생한다
01

Summary

"Immutable 이름만 붙이면 끝?" 당신의 코드가 '상속세'를 내고 있는 이유

객체 지향의 함정, 불변성을 무너뜨리는 잘못된 상속 설계와 그 대안을 파헤칩니다.

단순히 가변 리스트를 래핑하는 것만으로는 완벽한 불변성을 보장할 수 없습니다. 이 아티클은 코틀린의 상속 구조에서 발생할 수 있는 불변성 제약의 허점을 지적하며, 안전한 시스템 구축을 위한 가변/불변 객체의 올바른 계층 설계 원칙을 제시합니다.

  • 01ImmutableIntList 예제를 통한 상속 기반 불변성 파괴 시나리오 분석
  • 02Kotlin IntArray 활용 시 박싱(Boxing) 비용 절감과 가변성 트레이드오프 설명
  • 03가변 객체와 불변 객체가 서로를 상속할 때 발생하는 런타임 에러 위험성 경고
  • 04읽기 전용(Read-only) 인터페이스를 활용한 이상적인 객체 위계 구조 제안

RECOMMENDATION

도메인 모델의 무결성이 중요한 대규모 시스템 설계자에게 추천하며, 클래스 정의 시 'open' 키워드를 남발하기보다 'Composition'이나 인터페이스 기반 설계를 우선시할 것을 권장합니다.

The Problem

클래스에 'Immutable'이라는 명칭을 붙여 불변성을 의도하더라도 클래스를 상속 가능(open)하게 설계하면, 자식 클래스에서 내부 필드에 접근하거나 메서드를 오버라이드하여 객체의 가변성을 허용할 수 있는 보안 허점이 발생한다.

The Solution

불변성을 보장해야 하는 클래스는 기본적으로 상속을 금지(final)하고, 가변 객체와 불변 객체 간의 공통 분모가 필요한 경우에는 오직 조회 기능만 포함된 '읽기 전용(Read-only)' 인터페이스를 부모로 두는 구조로 개선해야 한다.

The Result

상속으로 인한 불변성 훼손 가능성을 근본적으로 차단함으로써 코드의 예측 가능성을 높이고, 런타임에 발생할 수 있는 예기치 못한 상태 변경 버그를 방지할 수 있다.

Trade-off

클래스 확장이 제한되어 상속을 통한 코드 재사용성이 낮아질 수 있으며, 읽기 전용 계층을 추가하는 과정에서 초기 설계 비용과 클래스 구조의 복잡도가 다소 증가할 수 있다.

03

Key Concepts

Concept · 01

Immutability (불변성)

객체 생성 후 내부 상태가 변하지 않는 특성으로, 멀티스레드 환경에서 안전하고 코드의 사이드 이펙트를 최소화하는 핵심 원칙이다.

  • 본문에서는 ImmutableIntList를 통해 명칭과 실제 제약 조건의 일치 여부를 강조함
  • 상속이 불변성이라는 제약 조건을 어떻게 약화시키는지 주요 논지로 다룸
Concept · 02

Open Class (상속 가능 클래스)

Kotlin에서 하위 클래스가 해당 클래스를 상속받아 기능을 확장하거나 메서드를 재정의할 수 있도록 허용하는 키워드이다.

  • 불변 객체를 open으로 설정했을 때 발생하는 설계적 결함을 분석함
  • 자식 클래스에서 부모의 내부 배열(valueArray)을 조작하는 사례를 통해 위험성 증명
Concept · 03

Read-only vs Immutable

수정 메서드가 없는 '읽기 전용' 상태와 상태 변경 자체가 불가능한 '불변' 상태를 구분하는 설계 개념이다.

  • 가변 객체와 불변 객체의 공통 부모로서 '읽기 전용' 계층의 필요성을 설명함
  • Kotlin의 List(읽기 전용)와 MutableList(가변)의 관계를 예시로 활용