JoungSik/토비의 스프링 - 6장 (2)

Created Thu, 27 Jan 2022 20:05:02 +0900 Modified Sat, 31 Dec 2022 01:31:35 +0900
1570 Words

토비의 스프링 - 6장 (2)

이번 장은 2~3번에 나눠서 올리게 되었습니다.

먼저 프록시 패턴과 데코레이터 패턴을 설명하려고 합니다. 프록시는 이전 책에서 이야기가 나온적이 있습니다. 바로 전의 트랜잭션 기능과 비지니스 로직을 분리해서 기능을 구현 했었던 적이 있었는데 이때 핵심 기능을 인터페이스화 해 부가 기능이 마치 핵심 기능인 것처럼 위장하고 클라이언트의 요청을 대신 받아주는 대리자와 같은 역할을 하는 코드가 있었습니다. 이 부분을 프록시라고 이야기 합니다.

이런 프록시를 사용하는 목적에는 크게 2가지로 구분 할 수 있습니다.

클라이언트가 타겟에 접근하는 방법을 제어하기 위한 것과 타겟에 부가적인 기능을 부여하기 위해서 쓰이게 됩니다. 다만 이 2가지 목적은 디자인 패턴에선 다른 패턴으로 구분되는데 이것이 프록시 패턴과 데코레이터 패턴입니다.

프록시 패턴은 이러한 프록시 목적 중 클라이언트가 타겟에 접근하는 방법을 제어하려는 목적에 해당할 때 사용하게 됩니다. 그리고 데코레이터 패턴은 부가적인 기능을 부여하기 위한 목적을 가지고 있을 때 사용하게 됩니다.

먼저 데코레이터 패턴으로 불리는 이유는 부가기능들이 마치 제품을 여러 겹으로 포장하고 장식을 붙이는 것처럼 보이는게 데코를 붙이는 것처럼 보이기 때문입니다. 이전에 프록시를 사용했던 코드가 이러한 데코레이터 패턴을 사용했다고 볼 수 있습니다. 그리고 데코레이터 라고 불리는만큼 타겟의 코드를 변경하지 않고 새로운 기능을 추가 할 때 유용한 방법입니다.

다음은 프록시 패턴입니다. 남은 목적 중 하나인 타겟에 대한 접근 방식을 제어하는 목적으로 쓰이게 됩니다. 특정 레이어에서는 읽기전용으로만 코드를 강제 해야 할 때 쓰기 기능을 사용하려고 할때 이런 기능을 동작하지 못하도록 제어할 때 사용하게 됩니다. 이는 즉 타겟의 기능 자체에는 관여하지 않으면서 접근하는 방식만 제어합니다.

이런 프록시를 사용하는데에 생기는 문제 중에는 타겟의 인터페이스를 구현하고 위임하는 코드를 작성하는게 번거롭고 부가 기능을 사용하지 않는 메소드도 구현해야 하는 등 코드의 양이 늘어나고 부가기능에 해당하는 코드가 중복될 가능성이 높아지게 됩니다. 그래서 이런 것을 관리하기 위해서 리플랙션 기능을 사용해 프록시를 만드는 다이나믹 프록시를 사용하게 됩니다.

다이나믹 프록시를 사용하게 된다면 가장 중요한 리플랙션이 뭔지를 알아보겠습니다. 리플랙션은 구체적인 클래스 정보는 알지 못해도 컴파일 된 자바 바이트 코드를 이용해 클래스에 대한 정보를 얻어내 클래스를 사용하는 기법을 말합니다.

다이나믹 프록시를 구현할 때는 InvocationHandler 를 구현하면 됩니다.

그리고 클라이언트에서 타겟을 받을 때는 Proxy 클래스의 newProxyInstance() 를 통해 받으면 됩니다.

Object object = (Object)Proxy.newProxyInstance(
	getClass().getClassLoader(),
  new Class[] {Object.class}, 
  new SampleHandler(new SampleTarget())
);

다이나믹 프록시는 newProxyInstance 를 통해서만 생성이 가능하기 때문에 이를 스프링 빈으로 만들 수가 없습니다. 하지만 팩토리 빈의 getObject() 기능을 이용한다면 가능하게 됩니다.

이러한 프록시 팩토리 빈은 이전의 다이나믹 프록시의 문제점을 해결해 줄 수 있지만 이는 메소드 단위로 일어나는 부가기능 제공이기 때문에 하나의 클래스에는 적용하기가 쉬웠지만 여러 개의 클래스에 공통적인 부가기능을 부여 하는것은 거의 비슷한 프록시 팩토리 빈의 중복을 발생하게 합니다. 이는 곧 수많은 설정 파일과 Handler 오브젝트가 프록시 팩토리 빈의 갯수 만큼 만들어집니다.

스프링에서는 ProxyFactoryBean 이라는 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공합니다.

이는 기존에 타겟 정보를 받아야 했던 InvocationHandler 와 달리 타겟 오브젝트가 담긴 MethodInvocation 오브젝트가 전달되는데 이는 콜백 오브젝트로 proceed() 메서도로 타깃 오브젝트의 메서드를 내부적으로 실행시켜주는 기능이 있어 앞의 프록시 팩토리 빈의 단점 중 하나인 새로운 부가기능을 추가할때 마다 프록시와 프록시 팩토리 빈을 추가 해줄 필요가 없게 됩니다.

이처럼 타겟 오브젝트의 부가기능을 담은 오브젝트를 어드바이스라고 부릅니다. 또한 부가기능 대상을 적용하는 메서드를 알고리즘으로 담은 오브젝트를 포인트 컷이라고 부릅니다.

프록시 팩토리 빈의 코드에서 이런 어드바이스를 등록할 때는 어드바이저 라고 하는 형태로 등록하게 되는데 이는 포인트컷과 어드바이스를 합쳐서 같이 등록해야 합니다.