어플리케이션 + 프록시

slee2·2022년 3월 9일
0

인터페이스 기반 프록시 - 적용

V1에 LogTrace를 사용해보자.
프록시를 사용하면 기존 코드를 전혀 수정하지 않고 로그 추적 기능을 도입할 수 있다.

V1 기본 클래스 의존 관계

V1 런타임 객체 의존 관계

여기에 프록시를 추가한다면,

Controller, Service, Repository각각 인터페이스에 맞는 프록시 구현체를 추가

Repository 프록시

Service 프록시

Controller 프록시

Config

LogTrace가 아직 빈에 등록되지 않아 빨간줄이 뜨는데 다음에 등록할 것이다.

V1 프록시 런타임 객체 의존 관계 설정

  • 프록시를 생성하고 프록시를 실제 스프링 빈 대신 등록한다.
    실제 객체는 스프링 빈으로 등록하지 않는다.

InterfaceProxyConfig 를 통해 프록시를 적용한 후
스프링 컨테이너에 프록시 객체가 등록된다. 스프링 컨테이너는 이제 실제 객체가 아니라 프록시 객체를 스프링 빈으로 관리한다.

이제 실제 객체는 스프링 컨테이너와는 상관이 없다. 실제 객체는 프록시 객체를 통해서 참조될 뿐이다.

프록시 객체는 스프링 컨테이너가 관리하고 자바 힙 메모리에도 올라간다. 반면에 실제 객체는 자바 힙 메모리에는 올라가지만 스프링 컨테이너가 관리하지는 않는다.

이제 InterfaceProxyConfig를 등록하고, 빈으로 LogTrace를 따로 등록한 후에, 실행하면,

실행이 잘 되는 것을 확인할 수 있다.

너무 많은 프록시 클래스를 만들어야 하는 단점이 있기는 한데, 이건 나중에 보기로 하자.
이제 인터페이스가 없는 구체 클래스에 프록시를 어떻게 적용할 수 있는지 알아보자.

구체 클래스 기반 프록시

예제1

이제 프록시를 넣어보자.

예제2

클래스 기반 프록시 도입
자바의 다형성은 인터페이스를 구현하든, 클래스를 상속하든, 상위 타입만 맞으면 다형성이 적용된다.
그러므로 이번에는 인터페이스가 아니라 클래스 기반으로 상속을 받아서 프록시를 만들어보자.

ConcreteClient에는 ConcreteLogic 타입의 객체를 받아야 한다.
ConcreteLogic

  • ConcreteLogic = conreteLogic 본인
  • ConcreteLogic = timeProxy 자식
    둘다 가능

참고: 자바 언어에서 다형성은 인터페이스나 클래스를 구분하지 않고 모두 적용된다. 해당 타입과 그 타입의 하위 타입은 모두 다형성의 대상이 된다. 자바 언어의 너무 기본적인 내용을 이야기했지만, 인터페이스가 없어도 프록시가 가능하다는 것을 확실하게 집고 넘어갈 필요가 있어서 자세히 설명했다.

어플리케이션에 적용

클래스 기반 프록시의 단점
super(null): 자바 기본 문법에 의해 자식 클래스를 생성할 때는 항상 super()로 부모 클래스의 생성자를 호출해야 한다. 이 부분을 생략하면 기본 생성자가 호출된다.
근데 부모 클래스인 OrderServiceV2는 기본 생성자가 없다. 그래서 파라미터를 넣어서 super(...) 호출해야 한다.

프록시는 부모 객체의 기능을 사용하지 않아서 super(null)로 입력해도 된다.

인터페이스 기반 프록시는 이런 고민을 하지 않아도 된다.

잘 실행되는 것을 확인할 수 있다.

인터페이스 기반 프록시와 클래스 기반 프록시

프록시
프록시를 사용하면 원본 코드를 수정하지 않고 LogTrace 기능을 적용할 수 있었다.

인터페이스 기반 프록시 vs 클래스 기반 프록시

  • 인터페이스가 없어도 클래스 기반으로 프록시를 생성할 수 있다.
  • 클래스 기반 프록시는 해당 클래스에만 적용할 수 있다. 인터페이스 기반 프록시는 인터페이스만 같으면 모든 곳에 적용할 수 있다.
  • 클래스 기반 프록시는 상속을 사용하기 때문에 제약이 있다.
    • 부모 클래스 생성자.
    • 클래스에 final 키워드가 붙으면 상속 불가능
    • 메서드에 final 키워드가 붙으면 해당 메서드를 오버라이딩 할 수 없다.

이렇게 보면 인터페이스 기반의 프록시가 더 좋아보이는데 맞다.
제약이 자유롭고, 프로그래밍 관점에서도 인터페이스를 사용하는것이 구현을 나누기 때문에 좋다.
단점은 인터페이스 만드는거다.

참고: 인터페이스 기반 프록시는 캐스팅 관련해서 단점이 있는데 이건 강의 뒷부분에

인터페이스를 도입하는 것은 구현을 변경할 가능성이 있을 때 효과적인데, 구현을 변경할 가능성이 거의 없는 코드에 무작정 인터페이스를 사용하는 것은 번거롭고 실용적이지 않다.
핵심은 인터페이스가 항상 필요하지는 않다는 것.
인터페이스가 쓰는게 좋긴 한데 항상 그렇지는 않다는 뜻.

결론
실무에서는 인터페이스가 있는 경우도 있고, 구체 클래스가 있는 경우도 있고, 섞여있는 경우도 있기 때문에
둘다 대응할 수 있어야 한다.

너무 많은 프록시
코드를 변경하지 않고, 로그 추적기라는 부가 기능을 적용할 수 있는건 좋은데,
프록시 클래스를 너무 많이 만들어야 한다. 컨트롤러 하나당 프록시를 하나 더 만들어야 한다는게 쉽지 않다.
프록시 클래스를 하나만 만들어서 모든 곳에 적용하는 방법은?
이건 다음에 설명할 동적 프록시가 해결해 준다.

0개의 댓글