공부

[Spring] POJO란? Hibernate와 Spring이 왜 POJO 친화적인가

SunYerim 2025. 4. 3. 17:05

POJO (Plain Old Java Object)

”오래된 방식의 간단한 자바 객체”

  • Getter, Setter와 같은 기본 기능만을 갖는 기본 객체를 의미.
  • 특정 기술에 종속되어 있는 상태로 개발하지 않는 개념을 위해 등장한 언어.
  • 종속성으로 인해 시스템 업그레이드, 모듈 교체와 같은 상황에서 많은 불편함이 있었기에 POJO라는 개념이 등장함.

(여기서 특정 기술이라함은 framework를 의미)

Spring은 POJO 개념을 잘 지킨 프레임워크이다.

  • IoC, DI, AOP와 같은 개념들이 모두 결합력을 느슨하게 하여 의존성을 낮춤으로써 종속성이 낮아지는 현상이 발생.
  • POJO에 가까운 개발이 가능하다!!
  • 특정 기술들이 종속성을 가지게 되면, 객체지향적인 설계가 힘들거나 불가능한 경우가 발생. 그렇다보니 코드의 유지보수와 재사용이 매우 불편해짐.

Hibernate도 POJO 개념을 잘 지킨 프레임워크이다!

  • Hibernate는 자바 객체의 순수성을 해치지 않으면서도 DB 매핑을 자동으로 처리해주는, POJO 친화적 ORM 프레임워크.

  • Hibernate는 매핑할 엔티티 클래스를 아래와 같이 순수한 POJO로 유지할 수 있도록 설계되어 있음.

      @Entity
      public class User {
              @Id
              private Long id;
              private String name;
              private int age;
    
              // 기본 생성자, getter, setter
      }
  • 위의 코드는 다음과 같은 집에서 POJO의 개념을 지킴.

    • 어떤 ORM 전용 클래스도 상속하지 않음.
    • 어떤 특수한 인터페이스도 구현하지 않음.
    • 필드와 메서드는 일반적인 자바 객체 방식으로 정의
    • 필요한 것은 @Enitity, @Id 같은 JPA 표준 애노테이션뿐.
  • 즉, Hibernate는 엔티티 클래스를 특정 기술에 종속시키지 않고 자바 객체 그대로 사용할 수 있게 해줌. → Hibernate가 POJO 친화적인 이유

  • POJO를 지킨 코드

      public class POJOClass {
              private String name;
              private int age;
    
              public String getName() {
                      return name;
              }
    
              public String setName(String name) {
                      this.name = name;
              }
    
              public int getAge() {
                      return age;
              }
    
              public int setAge(int age) {
                      this.age = age;
              }
      }
    • 단순한 Getter, Setter로 POJO의 개념을 매우 잘 지킨 것을 확인할 수 있음.
  • POJO를 지키지 않은 코드

      public class POJOClass extends UserService {
              private String name;
              private int age;
    
              @Override
              public List<User> findUsers() {
                      return super.findUsers();
              }
      }
    • UserService의 기능을 사용하기 위해 extends를 받았다.
    • 이렇게 되면 UserService의 메서드를 사용하기 위해 많은 양의 코드 리팩토링은 물론 코드의 가독성 및 유지보수 확장 측면에서 어려움이 생길 수 있음.
  • Spring의 POJO 개념을 지킨 코드

      public class POJOClass {
    
              @Autowired
              UserRepository userRepository;
    
              private String name;
              private int age;
    
              public void test() {
                      userRepository.findAll();
              }
      }
    • @Autowired를 활용하여 의존성 주입
    • 느슨한 결합력을 갖게 되면서 직접적으로 extends, implements를 하지 않아도 해당 클래스의 메서드에 접근이 가능해짐.
    • extends를 하여 직접 메서드를 override하여 작성하지 않아도 되면서 userRepository 코드에 변경이 생겨도 같이 변하는 장점이 있다.

POJO 조건

  • 객체지향적인 설계를 하였는가?

  • 특정 기술에 종속되어 있지 않은가?

  • 특정 환경에 종속되어 있지 않은가?

  • 테스트가 간편하고 용이한가?

  • 비즈니스 로직에 단순 자바 오브젝트가 아닌 HttpSession과 같은 특정 기술이나 특정 환경에 종속적이지 않고 객체지향 설계가 적용되어야 POJO라고 할 수 있음.

  • 애노테이션을 사용하는 경우, 부가적인 정보를 담고 있고 그 때문에 특정 환경에 종속적이지 않다면 POJO라 할 수 있음.

  • 하지만, 애노테이션이나 엘리먼트에 특정 기술과 환경에 종속적인 정보를 담고 있으면 POJO가 아님.

POJO의 장점

  • POJO의 조건인 특정 기술과 환경에 종속적이지 않는 코드는 깔끔한 코드가 될 수 있음.
  • 특정 기술 및 환경의 제약은 테스트 코드 작성이 힘들기 때문에 POJO로 개발된 코드는 자동화된 테스트에 매우 유리함.
  • POJO의 코드는 매우 유연한 방식으로 원하는 레벨에서 코드를 빠르고 명확하게 테스트할 수 있음.
  • POJO는 객체지향적인 설계를 자유롭게 적용할 수 있음.
  • 재활용 가능한 설계 모델인 디자인 패턴 등은 POJO가 아니고 적용하기 힘듦.

그렇다면, 이제 대략적으로 POJO가 무엇인지 살펴봤으니 이를 구성하고있는 각각에 대하여 더 자세하게 알아보도록 하자.

AOP란?

  • AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)
  • 프로그래밍을 할 때 핵심 관점과 부가 관점을 나눠서 개발하는 것
  • 프로그래밍에 대한 관심을 핵심 관점, 부가 관점으로 나눠 관심 기준으로 모듈화하는 것을 의미한다.

예시 - 계좌 이체, 고객 관리하는 프로그램

  • 각 프로그램에는 로깅 로직, 즉 지금까지 벌어진 일을 기록하기 위한 로직 + 여러 데이터를 관리하기 위한 db 연결 로직이 포함됨.
  • 여러 기능중에서도 관점을 나눌 수 있음.
    • 핵심 관점: 소프트웨어 시스템의 주요 기능이나 핵심 비즈니스 로직과 관련된 것. ⇒ 계좌 이체, 고객 관리 로직
    • 부가 관점: 핵심 관점 이외의 기능이나 시스템 전반에 걸쳐 중복되거나 공통적으로 발생하는 것. ⇒ 로깅, db 연결 로직
  • 로깅, db 연결은 모두 계좌 이체와 고객 관리에 필요한데, 여기에 AOP를 적용하면 부가 관점에 해당하는 로직을 모듈화해서 개발이 가능하다.
  • 부가 관점 코드를 핵심 관점 코드에서 분리할 수 있게 해준다.
  • 이때문에 개발자는 핵심 관점 코드에만 집중할 수 있게 될 뿐만 아니라 프로그램의 변경과 확장에도 유연하게 대응이 가능하다.

IOC란?

  • IoC(Inversion of Control, 제어의 역전)
  • 객체의 생성과 관리를 개발자가 하는 것이 아니라 프레임워크가 대신하는 것
  • 다른 객체를 직접 생성하거나 제어하는 것이 아니라, 외부에서 관리하는 객체를 가져와 사용하는 것.
// 클래스 A에서 클래스 B 객체 생성 예
public class A {
    b = new B();
}

위의 코드에 제어의 역전을 적용하면? 아래와 같이 된다.

// 스프링 컨테이너가 객체를 관리하는 방식 예 
public class A {
    private B b;
}
  • 클래스 B객체를 직접 생성하는 것이 아니므로, 어딘가에서 받아와서 사용하고 있다고 추측가능.
  • 이처럼 제어의 역전을 적용하면 객체를 외부에서 관리하게 되고, 실제로 사용할 때는 외부에서 제공해주는 객체를 받아오게 됨. 스프링에서 제어의 역전 개념은 중요하며, 객체를 관리하고 관리하는 주체를 스프링 컨테이너라고 함.

DI란?

  • DI(Dependency Injection, 의존성 주입)
  • 외부에서 객체를 주입받아 사용하는 것
  • 스프리에서는 객체들을 관리하기 위해 제어의 역전을 사용함.
  • 이때, 제어의 역전을 구현하기 위해 사용하는 방법이 DI!!!
  • DI는 어떤 클래스가 다른 클래스에 의존한다는 뜻임.
// 객체를 주입받는 모습 예시
public class A {
    // A에서 B를 주입받음
    @Autowired
    B b;
}
  • 이때, @Autowired라는 애노테이션은 스프링 컨테이너에 있는 빈이라는 것을 주입하는 역할.
  • 빈이란? 스프링 컨테이너에서 관리하는 객체
  • 이전에는 직접 B객체를 생성했지만, 위의 코드에서는 B b;라고 선언만 했을 뿐 직접 개체는 생성하고 있지는 않다. 즉, 객체를 주입받고 있다.
  • 스프링 컨테이너라는 곳에서 객체를 주입했기 때문에 프로그램은 잘 동작하게 된다. 즉, 스프링 컨테이너가 B 객체를 만들어서 클래스 A에 준 것이다.

PSA란?

  • PSA(Portable Service Abstraction, 이식 가능한 서비스 추상화)
  • 어느 기술을 사용하던 일관된 방식으로 처리하도록 하는 것
  • 스프링에서 제공하는 다양한 기술들을 추상화해 개발자가 쉽게 사용하는 인터페이스.

예시

  • 스프링에서 db에 접근하기 위한 기술로는 JPA, MyBatis, JDBC 같은 것들이 있음.
  • 여기에서 어떤 기술을 사용하든 일관된 방식으로 db에 접근하도록 인터페이스를 지원함.
  • WAS도 PSA의 하나인데, 코드는 그대로 두고 WAS를 톰캣이 아닌 언더토우나 Netty와 같은 다른 곳에서 실행해도 기존 코드를 그대로 사용가능하기 때문.

빈과 스프링 컨테이너

  • 스프링은 스프링 컨테이너를 제공한다.
  • 스프링 컨테이너는 빈을 생성하고 관리한다.
  • 즉, 빈이 생성되고 소멸되기까지의 생명주기를 스프링 컨테이너가 관리한다.
  • @Autowired 같은 애노테이션을 사용해 빈을 주입받을 수 있게 DI를 지원하기도 한다.

여기서 빈이란?

  • 스프링 컨테이너가 생성하고 관리하는 객체
// 객체를 주입받는 모습 예시
public class A {
    // A에서 B를 주입받음
    @Autowired
    B b;
}
  • 위의 코드에서 @Autowired로 주입받은 B 객체가 바로 빈이다.
  • 스프링은 빈을 스프링 컨테이너에 등록하기 위해 XML 파일에서 설정하거나, 애노테이션으로 등록하는 등의 여러 방법을 제공한다.
// 클래스를 빈으로 등록하는 방법 예시
@Component
public class MyBean {
}
  • 여기서는 @Component 애노테이션을 붙였기에 MyBean 클래스가 빈으로 등록된다.
  • 이후에는 스프링 컨테이너에서 이 클래스를 관리하게된다.
  • 이때 빈의 이름은 클래스 이름의 첫 글자를 소문자로 바꿔 관리한다. (myBean)

참고자료

토비의 스프링 - 8.3 POJO 프로그래밍

'공부' 카테고리의 다른 글

[Spring] 순환참조문제  (0) 2025.04.04
[Spring] spring의 의존성 주입 방법 4가지  (0) 2025.04.04
[DDD] Aggregate란?  (0) 2025.04.01
[JPA] JPA의 N + 1 문제 정리  (0) 2025.03.27
[JPA] FetchType 정리  (0) 2025.03.27