• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

복잡한 decorator 예제

23.02.01 00:57 작성 조회수 654

1

 안녕하세요 토비님!

강의를 듣는 중에, 여러개의 데코레이터를 우선순위를 정하는 방법 말고 원하는 상황에 맞게 주입이 되도록 처리하는 방식에 대해 말씀해주셨는데, 해당 방법에 대해 좀더 자세하게 알고 싶어져서 사용하는 예제 코드를 구글링 해보았지만 못찾겠어서 질문 올립니다.

답변 1

답변을 작성해보세요.

3

데코레이터 패턴의 특징은 같은 인터페이스를 구현한 여러개의 클래스가 존재하고 각각이 다음 오브젝트를 DI 받아서 가지고 있다가 호출을 하는 방식입니다.

예를 들어 Client -> Service(DefaultService) 두 개의 빈이 있는데 Service 인터페이스를 구현한 기본 클래스가 있는 상태에서 Service 인터페이스를 구현한 데코레이터 D1,D2,D3를 만들었고 적용 순서는 Client -> D1-> D2-> D3-> DefaultService 이렇게 지정을 하고 싶을 수가 있겠죠.

데코레이터는 동적이어야 하니까 당연히 이 클래스 내부에는 다음에 호출될 클래스 정보를 알고 있으면 안 됩니다. 대신 구성 정보 어딘가에 이를 넣어줘야 하는데요.

이 경우 Client는 Service 인터페이스에 의존을 하는데, D1~D3와 DefaultServiec모두 Service인터페이스를 의존하고 있으니까 그 중 D1이 선택되도록 만들려면 특별한 작업이 필요합니다.

아주 단순한 방법은 빈의 이름을 이용하는 것입니다. 만약 동일한 타입의 빈이 여러개인 경우 빈의 name이 주입 받을 변수의 이름과 일치하면 그것을 선택해주는 메카니즘이 있습니다. 하지만 이렇게 이름을 이용해서 주입을 받게 하면 결국은 클래스 코드가 빈 주입정보를 직접 가지고 있는 셈이 되어서 DI의 의미가 없어집니다.

가장 나은 방법은 자바 설정(JavaConfig)을 이용하는 것입니다. @Bean 팩토리 메소드에서 D1,D2,D3,DefaultService빈의 구성을 직접 코드로 구현하는 것이지요. 아주 단순하게 만들자면 다음과 같은 식이 될 겁니다. 이 경우 DefaultService는 @Component 계열의 빈 설정을 가지고 있으면 안 되겠죠.

@Bean
Service service() {
return new D1(new D2(new D3(new DefaultService())));
}

그런데 이렇게 코드로 만들면 결국 DI의 기본인, 코드에 미리 지정되어 있지 않고 동적으로 의존관계가 주입된다는 걸 위반하는 게 아닌가 싶겠죠? 근데, 여기서 작성한 @Bean 메소드 코드는 애플리케이션 코드가 아니고 자바 코드로 만든 메타 정보입니다. 따라서 이 경우는 DI가 동작한다고 할 수 있습니다.

그 외에 데코레이터를 AOP를 이용해서 적용하는 방법이 있습니다. 이건 AOP라는 좀 더 복잡한 주제라서 여기서 길게 설명드리기는 힘들 것 같습니다. 지금 준비중인 토비의 스프링 6 강의에서 관련해서 자세히 설명드릴 예정입니다.