ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 핵심 원리 이해2 - 객체 지향 원리 적용
    Spring/스프링 핵심 원리 - 기본편 2022. 8. 3. 14:39

    [인프런] 스프링 핵심 원리 - 기본편

     

    할인 정책 변경 요청 => 금액당 할인

     

    RateDiscountPolicyTest

     

    @DisplayName

     클래스와 메소드에 사용 가능

     테스트의 이름을 텍스트로 설정함

     

    할인 정책을 변경하려면 OrderServiceImpl의 new 문의 코드를 고쳐야 함

    📎문제점

    OrderServiceImplDiscountPolicy 추상 인터페이스에 의존하긴 하지만,

    구현 클래스인 FixDiscountPolicyRateDiscountPolicy에도 의존하고 있음

     => DIP 위반 + OCP 위반

     

    📎해결방법

    인터페이스에만 의존하도록 변경하면 됨

    private DiscountPolicy discountPolicy;
    // new 객체를 삭제 (인터페이스 문만 남김)

     인터페이스로 객체를 만들었기 때문에 NullPointerException 발생

        ⭐누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해야 함

     


    관심사의 분리

    AppConfig의 등장

     애플리케이션의 전체 동작 방식을 구성(config)하기 위해 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스 생성

    생성한 객체 인스턴스의 참조를 생성자를 통해서 주입해줌

     

    전에는 MemberServiceImpl에서 객체를 직접 만들었음

    이제는 DIP를 지키는 방식으로 객체를 만들기 위해 AppConfig에서 객체를 만드는 것을 담당함

     

        ⭐ 이렇게 하면 결국 객체를 생성하고 연결하는 역할실행하는 역할이 명확리히 분리됨(관심사의 분리)!!

     

    main 메소드에 추가

    AppConfig appConfig = new AppConfig();
    MemberService memberService = appConfig.memberService();
    //MemberService memberService = new MemberServiceImpl();

     

     

    역할이 잘 드러나게 Refactoring해야 함 => 역할과 구현 클래스가 한 눈에 보임

    📎memberRepository가 호출되면 memberRepository() 메소드에서 memberRepository를 생성하고 MemoryMemberRepository를 반환하는 방식

    public class AppConfig {
    
        public MemberServiceImpl memberService() {
            return new MemberServiceImpl(memberRepository());
        }
    
        public MemberRepository memberRepository() {
            return new MemoryMemberRepository();
        }
    
        public OrderService orderService() {
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }
    
        public DiscountPolicy discountPolicy() {
            return new FixDiscountPolicy();
        }
    }

     

    📎new MemoryMemberRepository() 의 중복이 memberRepository() 를 만들면서 제거됨

     

     


    새로운 구조와 할인 정책 적용

     

     

    IoC, DI, 컨테이너

     

    📎제어의 역전 IoC(Inversion of Control)

    프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 뜻 함

    프로그램의 제어 흐름을 구현 객체가 아닌 AppConfig가 가져간 것

     

    📎프레임워크 vs 라이브러리

    프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크

    내가 작성한 코드가 직접 제어의 흐름을 담당하면 라이브러리

     

    📎의존관계 주입 DI(Dependency Injection)

    의존관계는 정적인 클래스 의존관계와, 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계를 분리해서 생각해야함

    *동적인 객체 인스턴스 의존 관계

        : 애플리케이션 실행 시점에 실제 실행된 객체 인스턴스 참조가 연결된 의존 관계

     

    의존관계 주입: 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것 (참조값을 전달해서 연결됨)

     

    📎IoC 컨테이너, DI 컨테이너

    AppConfig 같이 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC 컨테이너 혹은 DI 컨테이너라고 함

    어샘블러, 오브젝트 팩토리 등으로 불리기도 함

     

     

    스프링으로 전환하기

    AppConfig 에 @Configuration 과 각 메소드마다 @Bean 어노테이션 추가

    @Configuration
    public class AppConfig {
    
        @Bean
        public MemberServiceImpl 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 RateDiscountPolicy();
        }
    }
    

    @Configuration: 애플리케이션의 설정정보를 뜻함

    @Bean: 메소드에 설정해주면 스프링 컨테이너에 등록됨

     

     

    public class MemberApp {
        public static void main(String[] args) {
    
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
            MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
            
            ```

    ApplicationContext: 스프링 컨테이너

    applicationContext 객체: @Bean들을 전부 관리해줌

    📎ApplicationContext applicationContext  = new AnnotationConfigApplicationContext(AppConfig.class);

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

    getBean(찾을 객체 이름 = 보통 메소드 이름으로 설정됨, 타입)

     

    실행 결과

    @bean으로 등록한 메소드 => key는 memberService 같은 이름으로, value는 객체 인스턴스로 해서 스프링 컨테이너에 등록됨

     

    📍결론

          📎ApplicationContext를 스프링 컨테이너라고 함

          📎스프링 컨테이너는 @Configuration이 붙은 AppConfig를 구성 정보로 사용

          📎그때, @Bean이 적힌 메서드를 모두 스프링 컨테이너에 등록함 => 스프링 빈

          📎스프링 빈은 @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용함

          📎스프링 빈은 스프링 컨테이너를 통해서 찾아야 함 => applicationContext.getBean()

Designed by Tistory.