스프링이 사랑한 디자인 패턴

어댑터 패턴(Adapter Pattern)

public class ServiceA {
    void runServiceA() {
        System.out.println("ServiceA");
    }
}
public class ServiceB {
    void runServiceB() {
        System.out.println("ServiceB");
    }
}
public class ClientWithNoAdapter {
    public static void main(String[] args) {
        Service sa1 = new ServiceA();
        Service sa2 = new Service B();
        
        sa1.runServiceA();
        sa2.runServiceB();
    }
}

프록시 패턴(Proxy Pattern)

Spring 에서 프록시 패턴은 AOP(Aspect-Oriented Programming)을 구현할 때 핵심적으로 사용된다. 트랜잭션 처리, 로깅, 보안, 지연 로딩(Lazy loading) 등에 쓰인다.

프록시 패턴

  • 실제 객체에 대한 접근을 제어하거나 기능을 추가하기 위해 대리 객체(proxy)를 사용하는 패턴

✅ Spring에서의 프록시 패턴 적용 전 vs 적용 후

🧱 적용 전 (순수 서비스 호출)

🛠 적용 후 (AOP 기반 프록시 적용)

✅ 핵심 구조

Spring은 내부적으로 다음 중 하나로 프록시 생성:

  • JDK 동적 프록시 (인터페이스 기반)

  • CGLIB 프록시 (클래스 기반)

  • 대리자는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용한다.

  • 대리자는 실제 서비스에 대한 참조 변수를 갖는다(합성)

  • 대리자는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려줌

  • 대리자는 실제 서비스의 메서드 호출 전후에 별도의 로직을 집어넣을 수 있다.

✅ 데코레이터 패턴이란?

기존 객체에 책임(기능)을 덧붙이는 구조 패턴. 상속이 아닌 합성(composition)으로 기능을 확장함.

프록시 패턴과 decorator 패턴은 구현 방법이 같다. 다만 프록시 패턴은 클라이언트가 최종적으로 돌려 받는 반환값을 조작하지 않고 그대로 전달하는 반면 데코레이터 패턴은 클라이언트가 받는 반환값에 장식을 덧입힌다.

공통점

항목
설명

구조

둘 다 내부에 실제 객체(Real Object)를 가지고 있음

동작

메서드 호출을 위임함 (delegate.method())

합성

상속보다 객체 합성을 사용함 (구성관계)


✅ 핵심 차이점 요약

구분
프록시 패턴
데코레이터 패턴

목적

접근 제어 / 로깅 / 보안 / 캐싱 / 트랜잭션 관리

기능 확장

의도

실제 객체를 대신해 제어/보호하거나 간섭

실제 객체에 기능을 추가

예시

@Transactional, @Cacheable, 지연 로딩 Proxy

WebClient Filter, 필터 체인, 로깅 데코레이터

기능 변화

원래 기능은 그대로, 호출 방식만 제어

기능에 새로운 동작을 덧붙임

사용자 입장

실제 객체인지 Proxy인지 몰라도 됨

사용자는 여러 데코레이터를 조립함


✅ 한 문장 요약

프록시는 "통제"를 위해 감싸고, 데코레이터는 "확장"을 위해 감싼다.


✅ 코드 비교 예

📌 프록시 (예: 트랜잭션 프록시)

  • 목적: 실제 호출을 감시하고 제어 (트랜잭션 관리 등)


📌 데코레이터

  • 목적: 기존 기능에 로깅 기능을 확장

✅ 차이점 정리: 리턴값 처리 방식

항목
프록시
데코레이터

리턴값 처리

보통 변경하지 않고 그대로 반환

기능에 따라 리턴값을 가공하거나 변형

목적

호출 전후 로깅/트랜잭션 등의 부가처리

호출 전후 + 리턴값을 꾸미거나 추가기능 포함 가능

사용 예

AOP의 트랜잭션 프록시 → return target.method()

응답 캐싱, 출력 포맷 가공 등

싱글턴 패턴(Singleton Pattern)

오직 인스턴스를 하나만 만들고 그것을 계속해서 재사용함.

  • new를 실행할 수 없도록 생성자에 private 접근 제어자를 지정한다.

  • 유일한 단일 객체를 반환할 수 있는 정적 메서드가 필요하다.

  • 유일한 단일 객체를 참조할 정적 참조 변수가 필요하다.

  • 단일 객체는 쓰기 가능한 속성을 갖지 않는 것이 정석이다.

단일 객체인 경우 결국 공유 객체로 사용되기 때문에 속성을 갖지 않도록 하는 것이 정석이다. 단일 객체가 속성을 갖게 되면 하나의 참조 변수가 변경한 단일 객체의 속성이 다른 참조 변수에 영향을 미치기 때문이다.

템플릿 메서드 패턴(Template Method Pattern)

  • 상위 클래스가 알고리즘의 "골격"을 정의하고,

  • 하위 클래스가 세부 구현을 오버라이딩하는 방식의 디자인 패턴

  • 핵심은 "공통된 흐름은 고정하고, 세부는 확장"

템플릿 메서드 패턴의 구성요소
상위 클래스 Animal
하위 클래스 Dog/Cat

공통로직필수

playWithOwner()

추상 메서드

play()

오버라이딩 필수

훅 메소드

runSomething()

오버라이딩 선택

팩터리 메서드 패턴

팩터리 메서드는 객체를 생성 반환하는 메서드를 말한다. 여기에 패턴이 붙으면 하위 클래스에서 팩터리 메서드를 오버라이딩해서 객체를 반환하게 하는 것을 의미한다.

Last updated