Tag Archives: POJO

POJO (Plain Old Java Object)

이미 자바 개발의 세계에선 POJO가 당연하게 느껴질 정도로 기본적인 프로그래밍 모델이 됐음에도, 아직도 주위에서 종종 POJO의 의미와 중요성을 강조하는 이야기를 듣곤 한다. 그리고 POJO라는 단어의 개념에 대한 이해가 서로 조금씩 다를 모습을 발견하는 순간이 있다. 예를 들어 최근에 스프링 프레임워크에 의존해 개발해 온 동료가 POJO를 스프링 만의 특징인 것처럼 설명하며 어노테이션 기반의 타입 정의는 잘못됐다고 주장하는 상황이 있었다. 어려울 것 없고 당연하게 사용되는 단어이기 때문에 이해가 잘못될 수 있는 여지가 오히려 더 높나보다는 생각을 갖게 됐는데, 이 참에 제대로 개념을 정리해두고자 한다.

POJO의 개념을 정의하기에 앞서, 먼저 POJO가 아닌 객체의 의미를 살펴보자. 자바나 C++와 같은 객체 지향 언어는 보통 인터페이스나 상속의 개념을 정의하고 있다. 이는 디자인 결정 사항을 분리해 숨기고 추상적 오퍼레이션만을 드러냄으로써 모듈화라는 소프트웨어 공학의 기본 원칙을 실현하는 도구가 된다. 하지만 이런 언어적 장치에 숨어있는 맹점은 런타임 객체의 타입에 모호성과 의존성을 추가하게 된다는 점이다.

어떤 인터페이스의 콘크리트 클래스를 정의하기 위해서는 인터페이스로 정의된 모든 오퍼래이션이 구현돼야만 한다. 이를 통해 콘크리트 클래스는 인터페이스의 타입을 자신의 타입 안에 포함할 수 있다. 결국 콘크리트 클래스는 해당 클래스의 타입 안에 인터페이스의 타입을 완전히 포함하는 타입의 중복이 발생한다. 인터페이스 타입의 정보를 모두 재정의 해 사용한다면 그나마 나은 상황이겠지만, 인터페이스의 타입 정보 중 일부만을 사용하고자 한다면 불필요한 타입 정보의 낭비가 발생하고, 그에 따른 불편한 제약 요소는 결국 프로그래머가 떠안게 된다.

상속의 경우는 이런 상황이 더욱 심화된다. 앞서 언급한 인터페이스와 콘크리트 클래스 간의 관계 뿐만 아니라, 상속은 클래스의 내부에 존재하는 상태나 디자인 선택 사항도 함께 전달되기 때문에 객체의 런타임 행태가 부모 클래스의 속성에 의존적이게 된다. 경우에 따라서는 오버라이딩을 통해 자식 클래스가 부모의 오퍼래이션을 재정의 할 수도 있다. 자식 클래스로 정의된 타입이 부모 클래스로 표현되는 타입과 단순한 포함 관계를 형성하는 데서 더 나아가, 타입 모호하게 분리된 가운데 복잡한 의존성을 형성할 수 있는 위험을 내포하게 된다.

결국 POJO라는 객체는 이러한 타입 간 중복 및 불일치성을 제거하고, 자신의 타입이 다른 타입에 의존하지 않는 경우의 객체를 의미한다. 이 때 어노테이션이나 XML로 해당 객체를 마킹하는 행위는 인터페이스나 상속과는 달리 실제로 객체 타입의 속성을 변경하지는 않으며, 단지 객체의 표면에 메타데이터를 덧붙이는 역할만을 한다. 즉, 이런 메타데이터는 타입 자체를 확장하거나 변경하지 않으며 오직 다른 타입이 해당 객체를 참조할 때만 사용될 수 있다. 어노테이션이나 XML은 메타데이터을 나타내는 형식이 다를 뿐이고, 두 방식의 역할은 서로 완전히 동일하다. 이러한 이유로 POJO는 불필요한 복잡성을 제거해 프로그래머가 유연하고 효율적인 프로그래밍을 수행할 수 있도록 돕는 유용한 방식이 될 수 있다.