일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- ORM
- 컨테이너
- 스프링 시큐리티
- 자바
- vm
- 데이터베이스
- Spring
- CI/CD
- 스프링 부트
- Java
- 웹 서버
- HTTP
- mysql
- 가상화
- web server
- JPA
- 스프링
- virtualization
- 도커
- Container
- spring cloud
- spring batch
- spring boot
- 백엔드
- Spring Security
- 배포
- CS
- 스프링 배치
- 영속성 컨텍스트
- computer science
- Today
- Total
개발 일기
[Spring Boot] Spring 3대 특징(Spring 삼각형) 1 - POJO(Plain Old Java Object) 본문
[Spring Boot] Spring 3대 특징(Spring 삼각형) 1 - POJO(Plain Old Java Object)
개발 일기장 주인 2024. 3. 9. 11:04이전 게시글에서 스프링 생태계에 대해서 알아봤는데 Spring Framework의 3대 특징인 Spring 삼각형에 대해서 공부해보고자 한다.
Spring 삼각형
위 사진이 바로 유명한 Spring 삼각형이다. Spring의 핵심 특징인 3대 특징이다.
POJO는 IoC/DI, AOP, PSA를 통해서 달성할 수 있다는 것을 의미한다.
이제 하나씩 알아보자
POJO(Plain Old Java Object) 기반의 구성
우선 POJO부터 알아보자. POJO는 이전 게시글 스프링의 등장 배경에서 마틴 파울러는 EJB에 오래된 방식의 "오래된 방식의 간단한 자바 오브젝트로 돌아가자"라는 말이 기원이라고 했었다. 조금 더 자세한 일화를 봤는데 마틴 파울러가 2000년에 콘퍼런스 발표를 준비하다가 만들어낸 용어인데, 단순히 발표 중의 "간단한 자바 오브젝트를 사용하는데요~"라고 하는 것보다 "POJO 방식의 기술을 사용하는데요~"라고 하면 왠지 세련되고 첨단 기술을 쓰는 것처럼 느껴진다는 심리를 이용하여 만들어진 것이라고 한다.
POJO는 Plain Old Java Object로, 일반적으로 자바로 개발할때 사용하는 평범한(순수한) 자바 객체를 의미한다.
이를 해석하면 JLS(Java Language Specification)에 의해 강제된 것 이외의 제한에 의존하지 않는 자바 오브젝트를 부르는 용어로 객체 지향적인 원리에 충실하면서 환경과 기술에 종속적이지 않고, 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 의미한다. 이러한 POJO에 애플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 한다.
그런데 이때 순수한 자바 객체를 사용한다고 해서 무조건 POJO 프로그래밍으로는 볼 수 없다.
POJO 프로그래밍 규칙
1. 특정 기술/규약에 종속적이지 않아야 함
(Java나 Java의 스펙에 의해 정의된 것 이외에는 다른 기술이나 규약에 얽매이지 않아야 함)
public class Pet {
// Pet 객체의 속성(필드)
private String name; // 이름
private String type; // 종류 (예: 고양이, 개 등)
// 생성자
public Pet(String name, int age, String type) {
this.name = name;
this.age = age;
this.type = type;
}
// name 필드에 대한 getter
public String getName() {
return name;
}
// name 필드에 대한 setter
public void setName(String name) {
this.name = name;
}
// type 필드에 대한 getter
public String getType() {
return type;
}
// type 필드에 대한 setter
public void setType(String type) {
this.type = type;
}
}
위의 코드는 해당 클래스는 Java 자체 이외의 특정한 기술에 종속되어 있지 않고 자바 자체에서 제공하는 getter, setter를 사용한 순수한 객체이기 때문에 POJO이라고 볼 수 있다.
그러나 아래의 코드는 이에 반하는 예시로 볼 수 있다. 어느 블로그에서 참고한 코드이다.
public class MessageForm extends ActionForm {
String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class MessageAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
MessageForm messageForm = (MessageForm) form;
messageForm .setMessage("Hello World");
return mapping.findForward("success");
}
}
ActionForm 클래스는 과거에 Struts라는 웹 프레임워크에서 지원하는 클래스였는데 MessageForm 클래스는 Struts라는 기술을 사용하기 위해 ActionForm 클래스를 상속받고 있다. 또한, MessageAction 클래스에서 역시 Struts 기술의 Action 클래스를 상속받고 있다.
또 다른 POJO 개념을 사용하지 않은 예시로는 JMS 라는 기능을 사용하기 위해 MessageListener 인터페이스를 구현한 예시이다.
// MessageListener 인터페이스를 직접 구현하므로 POJO가 아님
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
이렇게 POJO 기반의 코드를 작성하지 않게 되면 이후 JMS와 기능을 비슷하지만 다른 솔루션을 사용하고자 할 때 교체하기가 굉장히 힘들어지게 된다.
아래 코드가 POJO를 지킨 코드이다.
// 어노테이션으로 MessageListener 인터페이스와의 결합도를 낮춘 POJO
@Component
public class ExampleListener {
@JmsListener(destination = "myDestination")
public void processOrder(String message) {
System.out.println(message);
}
}
위 예제는 JMS라는 기능을 위해 JmsListner를 상속받는 방식이 아니라 어노테이션을 통해 객체를 주입받은 상황이다. 이런식으로 코드를 작성하게 되면 해당 클래스와의 결합도가 낮아져 다른 솔루션으로 변경하고 싶은 경우에 @JmsListener를 @(다른 솔루션)으로 코드를 수정만 하면 가능하므로 유지 보수에 있어 좀 더 유용하게 활용할 수 있다.
이러한 방식으로 기술을 상속받아 코드를 작성하게 되면, 클래스 간의 결합도가 높아져
애플리케이션의 요구사항이 변경되어 다른 기술로 변경해야 할 때 기존에 상속한 클래스를 명시적으로 사용했던 부분들을 모두 제거하여 수정해야 한다. 또한, Java는 다중 상속을 지원하지 않기 때문에 상위 클래스를 상속받아 하위 클래스를 확장하는 객체지향 설계 기법을 적용하기 어려워진다고 한다.
2. 특정 환경에 종속적이지 않아야 함
특정 환경에 종속적이지 않고 독립적이어야 한다는 것은 특정한 프레임워크에서만 동작이 가능하면 안된다는 의미 한다.
1)
첫번째로 비즈니스 로직을 담고 있는 POJO 클래스는 웹 기반의 환경 정보나 웹 기술을 담고 있는 클래스 또는 인터페이스를 사용하면 안 된다.
웹 컨트롤러와 연결하여 사용하더라도, 직접적으로 사용 환경을 웹으로 제한하거나 웹에서만 동작하는 API를 직접 사용하지 말아야 한다. 이러한 이유는 웹 이외의 클라이언트는 해당 기능을 요청할 수 없기 때문이다.
따라서 웹 환경에 종속되는 HttpServletRequest나 HttpSession와 관련된 API를 직접 이용해서는 안된다.
아래 코드는 웹 환경에 종속되는 HttpServlet을 확장하여 활용한 예시이다.
public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userId = request.getParameter("userId");
// 직접 비즈니스 로직을 처리
if (userId == null || userId.isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
이들을 사용하는 코드는 웹 환경에 종속되어 다른 유형의 애플리케이션(예: 콘솔 애플리케이션, 데스크톱 애플리케이션)에서는 재사용이 어렵다. 그래서 만약 비즈니스 로직이 이러한 API와 밀접하게 연결되어 있다면, 로직을 다른 환경으로 옮기거나 재사용하기 위해 상당한 수정이 필요하다.
2)
또 다른 예시로는 특정 서블릿 컨테이너의 API에 직접적으로 의존했다가 이 서블릿 컨테이너를 요구 사항 등 필요에 의해 변경해야 하는 경우이다. 아파치 톰캣(Apache Tomcat)에서 제공하는 특정 API를 사용하여 세션 관리를 구현한 코드가 있다고 가정해 보자. 아래 코드는 톰캣의 세션 관리 기능을 활용하여 사용자의 세션 정보를 관리한다.
// Tomcat 기반의 세션 관리 예시
import org.apache.catalina.session.StandardSessionFacade;
public class SessionManager {
public void manageSession(HttpServletRequest request) {
HttpSession session = request.getSession();
StandardSessionFacade tomcatSession = (StandardSessionFacade) session;
// Tomcat의 세션 관리 API를 사용하여 세션 관리 로직 구현
// 예: 세션 타임아웃 설정, 세션 데이터 저장 및 조회 등
}
}
위 코드는 톰캣의 StandardSessionFacade 클래스를 직접 사용하여 세션 관리를 수행하고 있습니다. 이는 톰캣에 특화된 방식이며, Tomcat이 아닌 다른 서블릿 컨테이너에서는 이 API를 지원하지 않을 수 있습니다.
이제 시스템의 요구 사항이 변경되어 톰캣 대신 제티(Jetty)를 사용하게 되었다고 가정해 봅시다. 제티는 톰캣과는 다른 API를 제공하므로, 기존에 톰캣 API에 의존적이었던 코드를 제티가 지원하는 방식으로 아래처럼 전부 수정해야 한다.
// Jetty로 변경 후 세션 관리 예시
import org.eclipse.jetty.server.session.Session;
public class SessionManager {
public void manageSession(HttpServletRequest request) {
HttpSession session = request.getSession();
Session jettySession = (Session) session;
// Jetty의 세션 관리 API를 사용하여 세션 관리 로직 구현
// 예: 세션 타임아웃 설정, 세션 데이터 저장 및 조회 등
}
}
이처럼 특정 서블릿 컨테이너의 API에 직접적으로 의존하는 코드는 서블릿 컨테이너를 변경할 때 큰 재작업이 필요할 수 있다. 이러한 문제를 방지하기 위해, 가능한 한 표준 Java EE API를 사용하거나, 서블릿 컨테이너에 독립적인 방식으로 애플리케이션을 설계하는 것이 좋다. 이렇게 하면 서블릿 컨테이너를 변경하더라도 애플리케이션 코드의 수정을 최소화할 수 있다. 이러한 이유 때문에 특정 환경에 종속적이지 않아야 한다는 것이다.
정리해보면, JLS 이외의 특정 규약에 얽매이지 않으며, 특정 환경에 종속적이지 다른 않아야 POJO 프로그래밍이라고 할 수 있다.
잘못된 정보가 있다면 피드백 부탁드립니다!!
'Back-End > Spring' 카테고리의 다른 글
[Spring Boot] Spring MVC - 구조 이해 (0) | 2024.05.02 |
---|---|
[Spring Boot] Spring 3대 특징(Spring 삼각형) 3 - 관점 지향 프로그래밍(AOP) (0) | 2024.03.13 |
[Spring Boot] Spring 3대 특징(Spring 삼각형) 2 - 의존성 주입(DI)과 제어의 역전(IoC) (1) | 2024.03.13 |
[Spring Boot] Spring 생태계와 본질 (1) | 2024.03.08 |
[Spring Boot] Spring의 등장 배경 (0) | 2024.03.07 |