-
스프링 핵심 원리 이해2 - 객체 지향 원리 적용Spring/스프링 핵심 원리 - 기본편 2022. 8. 3. 14:39
[인프런] 스프링 핵심 원리 - 기본편
할인 정책 변경 요청 => 금액당 할인
RateDiscountPolicyTest
@DisplayName
• 클래스와 메소드에 사용 가능
• 테스트의 이름을 텍스트로 설정함
할인 정책을 변경하려면 OrderServiceImpl의 new 문의 코드를 고쳐야 함
📎문제점
OrderServiceImpl은 DiscountPolicy 추상 인터페이스에 의존하긴 하지만,
구현 클래스인 FixDiscountPolicy와 RateDiscountPolicy에도 의존하고 있음
=> DIP 위반 + OCP 위반
📎해결방법
인터페이스에만 의존하도록 변경하면 됨
private DiscountPolicy discountPolicy; // new 객체를 삭제 (인터페이스 문만 남김)
인터페이스로 객체를 만들었기 때문에 NullPointerException 발생
⭐누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해야 함
관심사의 분리
AppConfig의 등장
• 애플리케이션의 전체 동작 방식을 구성(config)하기 위해 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스 생성
• 생성한 객체 인스턴스의 참조를 생성자를 통해서 주입해줌
전에는 MemberServiceImpl에서 객체를 직접 만들었음
이제는 DIP를 지키는 방식으로 객체를 만들기 위해 AppConfig에서 객체를 만드는 것을 담당함
⭐ 이렇게 하면 결국 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확리히 분리됨(관심사의 분리)!!
main 메소드에 추가
AppConfig appConfig = new AppConfig(); MemberService memberService = appConfig.memberService(); //MemberService memberService = new MemberServiceImpl();
역할이 잘 드러나게 Refactoring해야 함 => 역할과 구현 클래스가 한 눈에 보임
📎memberRepository가 호출되면 memberRepository() 메소드에서 memberRepository를 생성하고 MemoryMemberRepository를 반환하는 방식
public class AppConfig { public MemberServiceImpl memberService() { return new MemberServiceImpl(memberRepository()); } public MemberRepository memberRepository() { return new MemoryMemberRepository(); } public OrderService orderService() { return new OrderServiceImpl(memberRepository(), discountPolicy()); } public DiscountPolicy discountPolicy() { return new FixDiscountPolicy(); } }
📎new MemoryMemberRepository() 의 중복이 memberRepository() 를 만들면서 제거됨
새로운 구조와 할인 정책 적용
IoC, DI, 컨테이너
📎제어의 역전 IoC(Inversion of Control)
프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 뜻 함
프로그램의 제어 흐름을 구현 객체가 아닌 AppConfig가 가져간 것
📎프레임워크 vs 라이브러리
프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크
내가 작성한 코드가 직접 제어의 흐름을 담당하면 라이브러리
📎의존관계 주입 DI(Dependency Injection)
의존관계는 정적인 클래스 의존관계와, 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계를 분리해서 생각해야함
*동적인 객체 인스턴스 의존 관계
: 애플리케이션 실행 시점에 실제 실행된 객체 인스턴스 참조가 연결된 의존 관계
의존관계 주입: 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것 (참조값을 전달해서 연결됨)
📎IoC 컨테이너, DI 컨테이너
AppConfig 같이 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC 컨테이너 혹은 DI 컨테이너라고 함
어샘블러, 오브젝트 팩토리 등으로 불리기도 함
스프링으로 전환하기
AppConfig 에 @Configuration 과 각 메소드마다 @Bean 어노테이션 추가
@Configuration public class AppConfig { @Bean public MemberServiceImpl memberService() { return new MemberServiceImpl(memberRepository()); } @Bean public MemberRepository memberRepository() { return new MemoryMemberRepository(); } @Bean public OrderService orderService() { return new OrderServiceImpl(memberRepository(), discountPolicy()); } @Bean public DiscountPolicy discountPolicy() { return new RateDiscountPolicy(); } }
@Configuration: 애플리케이션의 설정정보를 뜻함
@Bean: 메소드에 설정해주면 스프링 컨테이너에 등록됨
public class MemberApp { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); MemberService memberService = applicationContext.getBean("memberService", MemberService.class); ```
ApplicationContext: 스프링 컨테이너
applicationContext 객체: @Bean들을 전부 관리해줌
📎ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
getBean(찾을 객체 이름 = 보통 메소드 이름으로 설정됨, 타입)
실행 결과 @bean으로 등록한 메소드 => key는 memberService 같은 이름으로, value는 객체 인스턴스로 해서 스프링 컨테이너에 등록됨
📍결론
📎ApplicationContext를 스프링 컨테이너라고 함
📎스프링 컨테이너는 @Configuration이 붙은 AppConfig를 구성 정보로 사용
📎그때, @Bean이 적힌 메서드를 모두 스프링 컨테이너에 등록함 => 스프링 빈
📎스프링 빈은 @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용함
📎스프링 빈은 스프링 컨테이너를 통해서 찾아야 함 => applicationContext.getBean()
'Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
컴포넌트 스캔 (0) 2022.08.05 싱글톤 컨테이너 (0) 2022.08.04 스프링 컨테이너와 스프링 빈 (0) 2022.08.04 스프링 핵심 원리 이해1 - 예제 만들기 (0) 2022.08.03 객체 지향 설계와 스프링 (0) 2022.08.02