스프링 핵심 원리 - 기본 3] 객체 지향을 적용한 Spring 프로젝트
해당 포스팅은 강좌를 보고 요약하고 실습을 따라가는 글이 적혀있습니다.
[스프링 핵심 원리 - 기본편 : 김영한] : 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/dashboard
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요! 📣 확인해주
www.inflearn.com
새로운 할인 정책
이전 프로젝트에 새로운 할인 정책을 확장해보자
새로운 할인 정책은 10%가 깎이는 것으로 바뀌었다.
현재 프로젝트에서 DiscountPolicy 인터페이스를 구현하는 구현체를 하나 더 만들면 된다.
- RateDiscountPolicy.java
- OrderServiceImpl.java
그럼 여기서 discountPolicy를 RateDiscountPolicy()로 바꾸기만 하면 이건 좋은 코드일까?
정답은 '아니다'이다.
실제 코드를 보면, OrderServiceImpl은 추상적인 것에도 의존하고 있고, 구체적인 것에도 의존하고 있다.
이는 DIP를 위반한 것이다.
그래서 Fix -> Rate로 변경하는 순간, OrderServiceImpl의 소스코드도 함께 변경해야 한다.
또 이는 OCP를 위반한 것이다.
항상 "인터페이스에만 의존하도록 의존 관계를 만들어야"한다.
그렇다면 누가 대신 구현체를 OrderServiceImpl에 주입해주어야한다.
관심사의 분리
인터페이스는 자신이 하기로 했던 일만 해야한다.
즉, 이건 배우가 자신이 연기도 하고, 상대배역도 초빙해야하는 "다양한 책임"을 가져야하는 것이다.
"관심사를 분리하자"
- 배우는 역할인 배역을 수행하는 것에만 집중해야 함
- 배우는 어떤 상대가 오더라도 똑같이 공연을 할 수 있어야 함
- 공연을 구성하고, 섭외하고 등의 별도의 일은 "공연 기획자"가 나올 시점
그러니, 관심사를 확연히 분리시켜야한다.
AppConfig 등장
어플리케이션의 전체 동작 방식을 구성하기 위해 "구현 객체를 생성"하고, "연결"하는 책임을 가지는 별도의 설정 클래스가 필요하다.
- AppConfig.java
AppConfig에서 어떤 MemberRepository를 사용할 지 생성자를 통해 지정해주고,
- MemberServiceImpl.java
MemberService에서는 생성자로 초기화만 해주며 값을 넣어주면 된다.
이런식으로 하면 DIP를 절대적으로 지키게 되며,
나머지 Service도 똑같이 해주면 된다.
Implements에서는 생성자로 주입을 해주면 된다.
ServiceImpl는 무엇이 할당받을지는 모르지만 생성자로 값을 받아오는 방식으로 구현된다.
그럼 이제 DIP를 지키게 된다.
이제부터 각 Impl은 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 된다.
이제 구조는 위와 같이 바쀠게 된다.
상세한 건 AppConfig가 모두 설정하게 된다.
"관심사의 분리" : 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.
이러한 의존관계 주입을 DI(Dependency Injection)라고 한다.
AppConfig 구조 refactoring
어느정도 고쳐지기는 했지만, 아직까지 설정정보가 한눈에 보이지는 않는다.
이를 고쳐주기 위해,
이제 역할과 구현의 정보가 한눈에 보인다.
상세 구현 클래스가 바뀌면 memberRepository()와 discountPolicy() 함수 내부만 바꾸어주면 된다.
IoC, DI, 컨테이너
1. IoC(Inversion of Control)
AppConfig 등장 전 : 클라이언트 구현 객체가 스스로 서버 구현 객체를 생성, 연결, 실행
AppConfig 등장 후 : 클라이언트 구현 객체는 자신의 로직을 실행하는 역할만 담당
프로그램 제어 흐름은 AppConfig가
이렇게 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)라고 한다.
**
프레임워크 vs 라이브러리
- 프레임워크 : 내가 작성한 코드를 대신 제어 + 대신 실행 (ex. JUnit)
- 라이브러리 : 내가 작성한 코드가 제어의 흐름을 담당 (ex. java객체를 xml로 바꾸기)
2. DI (Dependency Injection, 의존관계 주입)
AppConfig를 통해 이제는 OrderServiceImpl이 MemberRepository와 DiscountPolicy 인터페이스들만 의존하게 된다.
어떤 구현 객체가 사용될지는 모른다.
의존관계는 "정적인 클래스 의존 관계"와 "실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계"를 분리해서 생각해야한다.
> 정적인 클래스 의존 관계
실행하지 않아도 어떤 인터페이스를 의존하고 있는지 분석할 수 있다.
> 동적인 객체 의존 관계
어플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계이다.
3. IoC 컨테이너, DI 컨테이너
지금의 AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것
의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다.
스프링으로 전환
지금까지 순수한 자바 코드만으로 어플리케이션을 구현한 것을 스프링으로 전환해본다.
> AppConfig.java
Spring에서는 설정 정보에서 @Configuration를 붙여 설명해준다.
그리고 각 메서드에서 @Bean을 적어준다.
이제 MemberApp에서 Spring으로 전환해준다.
>MemberApp.java
ApplicationContext가 이제 모든 컨테이너들을 관리해준다.
파라미터로 AppConfig.class를 넣어주면 AppConfig에서 지정해준 메소드들을 컨테이너로 만들어준다.
getBean()을 사용하면 꺼내올 객체를 메소드 이름과 반환 타입 파라미터를 통해 가져올 수 있다.
실행을 해보면, 기존과 다른 로그들이 나온다.
우리가 @Bean으로 지정해준 메소드들이 컨테이너로 등록이 된다.
> OrderApp.java
OrderApp도 똑같이 호출해준다.
** 스프링 컨테이너
- ApplicationContext를 스프링 컨테이너라고 한다.
- 기존에는 AppConfig를 사용하여 객체를 생성하고 DI를 했지만, 이제부터는 스프링 컨테이너를 통해서 사용한다.
- 스프링 컨테이너는 @Configuration이 붙은 AppConfig를 생성(구성) 정보로 사용한다.
- 여기서 @Bean이라 적힌 메서드를 모두 호출하여 반환된 객체(스프링 빈)를 스프링 컨테이너에 등록한다.
- 스프링 빈은 @Bean이 붙은 메서드 명을 스프링 빈의 이름으로 사용한다.
- 필요한 객체는 applicationContext.getBean()을 통해 찾아올 수 있다.
** 코드는 복잡해졌지만 장점은 분명히 있다. 앞으로 알아볼 것