본문 바로가기

백엔드 잡학사전

[스프링 핵심] OOP 관점에서 코드 관찰하기, 스프링으로 전환하기

일단 정률 할인을 넣어준다. 그리고 테스트를 해준다.

 

만약 이렇게 잘 설계가 안된 코드라면 테스트를 하기도 매우 어려웠을 것이다. 그런데 우리 코드는 잘 되어 있기 때문에 좋은 것이다.

 

새로운 할인정책을 넣으려면, OrderServiceImpl로 들어가서 또 생성자를 건드려줘야 하는데,

기존에 FixDiscountPolicy를 RateDiscountPolicy로 교체해줘야 한다...

 

지금까지 잘하고 있는지 확인해보자.

 

OrderService가 DiscountPolicy의 인터페이스에만 의존하기로 되어있었는데, 그의 구현체 클래스에도 의존을 하고 있기 때문에 OCP를 위배하고 있다는 것이다.

 

그러니까 구현체를 다루는 부분의 코드를 아예 클라이언트 단의 코드에서 제거해야 한다. 어떻게?

 

관심사의 분리

조승우가 팬텀역할이라고 하면, 상대 역할인 크리스틴에 누구를 캐스팅할지를 조승우가 정하는 게 아닌 것처럼,

OrderServiceImpl도 DiscountPolicy의 구현체를 특정하면 안 된다. 이건 곧 DIP와 OCP를 모두 위반한다.

 

그렇기 때문에 '관심사 분리'가 필요하다 - 다른 말로는 '역할 분리'. 조승우한테 줄리엣 캐스팅까지 맡기지 말고, 그건 다른 사람에게 맡기고 조승우는 팬텀 역할만 잘 수행할 수 있어야 한다는 것.

 

AppConfig의 등장: 앱의 전체 동작 방식의 운영을 책임지는 친구라고 생각하면 된다. 위의 저런 것들을 여기서 다 하면 된다. 그러니까 가장 윗단에 파일을 만들어야 하고.

 

AppConfig 쪽에서 "생성자 주입"으로 넘겨주는 코드를 다음과 같이 짜준다.

 

 

그러니까, 클라이언트 구현체에서는

이렇게 하고, AppConfig에서는

이렇게 해주고, 그 외 부산물들에서는 이제부터는 AppConfig를 받아서 사용하면 그만인 것이다!

 

Appconfig 리팩터링!

기존 코드는 다음과 같다. 가독성이 별로다...

 

그래서 이걸 바꿔주면,

 

이렇게 되고 만다.

 

이제 만약 FixDiscountPolicy를 Rate'''로 바꿔주려면, 다른 부분 건드릴 필요없이 걍 AppConfig만 고쳐주면 그만이다.

 

자 이렇게 되면, 지금껏 만든 프로그램은 SRP, DIP, OCP를 만족한다. 제어의 역전(IoC)도 시전되어 있고.

정적 클래스 의존관계는 코드에 implements 혹은 import 보면 확인 가능하다.

동적 클래스 의존관계는 코드로 봐서는 확인하기 어렵지만, 런타임에 확인가능한데, DI로 확인 가능하다.

 

IOC 컨테이너 DI 컨테이너: 우리 앱에서의 AppConfig가 우리 코드의 IoC 컨테이너 (DI 컨테이너) 인 것이다.

                                             어셈블러, 오브젝트 팩토리 등으로 불리기도 한다.

 

DI를 순수 JAVA가 아닌 SPRING 기반으로 바꿔주기

AppConfig 파일에,

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

 

추가해주고 AppConfig에 @Configuration 붙여주고 각각의 메소드에 @Bean 해주고.

 

중요한 것은, 이게 사용되는 곳에서도 AppConfig() 으로 불러주는 게 아니라,

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);

 

이렇게 짜줘야 한다.

 

스프링 컨테이너?

위에서의 ApplicationContext를 스프링 컨테이너라고 한다. 기존에 우리가 만들어준 AppConfig를 스프링 컨테이너로 사용해주는 것이다. @Configuration이 붙으면 Spring이 스프링 컨테이너라고 칭하고, 여기서 @Bean이라는 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 기존처럼 AppConfig().뭐시기 가 아니라 applicationContext.getBean()을 사용하면 그만이다.