Spring 핵심 원리 TIL (4)

YulHee Kim·2021년 8월 22일
0

Spring 핵심 원리

목록 보기
4/13

[참고 강의] 김영한님의 스프링 핵심 원리 - 기본편

💡 객체 지향 원리 적용

기존 코드는 OCP, DIP를 위반한 코드였다.
그래서 위 원칙들을 위반하지 않는 코드를 짜보겠다.
즉, 인터페이스에만 의존하도록 의존관계를 변경하여 코드를 짜보겠다. 😎

✏️ AppConfig 등장

그전부터 계속 언급했던 의존관계 주입 역할을 AppConfig가 하게 될 것이다.
클라이언트 코드를 변경하지 않고 AppConfig 코드 변경만으로
기능을 확장해서 변경할 수 있게된다. DIP, OCP 원칙을 잘 지킬 수 있게된다.

뒤에서 자세히 설명할 DI컨테이너의 역할을 하는 아이다.

AppConfig

  • 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;
    }
...
}

이제 클라이언트는 구현체(MemoryMemberRepository)가 아닌 인터페이스에만 의존한다 !

모든 구현체의 설정과 변경은 AppConfig에서 진행된다
DIP 완성 👍

클래스 다이어그램

객체의 생성과 연결은 AppConfig가 담당한다.
실행의 역할(클라이언트)과 AppConfig는 명백히 분리되어있다!

AppConfig 실행

MemberApp

public class MemberApp {
      public static void main(String[] args) {
          AppConfig appConfig = new AppConfig();
          MemberService memberService = appConfig.memberService();
          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("find Member = " + findMember.getName());
      }
}

appConfig에서 memberService()를 호출하고 있다
정말 AppConfig에서 객체 생성과 연결을 함께해주고 있다 🧐

✏️ AppConfig 리팩토링

현재 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();
} }

할인정책과 레포지토리 설정에 대한 부분의 역할이 명확하게 보인다.
예시로 할인정책을 정률정책으로 변경하려면 discountPolicy() 부분만 정률 정책으로 변경하면 된다.
애플리케이션 전체 구성이 어떻게 되어있는지 훨씬 더 잘보인다

코드로 직접 해보며 이해하니 DI의 느낌이 점점 잡히고 있는거 같다 > <

할인 정책의 변경

위에서 내가 말로 든 예시인 할인정책을 변경하려면

public DiscountPolicy discountPolicy() {
        // return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

이 부분만 변경해주면된다.
예전 DIP가 위반된 코드에선 클라이언트 코드도 변경해주어야 하기에 OCP위반도 되었다.
하지만 AppConfig에서 한 부분만 바꾸고 기능이 변경되었다 !

그림으로 한번 봐보면,, 이런 상태이다

💡 IoC, DI, 그리고 컨테이너

스프링의 핵심기술 IoC, DI에 대해서도 정리해보겠다

✏️ 제어의 역전 IoC

객체에 대한 제어권이 개발자로부터 컨테이너 혹은 프레임워크로 넘어간 것이라고 표현할 수 있다. (제어권이 뒤바뀐다는 느낌)

제어권이 개발자에서 컨테이너로 넘어갔다는 표현이 무엇일까?
말그대로 개발자가 프로그램을 개발할 때 클라이언트 구현 객체가 스스로 필요한 객체를 생성하고 연결하고 실행했다.
구현 객체가 프로그램의 제어 흐름을 스스로 조종하는 것이다.

하지만 DI 컨테이너인 AppConfig가 등장한 후에는
구현 객체는 자신의 로직을 실행하는 역할만 담당한다.
프로그램의 제어 흐름은 AppConfig가 담당하는 것이다.

위에서 계속 말했듯이 OrderServiceImpl같은 클라이언트를 AppConfig가 직접 생성하고 클라이언트를 생성할 때 메모리멤버레포지토리 등의 구현 객체를 주입하여 연결해준다.

즉, OrderServiceImpl은 자신의 로직을 실행하고만 있을 뿐
어떤 구현 객체가 주입되고 있는지 모른다.
사용 영역과 구성 영역이 명백히 분리된 것이다.

이렇듯 프로그램의 제어 흐름을 외부에서 관리하는 것을 제어의 역전(IoC)라고 한다.

✏️ 의존관계 주입 DI

DI 또한 객체를 직접 생성해 주는 것이 아니라 외부에서 생성한 후 주입시켜주는 방식이다.
DI를 통해서 객체 간의 결합도가 낮아지고 유연성이 높아진다.

OrderServiceImpl은 DiscountPolicy 인터페이스에 의존한다. 구현 객체는 정률할인, 고정할인 어떤것이 사용될지 OrderServiceImpl은 모른다.
외부(AppConfig)에서 직접 클라이언트(OrderServiceImpl)를 생성하고 DiscountPolicy를 구현한 객체를 생성하고 연결시켜 주기 때문이다.

✏️ IoC컨테이너, DI컨테이너

  • AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것이다.
  • IoC컨테이너, DI컨테이너라 한다.
  • 요즘에는 주로 DI컨테이너라고 한다. 의존관계 주입에 초점을 맞추기 때문 !

profile
백엔드 개발자

0개의 댓글