Languege/Java & Spring / / 2022. 3. 8. 11:05

[김영한 스프링] 스프링 핵심 원리 이해

스프링 핵심 원리 이해에서는 예제를 만들고 객체 지향 원리를 적용하는 방식으로 이루어졌습니다. 첫번째로 private final MemberRepository memberRepository = new MemoryMemberRepository(); 라는 순수한 자바코드를 작성하였고 이에 대한 코드는 인터페이스 뿐만 아니라 구현 객체도 의존하여 OCP, DIP 위반의 문제가 발생합니다. 왜냐하면 변경에는 닫혀있어야 하는 OCP와 구체화에 의존하면 안되는 DIP를 위반하기 때문입니다.

이를 해결하기 위해

private final MemberRepository memberRepository = new MemberRepository();

라는 인터페이스만을 의존하는 코드를 작성하였고 OCP와 DIP는 해결한 것 처럼 보였습니다.

하지만 구현 객체를 정해주지 않아 NPE가 발생하는 문제점이 생겼고, 문제를 해결하기 위해서는 다른 방법이 필요했습니다. 관심사의 분리를 통해 appconfig 파일을 생성하고, 사용 영역과 구성 영역으로 분리하였습니다. 그리고 생성자 주입을 통해서 이 문제를 해결했습니다.

appconfig라는 파일은 구성 영역으로 사용 영역에 있는 인터페이스만을 의존하는 곳곳에 역할을 분배한다. 이 말은

[AppConfig.java]

public OrderService orderService(){

        return new OrderServiceImpl(getMemberRepository(), discountPolicy());

    }

위와 같은 코드처럼 생성자 주입을 통해 DI를 해줍니다.  이렇게 생성자를 통해 구현 객체를 주입해주면

메인 클래스인 OrderApp에서는 Appconfig를 호출해서 MemberService와 OrderService에

AppConfig를 통해서 생성자를 호출하게 됩니다.

[OrderApp.java]

AppConfig appConfig = new AppConfig();

MemberService memberService = appConfig.memberService();

OrderService orderService = appConfig.orderService();

위 코드를 통해서 memberService와 orderservice에 구현 객체가 생성자 주입을 통해서 생성되고,

해당하는 OrderService 구현 객체에 가보면

[OrderServiceImpl.java]

private final MemberRepository memberRepository;

    private final DiscountPolicy discountPolicy;

 

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {

        this.memberRepository = memberRepository;

        this.discountPolicy = discountPolicy;

    }

같은 코드로 단지 인터페이스만 의존하고 있습니다.

이때 AppConfig에서 구현 객체를 DI 시켜주는 것 입니다. 그래서 DI 컨테이너라고도 불립니다.

이와 같은 일련의 과정을 통해 SRP(단일 책임 원칙)과 OCP(개방 폐쇄 원칙), DIP(의존관계 역전 원칙)을 지킬 수 있었습니다. 이후 spring으로 전환에서는 AppConfig 파일에 @Configuration 어노테이션과 @Bean 어노테이션을 통해 Bean으로 등록해서 스프링 컨테이너에서 관리할 수 있도록 만들었습니다.

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(getMemberRepository());
    }

    @Bean
    public MemoryMemberRepository getMemberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(getMemberRepository(), discountPolicy());
    }

}

 

그리고 OrderApp에서는 스프링 컨테이너를 생성하고 이를 통해서 DI를 해주도록 바꿔주면서 이제는 스프링 컨테이너에서 Bean을 관리하며 각각의 인터페이스에 맞는 구현 객체를 생성자 주입을 통해서 주입 시켜주었습니다.

//        AppConfig appConfig = new AppConfig();
//        MemberService memberService = appConfig.memberService();
//        OrderService orderService = appConfig.orderService();
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유