본문 바로가기
Spring(JAVA Framework)/Spring Core

스프링 핵심 원리 이해2

by 걸어가는 신사 2021. 8. 15.
스프링의 핵심기능을 사용하지 않은 상태로 개발을 해보고 객체지향의 원칙(OCP, DIP)을 지키기 위해 스프링 핵심 기능들이 무슨 역할을 하는지 알아보는 시간입니다. 

 

1. 새로운 할인 정책 개발

(1) Policy

  • 고정 금액 할인이 아니라 정률% 할인으로 변경
  • RateDiscountPolicy (VIP인 경우 10% 할인)
import hello.core.member.Grade;
import hello.core.member.Member;

public class RateDiscountPolicy implements DiscountPolicy {

    private int discountPercent = 10;

    @Override
    public int discount(Member member, int price) {
        if(member.getGrade()== Grade.VIP) {
            return price * discountPercent / 100;
        }
        else {
            return 0;
        }
    }
}

 

(2) Service

  • 새로운 할인 정책(Policy)을 적용하기 위해서 OrderServiceImpl의 코드를 고쳐야 한다.
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();
//    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

 

2. 문제점 

  • 역할과 구현을 충실하게 분리하였다. (다형성 활용, 인터페이스와 구현 객체를 분리)
  • DIP 위반 : 주문서비스(OrderServiceImpl)는 DiscountPolicy(인터페이스)에 의존하면서 동시에 FixDiscountPolicy or RateDiscountPolicy(구현 클래스)에 의존하고 있다.
  • OCP 위반 : 코드를 변경하지 않고 확장해야 하는데 FixDiscountPolicy에서 RateDiscountPolicy로 바꿀 때 코드 수정이 발생하였다.

 

3. 해결방안

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}
  • DIP, OCP를 지키기 위해 인터페이스에만 의존하도록 코드를 변경했다.
  • 그런데 구현체가 없는데 어떻게 코드를 실행할 수 있을까?
  • 이 문제를 해결하기 위해 누군가 구현 객체를 대신 생성하고 주입해주어야 한다. (아직 문제 해결되지 않았다.)

 

4. AppConfig 등장 (구현 객체를 대신 생성하고 주입)

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

(1) AppConfig 구현

import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

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

    public OrderService orderService() {
        return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}
  • AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
  • AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다.
    • MemberServiceImpl => MemoryMemberRepository
    • OrderServiceImpl => MemoryMemberRepository, FixDiscountPolicy

 

(2) MemberServiceImpl 생성자 주입

public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}
  • AppConfig를 사용으로 MemberServiceImpl은 MemberRepository(인터페이스)에만 의존한다. (DIP준수)
  • 이후에 MemoryMemberRepository가 아닌 다른 Repository사용 시 MemberServiceImpl 코드 수정 없이 AppConfig만 바꿔주면 된다. (OCP 준수)

 

(3) OrderServiceImpl 생성자 주입

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}
  • AppConfig의 사용으로 OrderServiceImpl은 MemberRepository(인터페이스), DiscountPolicy(인터페이스) 에만 의존한다. (DIP 준수)
  • 이후에 구현 클래스를 바꾸더라도 (FixDiscountPolicy -> RateDiscountPolicy) OrderServiceImpl의 코드 수정 없이 Appconfig만 수정해주면 된다. (OCP 준수)

 

5. 정리

아회원 객체 인스턴스 다이어그램

  • 객체의 생성과 연결은 AppConfig가 담당한다.
  • AppConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달한다.
  • 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 의존관계 주입 또는 의존성 주입이라고 한다. 
AppConfig의 역할 즉 외부에서 객체를 생성 주입 해주는 역할을 Spring이 해줄 것이다. 
Spring의 가장 핵심적인 역할이 DI, 의존관계 주입이라는 것을 알 수 있다.

 


본 글은 김영한 님의 "스프링 핵심 원리"(인프런) 유료 강의를 들으며 요약, 정리하고 일부 정보를 추가 작성한 것입니다.

저작권 문제시 903yh@naver.com으로 연락 주시면 삭제하겠습니다.

반응형

댓글