4.2 KiB
4.2 KiB
Introduce Null Object (널 객체 도입하기)
📌 Brief Summary
Introduce Null Object(널 객체 도입하기)는 코드 내에 반복적으로 등장하는 널(Null) 값 검사 로직을 제거하기 위해, 널 값을 대체할 수 있는 '널 객체(Null Object)'를 도입하는 리팩토링 기법이다 [1, 2]. 이 기법은 객체 지향 프로그래밍의 다형성(Polymorphism)을 활용하여 객체가 자신의 타입에 맞는 적절한 동작을 스스로 수행하게 함으로써 조건부 로직을 단순화한다 [2, 3]. 널 객체는 실제 객체와 동일한 인터페이스에 응답하여, 클라이언트가 널 값 여부를 직접 확인하지 않고도 기본 동작을 일관되게 처리할 수 있도록 돕는다 [2, 4].
📖 Core Content
- 다형성을 통한 조건문 제거: 시스템 전반에서 특정 객체가 존재하는지 검사한 후 메서드를 호출하는 중복 코드(
if (foo == null))가 많을 때 유용하다 [2]. 조건문을 다형성으로 대체하여 절차적 코드를 크게 줄이고, 객체의 행위를 단일한 위치에 집중시킬 수 있다 [5, 6]. - 구현 방법 (Mechanics):
- 원본 클래스의 하위 클래스(Subclass)로 널 객체 클래스를 생성하거나 테스트 인터페이스를 활용한다 [7, 8].
- 기존 클래스와 널 클래스에
isNull연산을 추가하여 원본은false를, 널 클래스는true를 반환하도록 한다 [7, 9]. - 기존에 널을 반환하던 코드가 널 객체를 반환하도록 수정하고, 기존의
foo == null형태의 비교문을foo.isNull()로 교체한다 [7, 10-12].
- 주요 활용 사례:
- 화면 표시(Display) 로직에서 수많은 인스턴스 변수의 널 여부를 확인해야 하는 복잡성을 제거할 때 사용된다 [5].
- 테스트 환경에서 실제 데이터베이스를 사용하지 않기 위해 가짜(Missing) 데이터베이스 세션 객체로 사용된다 [13].
- 급여 계산 시 비어있는 데이터 집합을 빈 컨테이너로 제공하여 0원으로 일괄 처리하는 등 중복된 객체 생성과 로직을 피하기 위해 활용된다 [13].
- 싱글톤(Singleton) 패턴 적용: 널 객체는 일반적으로 내부 상태가 변하지 않는 상수(Constant)와 같은 성격을 띠므로, 싱글톤 패턴을 이용해 단일 인스턴스로 구현하는 것이 권장된다 [4].
- 특수 사례(Special Case) 패턴으로의 확장: 널 객체 패턴은 더 큰 개념인 '특수 사례(Special Case)' 패턴의 일환이다 [14]. 단순히 비어있다는 것을 넘어 "알 수 없는 고객"과 "아직 입주하지 않은 새 건물(고객 없음)"을 구분하기 위해 여러 종류의 널 클래스를 별도로 구현하거나, 나중에 사용할 수 있도록 데이터를 임시로 담아두는 널 객체를 만들 수도 있다 [14, 15].
⚖️ Trade-offs & Caveats
- 오류 발견의 어려움: 널 객체는 실제 객체와 동일한 메시지에 정상적으로 응답하기 때문에, 널 객체가 시스템 내의 잘못된 위치에 존재하더라도 프로그램이 중단(blow up)되거나 예외를 던지지 않는다 [4]. 시스템이 겉으로는 정상적으로 동작하는 것처럼 보여, 실제로 버그가 발생했을 때 즉각적으로 발견하거나 원인을 추적하기 어려워질 수 있다 [4].
- 제한적인 적용 조건: 널 객체로 로직을 이동시키는 것은 '대부분의 클라이언트가 널 상황에 대해 동일한 기본 응답(동작)을 원할 때'에만 이점을 얻는다 [16]. 클라이언트마다 널 상황에서 수행해야 하는 동작이 제각각 다르다면 널 객체의 기본 동작에만 의존할 수 없으므로, 여전히
isNull검사를 통해 개별적인 분기 처리를 해야 한다 [16]. - 초기 적용의 번거로움과 복잡성: 널 객체를 도입하는 과정은 매우 까다로울 수 있다. 널을 반환하는 모든 코드 출처와 이를 검사하는 조건문을 샅샅이 찾아내 수정해야 한다 [17]. 만약 객체가 시스템의 여러 곳으로 광범위하게 전달되고 있다면, 이를 추적하고 변경하는 것을 안전한 작은 단계로 나누어 리팩토링하기 어려울 수 있다 [17].
Last updated: 2026-05-03