오봉이와 함께하는 개발 블로그

스프링 핵심 원리 - 고급편 > 인터페이스 기반 프록시 적용 본문

BE/Spring

스프링 핵심 원리 - 고급편 > 인터페이스 기반 프록시 적용

오봉봉이 2024. 8. 27. 22:45
728x90

스프링 핵심 원리 - 고급편 > 인터페이스 기반 프록시 적용

인터페이스와 구현체가 있는 V1 버전에 지금까지 학습한 프록시 혹은 데코레이터(여기서는 부가 기능이기 때문에 데코레이터)를 도입하여 LogTrace를 사용해보자.
이전 예시 코드들에서 보았듯, 프록시를 사용하면 기존 코드를 수정하지 않고 로그 추적 기능(부가 기능)을 도입할 수 있다.

V1의 기본 클래스와 런타임시 의존 관계는 아래 그림과 같다.

image

위 의존 관계를 바탕으로 로그 추적용 프록시를 추가하면 아래와 같은 의존 관계가 형성된다.

imageimage

(위 그림들에서 리포지토리는 생략)

그렇다면 이제 해야할 것은 어플리케이션 실행 시 프록시를 사용하도록 의존 관계 설정이 필요하다.

@RequiredArgsConstructor
public class OrderRepositoryInterfaceProxy implements OrderRepositoryV1 {

  private final OrderRepositoryV1 orderRepositoryV1;
  private final LogTrace logTrace;

  @Override
  public void save(String itemId) {
    TraceStatus status = null;
    try {
      status = logTrace.begin("orderRepository.save()");
      orderRepositoryV1.save(itemId);
      logTrace.end(status);
    } catch (Exception e) {
      logTrace.exception(status, e);
      throw e;
    }
  }
}
@RequiredArgsConstructor
public class OrderServiceInterfaceProxy implements OrderServiceV1 {

  private final OrderServiceV1 orderServiceV1;
  private final LogTrace logTrace;

  @Override
  public void orderItem(String itemId) {
    TraceStatus status = null;
    try {
      status = logTrace.begin("orderServiceV1.orderItem()");
      orderServiceV1.orderItem(itemId);
      logTrace.end(status);
    } catch (Exception e) {
      logTrace.exception(status, e);
      throw e;
    }
  }
}
@RequiredArgsConstructor
public class OrderControllerInterfaceProxy implements OrderControllerV1 {

  private final OrderControllerV1 orderControllerV1;
  private final LogTrace logTrace;

  @Override
  public String request(String itemId) {
    TraceStatus status = null;
    try {
      status = logTrace.begin("OrderControllerProxy.request()");
      String result = orderControllerV1.request(itemId);
      logTrace.end(status);
      return result;
    } catch (Exception e) {
      logTrace.exception(status, e);
      throw e;
    }
  }

  @Override
  public String noLog() {
    return orderControllerV1.noLog();
  }
}

코드는 이전 예제들에서 보았듯 그냥 프록시 객체 생성하고 부가 기능을 추가한 코드이다.

이제 위 프록시 클래스들을 Spring 컨테이너가 관리하도록 Bean 등록이 필요하다.

@Configuration
public class InterfaceProxyConfig {

  @Bean
  public OrderControllerV1 orderController(LogTrace logTrace) {
    OrderControllerV1Impl orderControllerV1 = new OrderControllerV1Impl(orderService(logTrace));
    return new OrderControllerInterfaceProxy(orderControllerV1, logTrace);
  }

  @Bean
  public OrderServiceV1 orderService(LogTrace logTrace) {
    OrderServiceV1Impl orderServiceV1 = new OrderServiceV1Impl(orderRepository(logTrace));
    return new OrderServiceInterfaceProxy(orderServiceV1, logTrace);
  }

  @Bean
  public OrderRepositoryV1 orderRepository(LogTrace logTrace) {
    OrderRepositoryV1Impl orderRepositoryV1 = new OrderRepositoryV1Impl();
    return new OrderRepositoryInterfaceProxy(orderRepositoryV1, logTrace);
  }
}

위 코드를 통해 결국엔 원본 객체를 감싼 프록시 클래스를 Spring Bean으로 등록했고, Bean으로 등록된 프록시를 통해 이전 V1 객체를 사용하고 있다.
즉, V1 객체는 Spring Bean으로 등록되어 있지 않고, 프록시 객체만 등록되어 있다.

imageimage

위 그림과 같은 형태로 작동하게 된다.

프록시 객체가 Spring Bean으로 등록되어 관리되고, 실제 객체는 Spring과는 상관 없는 객체가 되었다. 단지 프록시 객체를 통해 참조될 뿐이다.

//@Import({AppConfigV1.class, AppConfigV2.class})
@Import(InterfaceProxyConfig.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProxyApplication.class, args);
    }

    @Bean
    public LogTrace logTrace() {
        return new ThreadLocalLogTrace();
    }

}

LogTrace를 Bean으로 등록했다.

이제 실행 결과를 보면 아래 로그가 출력된다.

[b0640a7b] OrderControllerProxy.request()
[b0640a7b] |-->orderServiceV1.orderItem()
[b0640a7b] |   |-->orderRepository.save()
[b0640a7b] |   |<--orderRepository.save() time=1004ms
[b0640a7b] |<--orderServiceV1.orderItem() time=1005ms
[b0640a7b] OrderControllerProxy.request() time=1005ms

프록시와 DI를 통해 원본 코드 수정 없이 로그 추적기를 도입했다.
하지만, 프록시 도입 시 많은 클래스를 생성해야 한다는 문제가 존재한다.

 

출처: 김영한 지식공유자의 스프링 핵심 원리 고급편

728x90
Comments