일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- nestjs typeorm
- 시스템호출
- 카카오 코테
- 프로그래머스
- 카카오
- AWS
- nestJS
- 가상면접사례로배우는대규모시스템설계기초
- C++
- spring boot
- @Component
- @Autowired
- python
- Nodejs
- nestjs auth
- 해시
- 컴포넌트스캔
- 스프링
- Spring
- git
- 파이썬
- OpenCV
- 코테
- 카카오 알고리즘
- thymeleaf
- TypeORM
- 알고리즘
- C언어
- 구조체배열
- 코딩테스트
- Today
- Total
공부 기록장 💻
[Spring] List, Map을 이용해 동일한 타입 내 모든 스프링 빈 조회하기 (다형성에 기반한 전략 패턴을 활용하여 할인 정책 서비스 구현하기) 본문
[Spring] List, Map을 이용해 동일한 타입 내 모든 스프링 빈 조회하기 (다형성에 기반한 전략 패턴을 활용하여 할인 정책 서비스 구현하기)
dream_for 2023. 2. 9. 13:11전략 패턴이란?
할인 서비스를 제공하는 시스템에서, 클라이언트가 직접 할인의 종류(고정 할인 정책, 비율 할인 정책)를 선택할 수 있는 상황을 가정하면, 디자인 패턴 중 하나인 전략 패턴(strategy pattern)에 기반하여, 동일한 타입 내의 모든 스프링 빈을 조회하는 방식으로 이를 구현할 수 있다.
전략 패턴의 단어 정의를 간단히 살펴보면 다음과 같다.
전략 패턴(strategy pattern) 또는 정책 패턴(policy pattern)은 실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴이다. 전략 패턴은 특정한 계열의 알고리즘들을 정의하고 각 알고리즘을 캡슐화하며 이 알고리즘들을 해당 계열 안에서 상호 교체가 가능하게 만든다.
전략은 알고리즘을 사용하는 클라이언트와는 독립적으로 다양하게 만든다. 전략은 유연하고 재사용 가능한 객체 지향 소프트웨어를 어떻게 설계하는지 기술하기 위해 디자인 패턴의 개념을 보급시킨 디자인 패턴(Gamma 등)이라는 영향력 있는 책에 포함된 패턴들 가운데 하나이다.
- Wikipedia
전략 패턴을 구현하는 방식
이는 인터페이스와 추상화, DI 등의 기능을 통해 다형성을 가능하게 하는 스프링 컨테이너에서 동일한 타입 내의 모든 스프링 빈을 조회하는 방법을 이용해 위의 전략 패턴을 구현할 수 있다.
List와 Map 을 이용하여 같은 타입에 대해 조회한 모든 빈을 찾아 클라이언트의 요청이나 입력에 따라, 각 요청에 맞는 객체를 주입해주는 방식 (즉, 각 상황에 맞는 전략을 다르게 사용) 을 테스트 코드를 사용해 구현해보도록 하자.
DiscountService
DiscountService 라는 이름의 static 클래스를 만들어보자.
필드에 String을 Key 값으로, DiscountPolicy 인터페이스를 Value 값으로 하는 policyMap, 그리고 DiscountPolicy 리스트인 policies를 선언하고, 생성자 파라미터를 통해 이를 받아와 각 필드를 초기화하도록 하자.
즉 DiscountService는 모든 DiscountPolicy 객체들을 주입받는다고 할 수 있다.
findAllBean()
우선 AnnotationConfigApplicationContext 를 이용하여 스프링 컨테이너를 생성하고,
AutoAppConfig와 DiscountService의 클래스를 각각 생성자 파라미터에 추가하여 해당 클래스를 자동으로 스프링 빈으로 등록한다.
(여기서 등록하는 스프링 빈인 AutoAppConfig는 @Component가 붙은 객체들을 자동적으로 빈으로 등록하는 컴포넌트 스캔을 이용하는 방식의 설정 정보 클래스이다.)
이를 실행해 보았을 때, 아래와 같이 policyMap의 경우 fixedDiscountPolicy와 ratedDiscountPolicy 가 키 값으로, 그리고 각각의 객체 참조값이 value 을 출력하는 것을 볼 수 있다.
discount()
다음으로 DiscountService에 discount() 메서드를 추가하자.
discount()는 Member 객체와 상품 가격, discountCode라는 문자열을 파라미터로 받는다.
이후파라미터로 받은 discountCode 문자열 값을 key 값으로, 생성자 파라미터로 주입 받은 policyMap의 value 값 중, discountCode 값에 해당하는 DiscountPolicy 구현 객체를 찾아,
해당 구현 객체의 discount() 구현 메서드를 호출하여, 할인된 가격을 반환하게 된다.
이제 테스트 코드의 나머지 부분을 추가하여 검증 해보도록 하자.
ac.getBean() 을 이용해 DiscountService의 구현 객체들을 가져와, 위에서 만든 discountService 객체의 discount() 메서드를 실행하여 동적으로 구현 객체 빈을 직접 선택해보도록 하자.
discount() 메서드 호출 시 discountCode 파라미터 값을 fixedDiscountPolicy로 전달하여, 결과 값이 고정 금액인 1000이 맞는지 검증해보도록 하자.
위의 테스트는 검증 성공하였다.
아래를 보면, discountCode 값으로 fixedDiscountPolicy 문자열이 잘 전달되었고, 선택된 구현 객체인 discountPolicy의 구현 객체 참조값을 통해 FixedDiscountPolicy임을 확인할 수 있다.
discountCode 값을 ratedDiscountPolicy로 선택하여 전달하는 경우에는, 구매 금액 20000원의 10% 값인 2000이 discountPrice에 저장된다.
요약과 결론
DiscountService의 로직을 다시 요약해보자면 다음과 같다.
- 생성자 파라미터를 통해 List, Map을 이용해 모든 DiscountPolicy 구현 객체들을 주입받는다.
- 이때, DiscountPolicy 구현 객체들인 FixedDiscountPolicy, RatedDiscountPolicy는 모두 스프링 빈으로 등록되어 있다. 스프링 빈으로 등록한 AutoAppConfig는 컴포넌트 스캔을 통해 @Component 가 붙어 있는 모든 객체들을 스프링 빈으로 자동 등록하는 설정 정보 기능을 사용했기 때문이다.
- discount() 메서드 호출 시, discountCode에 어떤 값을 전달하느냐에 따라 동적으로 어떤 스프링 빈의 discount() 메서드를 호출할 지 결정하여 실행하게 된다.
결론적으로, 컴포넌트 스캔을 이용한 의존 관계 자동 주입 기능을 활용하여, DiscountService를 통해 우리는 DiscountPolicy 라는 동일한 타입 내의 모든 스프링 빈인 fixedDiscountPolicy와 ratedDiscountPolicy를 모두 주입 받아,
클라이언트의 요청에 따라 동적으로 구체적인 구현 객체를 선택하도록 구현을 하였다.
다시 말해, 동적으로 빈을 선택해야 하는 상황에서 다형성을 유지하면서 이를 활용하여 유연한 전략 패턴이 발휘할 수 있음을 확인할 수 있다.
[참고자료]