- POJO (Plain Old Java Object)
순수 Java 생성 객체 지향
- IoC / DI (Inversion of Control / Dependency Injection)
제어의 역전 / 의존성 주입
- AOP (Aspect Oriented Programming)
관심 지향 프로그래밍
- PSA (Portable Service Abstraction)
일관된 서비스 추상화
https://velog.io/@airoca/Spring-SpringSpring-Boot
앞선 글에서 설명했듯이, POJO는 Spring의 가장 핵심적인 특징으로, Java만을 통해서 생성한 객체를 의미한다. 이번 글에서는 예시와 함께 POJO에 대해 더 구체적으로 알아보고자 한다.
POJO는 흔히 EJB(Enterprise Java Bean)와 비교되어 설명된다. EJB는 1998년에 처음 등장한 J2EE(Java 2 Platform, Enterprise Edition)의 핵심 기술 중 하나이다. 당시 자바 엔터프라이즈 애플리케이션 개발의 표준이었던 EJB의 치명적인 단점은 코드들이 EJB 기술에 지나치게 종속되어야 한다는 것이었다. 아래는 EJB로 "Hello World!"를 출력하는 예시이다.
import java.rmi.RemoteException;
import javax.ejb.*;
public class HelloBean implements SessionBean {
private SessionContext sessionContext;
public void ejbCreate() {
}
public void ejbRemove() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
}
public String sayHello() throws java.rmi.RemoteException {
return "Hello World!";
}
}
위의 예시와 같이, EJB를 사용한 코드는 SessionBean을 상속받으며, ejbCreate, ejbRemove, ejbActivate, ejbPassivate, setSessionContext 등의 메서드를 필수적으로 구현해야 했다. 이처럼 종속성과 결합력이 강한 코드는 재사용성을 떨어뜨리고, 테스트를 복잡하게 만들며, 애플리케이션의 유지보수를 어렵게 한다. 결과적으로 이는 객체지향 설계의 장점들을 잃어버리게 하는데, POJO의 등장은 복잡하고 무겁던 EJB 기술에 대한 반작용으로 볼 수 있다.
import org.apache.commons.lang3.StringUtils; public class Person { private String name; public boolean isNameEmpty() { return StringUtils.isEmpty(name); } }
위의 예시의 경우, Person 클래스는 StringUtils 클래스에 의존하고 있다. POJO는 외부 라이브러리에 종속되지 않아야 하는데, 이 경우 Apache Commons Lang의 StringUtils에 종속되어 있다. 이를 POJO 형태로 바꾸면 아래와 같다.
import org.apache.commons.lang3.StringUtils; public class Person { private String name; public boolean isNameEmpty() { return name == null || name.isEmpty(); } }
위의 Person 클래스는 외부 라이브러리에 종속되지 않고, POJO의 특징을 만족한다.
import javax.servlet.http.HttpServletRequest; public class User { private String username; public User(HttpServletRequest request) { this.username = request.getParameter("username"); } }
위의 예시의 경우, User 클래스는 HttpServletRequest에 의존하고 있다. POJO는 특정 환경에 종속되지 않아야 하는데, 이 경우 웹 환경에 종속되어 있다. 이를 POJO 형태로 바꾸면 아래와 같다.
public class User { private String username; public User(String username) { this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
위의 Person 클래스는 웹 환경에 종속되어 있지 않은, getter와 setter만을 사용한 POJO 형태의 예시이다.
여기까지 글을 읽고, 아래와 같은 의문이 들 수 있다.
"Spring Framework는 POJO를 따른다고 하는데, 실제로는 어노테이션(Annotation)이 많이 사용되잖아?"
답변을 위해, 먼저 ORM, JPA, Hibernate에 대한 간단한 이해가 선행되어야 한다.
ORM (Object-Relational Mapping)
ORM은 객체 지향 프로그래밍 언어에서 사용되는 객체를 관계형 데이터베이스의 테이블과 매핑하는 기술이다.
JPA (Java Persistence API)
JPA는 ORM 기술을 사용하기 위한 표준 인터페이스이다. @Entity, @Id 등의 어노테이션들은 JPA의 주요 어노테이션이다.
Hibernate
Hibernate는 JPA를 구현한 대표적인 ORM 프레임워크이며, 자바 언어를 위한 ORM 프레임워크이다.
Spring에서는 Hibernate를 사용한다. 이는 기술에 종속성이 없어야 한다는 POJO에 부합하지 않는 것처럼 보일 수 있고, 실제로 JPA Annotation의 사용은 엄격한 POJO의 정의에는 위배된다고 한다.
하지만 그럼에도 Spring이 POJO를 이용한다고 볼 수 있는 이유는, ORM 기술을 사용하기 위해 JPA라는 표준 인터페이스를 도입했기 때문이다. JPA는 ORM 프레임워크(Hibernate 등)가 표준 인터페이스 아래에서 안전하게 구현될 수 있도록 한다. 이와 같은 접근 방식이 바로 이전 글에서 다뤄졌던 PSA(Portable Service Abstraction)인 것이다.
복잡하다! 쉽게 풀어서 다시 설명하면, 기술에 종속성이 있는 것이 문제였던 이유는 해당 기술에 변경이 생겼을 때 해당 기술을 사용하는 전체 코드가 수정되어야 했던 점이었다. 하지만 Spring에서는 Hibernate라는 특정 기술을 사용하더라도, JPA라는 표준 인터페이스가 있기 때문에, Hibernate가 아닌 다른 ORM 프레임워크로 변경되더라도 표준 인터페이스인 JPA를 사용한 코드 자체는 변경되지 않아도 된다.
Spring의 중요한 특성인 POJO에 대해 보다 자세하게 알아봤다. 앞으로 알아볼 나머지 Spring의 특징들인 IOC, DI, AOP, PSA 모두 결합도를 낮추고 재사용성을 높이는 방법들로, POJO의 특징을 지향하는 Spring Framework의 노력이라고 볼 수 있다.