[스프링 핵심 원리 - 기본편] AppConfig (동작 방식 구성, 관심사 분리)

강신현·2022년 8월 18일
0

✅ @Configuration ✅ @Bean


필요성

이전 포스트 참고
[스프링 핵심 원리 - 기본편] SOLID


AppConfig

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


예제

이전 포스트와 다르게 클라이언트인 MemberSeriveImpl에서 MemberRepository를 직접 객체를 생성하지 않고
AppConfig에서 따로 애플리케이션의 실제 동작에 필요한 구현 객체를 생성하고 연결 해주어 관심사를 분리한다.
구현 객체는 클라이언트인 ServiceImpl에서 생성자를 통해 주입된다.

관심사의 분리: 객체를 생성(AppConfig)하고 연결하는 역할과 실행(MemberSeriveImpl)하는 역할이 명확히 분리하는 것

  • AppConfig
public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl( new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}

클라이언트

  • MemberServiceImpl
public class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    ...
}
  • OrderServiceImpl
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
    ...
}
  • main
    메인에서도 클라이언트 객체(MemberServiceImpl)를 직접 생성하지 않고 AppConfig를 통해 외부에서 받아와 사용한다.
public class MemberApp {

    public static void main(String[] args) {
        // MemberService memberService = new MemberServiceImpl();

        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
    }
}

DIP ⭕️
(의존관계 역전 원칙)
👉 클라이언트는 인터페이스(MemberRepository)에만 의존할 뿐,
구체적인 구현 객체 클래스(MemoryMemberRepository) 에 대해 전혀 알 수 없으므로 DIP를 만족한다.

DI
👉 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서
DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입한다고 한다.


- 리팩터링1

AppConfig를 중복(MemoryMemberRepository 중복 생성)을 제거하고,
역할에 따른 구현도 잘 보이게 리팩터링 해보자

  • AppConfig
public class AppConfig {

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

👉 이제 새로운 할인 정책으로 변경(FixDiscountPolicy -> RateDiscountPolicy)해야 한다면
클라이언트(사용 영역)를 수정할 필요 없이 AppConfig(구성 영역)만 수정해주면 된다!


DI, 컨테이너 등 용어가 뭔지는 아래 포스트 참고
[스프링 핵심 원리 - 기본편] IoC(제어의 역전), DI(의존관계 주입), 컨테이너

- 리팩터링2 (스프링 기반)

이전까지는 순수한 자바코드로 DI를 적용했지만

스프링을 사용하면 @Configuration 와 @Bean 을 사용하여 간편하게 DI를 적용할 수 있다.
1. AppConfig에 설정을 구성한다는 뜻의 @Configuration 을 붙여준다.
2. 각 메서드에 @Bean 을 붙여 스프링 컨테이너에 스프링 빈으로 등록한다.

  • AppConfig
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(),
                discountPolicy());
    }
    
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}
  • main
public class MemberApp {

    public static void main(String[] args) {

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

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());

    }
}

👉 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만,
이제 스프링 컨테이너에 객체를 스프링 빈으로 등록하고, 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다.

코드는 약간 복잡해졌지만 스프링을 통해 수많은 기능들을 사용할 수 있을 것이다.


강의 출처

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

profile
땅콩의 모험 (server)

0개의 댓글