스프링 핵심 원리[기본편] - 예제만들기 , 객체 지향 원리 적용

2022. 12. 27. 11:13·Spring

예제 만들기

주문과 할인 도메인 설계

주문 도메인 협력, 역할, 책임

주문 도메인 전체

주문 도메인 클래스 다이어그램

주문과 할인 도메인 개발

주문서비스 구현체

  • 주문 구현체를 작성시 다음과 같이 인스턴스를 생성해주면 OCP, DIP 같은 객체지향 설계원칙을 준수하지 못한다.
  • DIP : 주문서비스 클라이언트(OrderServiceImpl)은 추상(인터페이스)뿐만 아닌 구체 클래스(FixDiscountPolicy)를 의존하고 있다.
  • OCP : 만약 할인 정책을 변경하거나 저장소를 변경할시 클라이언트(구현체)에 영향을 준다.(일일이 변경해줘야한다.)
public class OrderServiceImpl implements OrderService {
     private final MemberRepository memberRepository = new
    MemoryMemberRepository();
     private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 }

의존관계 분석

이상적인 의존관계

  • 구현체가 단순히 DiscountPolicy 인터페이스만을 의존한다.

실제 의존관계

  • 클라이언트(OrderServiceImpl)이 인터페이스(DiscountPolicy)뿐만 아닌 구체 클래스(FixDiscountPolicy)를 함께 의존하고 있다. -> 기획이 변경될때 구체 클래스까지 변경해줘야하므로 OCP위반

 

변경 예시

  • 다음과 같이 인터페이스에만 의존하도록 코드를 변경해야한다.
  • -> 누군가 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현객체를 대신 생성하고 주입해줘야한다.
  • -> Appconfig등장!!
public class OrderServiceImpl implements OrderService {
     //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
     private DiscountPolicy discountPolicy;
}

AppConfig 등장

  • 애플리케이션의 전체 동작방식을 구성(config)하기 위해, 구현객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들어줍니다.
  • 실제 동작에 필요한 구현객체를 생성
  • 생성자를 통해 주입해준다.
  • OrderServiceImpl -> MemoryMemberRepository , FixDiscountPolicy

 

AppConfig

  • 설계 변경으로 OrderServiceImpl은 MemoryMemberRepository와 FixDiscountPolicy를 의존하지 않는다.
  • 단지 인터페이스만 의존하고, 어떤 구현객체가 주입될지는 알수 없고 알 필요도 없다 단지 실행에만 집중하면 된다. 즉 비즈니스 로직상의 인터페이스의 구현객체가 다른 객체로 변경되어도 AppConfig만 수정하면 사용영역의 어떠한 코드변경없이 확장할수 있다.(OCR만족)
public class AppConfig {
     public MemberService memberService() {
     	return new MemberServiceImpl(new MemoryMemberRepository());
     }
     
     public OrderService orderService() {
         return new OrderServiceImpl(
         new MemoryMemberRepository(),
         new FixDiscountPolicy());
     }
}
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;
 	}
 }

 

AppConfig 리팩터링

리팩토링하기 전

  • 중복이 존재(MemoryMemberRepository)하고, 역할에 따른 구현(new ...)이 명확히 보이지 않음
public class AppConfig {
     public MemberService memberService() {
     	return new MemberServiceImpl(new MemoryMemberRepository());
     }
     
     public OrderService orderService() {
         return new OrderServiceImpl(
         new MemoryMemberRepository(),
         new FixDiscountPolicy());
     }
}

리팩토링 후

  • 중복을 제거(memberRepository()로 대체)하고, memberRepository로 묶음으로써 MemoryMemberRepository를 다른 구현체로 변경할때 한부분만 변경하면 된다.
  • 역할과 구현클래스가 명확히 보인다.
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();
     }
}

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

  • 구현체를 변경해도 구성영역(AppConfig)만 영향을 받고 사용영역은 전혀 영향받지 않는다.

IoC, DI, 그리고 컨테이너

제어의 역전 IoC

  • 기존 프로그램은 클라이언트 구현객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행하였다. 쉽게말하자면 필요한 클래스가 있으면 new를 통해 인스턴스를 생성해주고, 사용하듯 프로그램의 제어흐름을 스스로 조종했다.
  • 반면에 AppConfig가 등장한 이후에 구현객체(클라이언트)는 자신의 로직을 실행하는 역할만 담당하고, 그외의 제어흐름은 AppConfig가 가져간다. 예로들어 OrderServiceImpl에 필요한 인터페이스들을 호출하지만 어떤 구현체가 들어온지는 모르지만 자신의 로직을 이어나간다.
  • 이렇듯 프로그램의 제어흐름을 클라이언트가 직접 제어하는것이 아닌 외부(AppConfig)에서 관리하는것을 제어의 역전(IoC)라고 한다.

 

의존관계주입 DI

  • OrdereServiceImpl은 DiscountPolicy 인터페이스에 의존하지만, 실제 어떤 구현객체가 들어올지는 모른다.
  • 의존관계는 정적인 의존관계와 실행 시점에 결정되는 동적인 객체의존관계를 분리해서 생각해야한다.

정적인 클래스 의존관계

    • impor코드만 보고 의존관계를 쉽게 판단할수 있다. 
    • 하지만 실제로 어떤 구현객체가 사용되는지 구체적으로 알기는 힘들다.

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

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

DI정리

  • 애플리케이션 실행시점에 외부에서 실제 구현객체를 생성하고 클라이언트에 전달하여 서로 의존관계가 연결되는 것을 의존관계 주입이라 한다.
  • 객체 인스턴스를 생성하고 return을 통해 참조값을 전달하여 연결한다.
  • 의존관계주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할수 있다(AppConfig를 통해).
  • 의존관계 주입을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할수 있다.

DI 컨테이너

  • AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것
  • 스프링으로 전환하기
  • 지금까지 순수한 자바코드로 DI를 구현하였으니 스프링을 사용해보자

 

AppConfig 스프링 기반으로 변경

  • @Configuration : AppConfig에 설정을 구성해준다는 뜻
  • @Bean : 스프링 컨테이너에 스프링 빈으로 등록
@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();
    }
}
public class OrderApp {
    public static void main(String[] args) {
        // 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);
        long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);
        Order order = orderService.createOrder(memberId, "itemA", 10000);
        System.out.println("order = " + order);
    }
}

스프링 컨테이너

  • ApplicationContext를 스프링 컨테이너라 한다.
  • 기존의 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 DI를 한것과 달리, 이제는 스프링 컨테이너를 통해 사용
  • @Configuration이 붙은 AppConfig를 설정정보로 사용, @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록
  • 스프링 빈은 @Bean이 붙은 메서드 명을 스프링 빈의 이름으로 사용(memberService)
  • 필요한 객체를 이전에는 AppConfig를 통해 직접 조회했지만, 이제는 컨테이너를 통해 필요한 스프링빈을 찾아야한다.
  • applicationContext.getBean()을 통해 찾을수 있다.

 

참고강의

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

728x90
저작자표시 (새창열림)

'Spring' 카테고리의 다른 글

JPA Auditing기능이란?  (0) 2022.12.29
스프링 핵심 원리[기본편] - 의존관계 자동 주입  (0) 2022.12.28
스프링 핵심 원리[기본편] - 컴포넌트 스캔  (0) 2022.12.27
스프링 핵심 원리[기본편] - 싱글톤 컨테이너  (0) 2022.12.27
스프링 핵심 원리[기본편] - 스프링 컨테이너와 스프링 빈  (0) 2022.12.27
'Spring' 카테고리의 다른 글
  • 스프링 핵심 원리[기본편] - 의존관계 자동 주입
  • 스프링 핵심 원리[기본편] - 컴포넌트 스캔
  • 스프링 핵심 원리[기본편] - 싱글톤 컨테이너
  • 스프링 핵심 원리[기본편] - 스프링 컨테이너와 스프링 빈
study ticket
study ticket
  • study ticket
    혼자하는 공부
    study ticket
  • 전체
    오늘
    어제
    • 개발 (77)
      • 오류 (1)
      • Spring (13)
      • Java (0)
      • Data structure (6)
      • Algorithm (49)
        • 백준 (17)
        • 프로그래머스 (2)
      • 문제풀면서 알게되는것들 끄적 (2)
      • 머신러닝 (4)
        • sklearn (3)
        • pandas (1)
      • 프로젝트 (0)
        • 핏두 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준1157
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
study ticket
스프링 핵심 원리[기본편] - 예제만들기 , 객체 지향 원리 적용
상단으로

티스토리툴바