POJO(Plain Old Java Object)
: 특정한 프레임워크나 기술에 종속되지 않은 간단하고 순수한 자바 객체
POJO의 주요 특징
- 순수한 자바 객체
- 특정 프레임워크나 라이브러리에 의존하지 않는 자바 클래스.
- 예를 들어, Spring 프레임워크를 사용하더라도 POJO는 Spring 컨테이너와 독립적일 수 있음.
- 비종속성
- 특정 기술, 인터페이스, 상속 계층 등에 종속되지 않음.
- POJO 클래스는 오직 자바 언어만 사용하여 정의.
- 구조의 단순성
- 일반적으로 기본 생성자, 게터/세터 메서드, 간단한 로직으로 구성됨.
- 복잡한 상속, 종속성, 어노테이션 등이 없어야 함.
POJO의 예
아래는 POJO의 전형적인 예제 : POJO 클래스
public class Person {
private String name;
private int age;
// 기본 생성자
public Person() {}
// 매개변수 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
POJO의 활용
- Spring 프레임워크
- Spring은 POJO 기반의 개발을 지향합니다.
- POJO 클래스를 @Component, @Service, @Repository 등의 어노테이션으로 관리 객체로 선언하여 사용.
- Hibernate
- 엔터티(Entity) 클래스로 POJO를 사용하여 데이터베이스 테이블과 매핑.
- Jackson
- JSON 직렬화/역직렬화 작업에서 POJO 클래스를 데이터 모델로 활용.
웹 프로그래밍에서 POJO의 역할
1. 데이터 모델링
- 데이터 전송 객체(DTO)나 VO(Value Object) 형태로 사용됩니다.
- 예: 웹 애플리케이션에서 클라이언트와 서버 간의 JSON 데이터를 매핑할 때 POJO를 자주 사용합니다.
예시
public class UserDTO {
private String username;
private String email;
// Getters and Setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
이처럼, REST API 요청과 응답 데이터를 처리하는 데 POJO를 주로 사용합니다.
2. ORM(Object-Relational Mapping)
- Hibernate나 JPA(Java Persistence API) 같은 ORM 기술에서 데이터베이스 엔터티를 매핑하는 데 POJO를 사용합니다.
예:
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
// Getters and Setters
}
3. Spring 프레임워크와 POJO
- Spring MVC에서 POJO는 컨트롤러, 서비스, 리포지토리 계층에서 널리 사용됩니다.
- 컨트롤러 메서드의 파라미터로 POJO를 받을 수도 있습니다.
@PostMapping("/register")
public String registerUser(@RequestBody UserDTO user) {
// UserDTO를 활용하여 로직 실행
return "User registered";
}
4. JSON 데이터 직렬화/역직렬화
- Jackson, Gson 같은 라이브러리를 사용하여 JSON 데이터를 POJO로 변환하거나 그 반대로 변환합니다.
ObjectMapper mapper = new ObjectMapper();
UserDTO user = mapper.readValue(jsonString, UserDTO.class);
웹 프로그래밍에서 POJO가 덜 사용되는 이유
- 복잡한 비즈니스 로직
- 웹 애플리케이션에서는 프레임워크와의 긴밀한 통합이 필요하기 때문에 POJO만으로 모든 로직을 구현하기 어렵습니다.
- 프레임워크 의존성 증가
- 현대 웹 애플리케이션은 Spring Boot, Django, Node.js 같은 프레임워크에 의존합니다.
- 이런 프레임워크는 POJO 대신 어노테이션 기반 객체나 특정한 인터페이스를 요구할 수 있습니다.
- 템플릿 객체 사용
- 많은 웹 프로그래밍 작업은 이미 정의된 템플릿 객체나 라이브러리를 활용합니다.
그래도 POJO는 중요하다
- 프레임워크 독립성
- POJO는 특정 프레임워크와 상관없이 정의되기 때문에 코드의 재사용성과 유연성을 높입니다.
- 테스트 용이성
- 프레임워크 의존성이 없으므로, 단위 테스트 작성이 쉽습니다.
- 프레임워크 지원
- 현대의 많은 프레임워크(Spring, Hibernate 등)는 POJO와 잘 통합되도록 설계되어 있습니다.
결론
- POJO는 웹 프로그래밍에서 여전히 기본 단위로 많이 사용됩니다.
- 하지만 직접적인 활용보다는 DTO, 엔터티 클래스, JSON 매핑 객체 등으로 변형된 형태로 사용되는 경우가 많습니다.
디자인 패턴
디자인 패턴은 소프트웨어 설계 시 자주 등장하는 문제 상황을 해결하기 위해, 검증된 설계 기법(템플릿)을 정형화한 것
1. 디자인 패턴의 분류
디자인 패턴은 크게 3가지로 분류됩니다:
- 생성(Creational): 객체 생성 과정을 캡슐화하여 코드의 유연성과 재사용성을 높임.
- 예: Singleton, Factory Method, Abstract Factory, Builder, Prototype
- 구조(Structural): 객체 간의 관계를 정의하여 코드 구조를 단순화하고 효율적으로 연결.
- 예: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
- 행위(Behavioral): 객체 간의 통신과 상호작용 방식을 정의하여 동작을 효과적으로 분리.
- 예: Strategy, Observer, Command, Chain of Responsibility, State, Mediator, Visitor
2. 주요 디자인 패턴
아래는 실무에서 자주 사용되는 패턴과 그 활용 예입니다:
Singleton (생성 패턴)
- 목적: 애플리케이션에서 클래스의 인스턴스를 단 하나만 생성.
- 실제 사용: DB 연결, 로그 기록, 설정 관리 등.
- 예제:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Factory Method (생성 패턴)
- 목적: 객체 생성을 서브클래스에 위임하여 클라이언트 코드를 단순화.
- 실제 사용: 다양한 형태의 객체 생성, 예를 들어 UI 요소(Button, Checkbox).
- 예제
public abstract class Shape {
abstract void draw();
}
public class Circle extends Shape {
void draw() { System.out.println("Circle"); }
}
public class ShapeFactory {
public Shape createShape(String type) {
if ("Circle".equalsIgnoreCase(type)) {
return new Circle();
}
return null;
}
}
Strategy (행위 패턴)
- 목적: 알고리즘 군을 정의하고, 런타임에 동적으로 교체 가능.
- 실제 사용: 결제 방식(카드, 페이팔 등) 선택, 데이터 정렬 알고리즘.
- 예제:
public interface PaymentStrategy {
void pay(int amount);
}
public class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card");
}
}
public class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(int amount) {
strategy.pay(amount);
}
}
Observer (행위 패턴)
- 목적: 객체의 상태 변화에 따라 다른 객체들에게 알림.
- 실제 사용: 이벤트 리스너, 실시간 데이터 업데이트(예: 주식 가격, 채팅).
- 예제:
public interface Observer {
void update(String message);
}
public class User implements Observer {
public void update(String message) {
System.out.println("Received: " + message);
}
}
public class NotificationService {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer observer) {
observers.add(observer);
}
public void notifyAll(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
Decorator (구조 패턴)
- 목적: 객체의 기능을 동적으로 추가하거나 변경.
- 실제 사용: 파일 스트림(압축, 암호화), UI 구성 요소.
- 예제:
public interface Coffee {
String getDescription();
double getCost();
}
public class BasicCoffee implements Coffee {
public String getDescription() { return "Basic Coffee"; }
public double getCost() { return 5.0; }
}
public class MilkDecorator implements Coffee {
private Coffee coffee;
public MilkDecorator(Coffee coffee) {
this.coffee = coffee;
}
public String getDescription() { return coffee.getDescription() + ", Milk"; }
public double getCost() { return coffee.getCost() + 1.5; }
}
3. 디자인 패턴 학습 시 주의할 점
- 오용하지 말 것
- 패턴은 해결책이지만, 무조건 적용하면 코드가 복잡해질 수 있음.
- 필요할 때만 적용.
- 문제 먼저 이해하기
- 패턴은 특정 문제를 해결하려는 목적에서 나왔으므로 문제를 명확히 파악.
- 읽기 좋은 코드가 우선
- 지나치게 패턴에 집착하면 유지보수가 어려운 코드가 될 수 있음.
4. 어떻게 실무에서 익히나?
- 코드 리뷰
- 팀 프로젝트에서 디자인 패턴 사용 사례를 배우는 것이 가장 빠름.
- 리팩토링
- 기존 코드를 리팩토링하며 패턴을 적용해보는 연습.
- 라이브러리 분석
- Spring, Hibernate 같은 프레임워크 소스코드를 읽으며 패턴 학습.
결론
디자인 패턴은 개발자의 문제 해결 능력을 크게 향상시킵니다. 그러나 문제를 해결하기 위한 도구라는 본질을 잊지 말고, 필요한 상황에서 적절히 활용하는 것이 중요합니다.
추가.
Facade(퍼사드) 패턴 :
복잡한 서브시스템을 단순화하여 클라이언트가 쉽게 사용할 수 있도록 통합된 인터페이스를 제공하는 것
package day16;
// 서브시스템 1: 재고 관리
class InventoryService {
public boolean checkStock(String productId) {
System.out.println("Checking stock for product: " + productId);
return true; // 재고 있다고 가정
}
}
// 서브시스템 2: 결제 처리
class PaymentService {
public boolean processPayment(String customerId, double amount) {
System.out.println("Processing payment for customer: " + customerId + ", amount: " + amount);
return true; // 결제 성공 가정
}
}
// 서브시스템 3: 배송 준비
class ShippingService {
public void shipProduct(String productId) {
System.out.println("Shipping product: " + productId);
}
}
// Facade 클래스
class OrderFacade {
private InventoryService inventoryService;
private PaymentService paymentService;
private ShippingService shippingService;
public OrderFacade() {
this.inventoryService = new InventoryService();
this.paymentService = new PaymentService();
this.shippingService = new ShippingService();
}
public void placeOrder(String productId, String customerId, double amount) {
System.out.println("Placing order for product: " + productId);
if (inventoryService.checkStock(productId)) {
if (paymentService.processPayment(customerId, amount)) {
shippingService.shipProduct(productId);
System.out.println("Order placed successfully!");
} else {
System.out.println("Payment failed!");
}
} else {
System.out.println("Product out of stock!");
}
}
}
// 클라이언트 코드
public class FacadeExample {
public static void main(String[] args) {
OrderFacade orderFacade = new OrderFacade();
orderFacade.placeOrder("P123", "C456", 99.99);
}
}
전략(Strategy) 패턴 :
런타임에 동작(알고리즘)을 선택하거나 변경할 수 있도록, 알고리즘을 캡슐화하여 인터페이스로 정의하고, 이를 구현하는 여러 클래스를 사용하는 디자인 패턴
// 1. 전략 인터페이스 정의
interface PaymentStrategy {
void pay(int amount);
}
// 2. 구체적인 전략 클래스들
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);
}
}
class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal: " + email);
}
}
class NaverPayPayment implements PaymentStrategy {
private String account;
public NaverPayPayment(String account) {
this.account = account;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Naver Pay: " + account);
}
}
// 3. 컨텍스트 클래스 (Payment를 사용하는 부분)
class ShoppingCart {
private PaymentStrategy paymentStrategy;
// 전략(결제 방식)을 런타임에 설정
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
if (paymentStrategy == null) {
throw new IllegalStateException("Payment strategy is not set!");
}
paymentStrategy.pay(amount);
}
}
// 4. 클라이언트 코드
public class StrategyExample {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 1. 신용카드 결제
cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456"));
cart.checkout(5000);
// 2. PayPal 결제
cart.setPaymentStrategy(new PayPalPayment("user@example.com"));
cart.checkout(10000);
// 3. 네이버페이 결제
cart.setPaymentStrategy(new NaverPayPayment("naverUser123"));
cart.checkout(15000);
}
}
'멋쟁이사자처럼_부트캠프 > Java' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 39일차 람다식 (0) | 2025.02.05 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 22일차 XML, 웹사이트 접속 (1) | 2025.01.02 |
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 19일차 OOP, SOLID 원칙 (1) | 2024.12.27 |
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 18일차 멀티 스레드 (3) | 2024.12.26 |
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 17일차 Java IO, 버퍼 (2) | 2024.12.24 |