피드로 돌아가기
원문 읽기
LINE Engineering
Backend코드 품질 개선 기법 21편: 생성자를 두드려 보고 건너라
LY Corporation의 Review Committee가 미준비 상태의 객체 사용으로 인한 런타임 에러를 타입 시스템과 팩토리 패턴으로 컴파일 타임에 방지하는 3가지 기법 제시
AI 요약
Context
FooVideoPlayer 클래스는 play() 호출 전에 prepare()를 반드시 호출해야 하는데, 이를 강제할 방법이 없어 preparedValue가 null인 상태에서 play()가 호출되면 IllegalStateException이 발생한다. 클라이언트가 두 메서드의 호출 순서를 주석에만 의존하면 버그가 발생하기 쉽다.
Technical Solution
- 옵션 1: 초기화 시점 prepare 실행 (팩토리 함수 사용) - 생성자를 private으로 변경하고 companion object 내 createInstance() 팩토리 함수에서 prepare 로직을 실행 후 완전히 초기화된 인스턴스만 반환
- 옵션 2: 지연 실행 (Lazy 위임) - play() 메서드 내에서 preparedValue를 처음 접근할 때만 prepare 로직을 실행하는 lazy 패턴 적용으로 생성 비용을 play 호출 시점으로 미룸
- 옵션 3: 타입 분리 (두 단계 상태 모델) - PreparedFooVideoPlayer라는 별도의 클래스를 정의하고 prepare() 메서드가 FooVideoPlayer에서 PreparedFooVideoPlayer로 반환하도록 변경하여 play()는 준비된 상태의 클래스에만 정의
Key Takeaway
메서드 호출 순서를 주석으로 강제하는 대신 타입 시스템을 활용해 잘못된 사용 자체를 컴파일 타임에 불가능하게 만들어야 한다. 초기화 순서가 중요한 클래스는 생성자 내 복잡한 로직보다 팩토리 함수나 상태별 타입 분리를 통해 안전성을 확보할 수 있다.
실천 포인트
Kotlin/Java에서 상태 의존성이 있는 API 설계 시, prepare-then-use 패턴을 요구하는 대신 (1) 팩토리 함수로 fully initialized 인스턴스만 반환하거나 (2) lazy 위임으로 자동 초기화하거나 (3) Unprepared/Prepared 상태를 별도 타입으로 분리하면 클라이언트의 런타임 에러를 컴파일 단계에서 차단할 수 있다.