AppConfig
단어 그대로 앱의 환경설정을 담당한다. 강의의 코드 중 주문 서비스 구현 코드를 예로 설명하겠다.
주문 서비스에서는 회원을 조회해서 등급별 할인을 적용해야 하므로 멤버 레포지토리와 할인 정책 관련 객체가 필요하다. 처음에 코드를 작성했을 때의 모습은 아래와 같다.
MemberRepository memberRepository = new MemoryMemberRepository();
DiscountPolicy discountPolicy = new FixDiscountPolicy();
OrderServiceImpl 클래스 안에 위의 코드가 작성되어 있었다. 이는 DIP 와 OCP 원칙을 위반하게 된다.
- DIP 위반
추상화에 의존해야지 구체화에 의존하면 안된다. 즉 interface에만 의존해야 한다는 뜻이다. 하지만 구현체(MemoryMemberRepository(), FixDiscountPolicy())도 의존하고 있는 상태다. - OCP 위반
확장에는 열려있으나 변경에는 닫혀있어야 한다. 하지만 할인 정책을 바꾸는 상황에서 위에 코드에서 new FixDiscountPolicy();를
new RateDiscountPolicy();로 수정해야한다. 변경에 닫혀있지 않다.
위의 문제를 해결하는 방법이 AppConfig의 활용이다. 지금의 코드는 저장소와 할인 정책에서 어떤 것을 사용할지를 OrderServiceImpl에서
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
추상화에 의존하는 코드로 바꾸었다. 'OderServiceImpl 너는 어떤 것을 의존할지 신경 쓰지 말고 너 할 일만 해.'라고 생각하면 된다. 어떤 MemberRepository, DiscountPolicy를 사용할지 위의 코드만 봐서는 모른다. 그것이 안 적혀있으니 확장할 때도 해당 코드를 변경할 필요가 없으므로 OCP 또한 해결된다. AppConfig를 활용해서 위의 생성자를 통해 주입되는 형태이다.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
AppConfig의 모습이다. 일단 @Configuration을 사용해서 스프링에 설정 페이지라고 알리고 @Bean을 사용해서 컨테이너에 등록한다. 보기에도 이해가 정말 잘되는 형태이다. MemberRepository는? MemoryMemberReopository를 사용하고 다른 클래스와 의존은 없다. OrdeService는? OrderServiceImpl을 사용하고 memberRepostiory와 DiscountPolicy를 의존하고 둘 다 메서드이므로 또 각자의 이름으로 타고 가서 각각 new MemoryMemberRepository()와 new RateDiscountPolicy()가 리턴된다. discountPolicy()의 주석 부분을 보면 할인 정책을 변경해야 할 경우 코드 한 줄만 교체하면 되는 것을 볼 수 있다. 수정에 정말 용이하다.
이제 이것을 main 메서드에서 사용하는 방법을 알아보겠다.
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService =applicationContext.getBean("memberService", MemberService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
}
applicationContext를 사용해서 각각의 인스턴스에 주입해주는 모습을 볼 수 있다. getBean()의 첫 번째 인자는 메서드 명이고 두 번째 인자는 첫 번째 인자의 반환 타입이다. 해당 코드만 보면 뭔가 코드가 복잡해지고 길어진 거만 같다. 이렇게 spring을 사용하면 장점이 무엇일까?? 김영한님이 결론부터 말씀해주셨다. 어마어마한 장점이 있다고.ㅋㅋㅋㅋㅋ 기대하면서 듣다가 여기서 좀 웃었다.
지금까지 인강 들은 거 몰아서 한 번에 다 정리했다. 이제 본격적으로 spirng을 사용하는 강의가 나올 것 같다. 개강 전까지 강의도 열심히 듣고 블로그 정리도 꾸준히 해보자... 알고리즘이랑 자바 문법 공부는 언제 해..?
'Spring' 카테고리의 다른 글
[Spring] 싱글톤 컨테이너 (1) | 2022.07.23 |
---|---|
[Spring] 스프링 컨테이너 & 스프링 빈 조회 (3) | 2022.07.20 |
[Spring] Test (0) | 2022.07.16 |
[Spring] 객체지향 기본 개념 (0) | 2022.07.12 |
[Spring] 동적 페이지 만들기 (1) | 2022.02.06 |