목표

1.  예제 프로젝트에 객체 지향 원리를 적용한다.
2. IoC, DI, 그리고 컨테이너를 배우고 순수 자바코드만 사용했던 프로젝트를 이제 스프링으로 전환한다. 

'객체 지향 원리 적용' 목차

1. 새로운 할인 정책 개발

2. 새로운 할인 정책 적용과 문제점

3. 관심사의 분리

4. AppConfig 리팩터링 

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

6. 전체 흐름 정리 

7. 좋은 객체 지향 설계의 5가지 원칙의 적용

8. IoC, DI, 그리고 컨테이너 (이번 포스팅)

9. 스프링으로 전환하기

 


8. IoC, DI, 그리고 컨테이너 

제어의 역전 IoC (Inversion of Control)

 

1) 기존 프로그램 : 구현 객체가 스스로 제어 흐름을 조종

기존 프로그램은 구현 객체가 스스로 필요한 객체를 생성하고, 연결하고, 실행했다. 

 

2) AppConfig가 외부에서 프로그램 동작 방식을 제어

반면에 AppConfig를 도입한 후에는 구현 객체는 자신의 로직을 실행하는 역할만 담당했다. 

프로그램을 제어하는건 전부 AppConfig가 맡는다. 어떤 인터페이스에 어떤 구현체를 선택할지 정해준다. 

예를 들어, OrderServiceImpl은 필요한 인터페이스를 호출할 때 어떤 구현 객체가 실행될지는 모른다. 몰라도 자신의 로직 실행에 아무런 영향을 받지 않는다. 

 

이렇게 프로그램의 제어 흐름을 구현체들이 직접 제어하는게 아니라 외부에서 관리하는 것을 제어의 역전이라고 한다. 

 

프레임워크 vs 라이브러리

프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크가 맞다. (JUnit) 

JUnit 테스트 프레임워크는 자신만의 라이프사이클이 있다. 순서대로 테스트를 진행하되, 내가 작성한 테스트를 그 안에 집어넣어서 실행시켜주는 것이다. 

반면 내가 작성한 코드가 직접 제어의 흐름을 담당한다면, 그것은 라이브러리다.

 

의존관계 주입 DI(Dependency Injection)

구현체는 인터페이스에 의존한다. OrderServiceImpl은 DiscountPolicy 인터페이스에만 의존한다. 

OrderServiceImpl 구현체는 기능을 호출하면서도 인터페이스를 통해 실제 어떤 구현체가 사용될지는 모른다. 

의존관계는 정적인 클래스 의존관계와 실행 시점에 결정되는 동적인 객체(인스턴스)의존관계 2가지를 분리해서 생각해야 한다. 

 

정적인 클래스 의존관계

애플리케이션을 실행하지 않아도, 클래스가 사용하는 import 코드만 보고 의존관계를 분석할 수 있다. 

FixDiscountPolicy 와 RateDiscountPolicy 클래스는 DiscountPolicy 인터페이스에 의존한다. 

OrderServiceImpl 구현체는 MemberRepository 인터페이스와 DiscountPolicy 인터페이스에 의존한다. 

화살표 방향으로 -> 의존관계를 표현하고 있다.

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

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

외부에서 실제 구현체를 생성하고 연결해주는 것을 의존관계 주입이라 한다. (AppConfig가 해준다)

 

의존관계 주입의 효과 

클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 인스턴스를 변경할 수 있다. 외부에서 구현체를 정해주기 때문이다. 

정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스를 쉽게 변경할 수 있다. (중요하니까 반복한다)

IoC컨테이너, DI 컨테이너 

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC컨테이너 또는 DI컨테이너라 한다. 

 


9. 스프링으로 전환하기 

지금까지 순수 자바코드만으로 DI를 적용했다. 이제 스프링으로 전환해보자. 코드 부터 작성하자. 스프링으로 전환한 코드 private repo

 

1) 스프링 컨테이너에 등록하기

AppConfig에 '구성 정보'를 의미하는 @Configuration 애노테이션을 붙인다.

DI 하는 메서드에 @Bean애노테이션을 붙인다. 

2) ApplicationContext에 Bean을 등록 

테스트를 위해 작성했던 MemberApp를 열어보자. 

기존에는 AppConfig 에서 직접 필요한 객체를 꺼냈었다. 이제 스프링을 쓰자. 

스프링은 ApplicationContext 로 시작한다. 이것이 객체(Bean)을 관리해주는 스프링 컨테이너다. 

AppConfig.class 를 파라미터로 넘겨서 ApplicationContext를 생성하자. 

AppConfig에 있는 설정 정보를 가지고 Bean을 스프링 컨테이너에 넣고 관리해준다. 

객체(Bean)의 이름은 각각의 메서드 이름으로 붙여진다. ex) memberService 빈, memberRepository 빈

 

memberService 객체가 필요할 때 AppConfig에서 꺼내지 않고, 스프링 ApplicationContext에서 꺼낸다. 

스프링 컨테이너에 빈으로 등록된 인스턴스가 로그에 뜬다! 

3) OrderApp도 스프링으로 바꿔보자. 

AppConfig.class 를 파라미터로 넘겨서 ApplicationContext를 생성하자. 

ApplicationContext 에서 memberService와 orderService를 꺼낸다. 

4) 스프링 컨테이너 (== DI 컨테이너) 

  • ApplicationContext를 스프링 컨테이너라 한다. 
  • 기존에는 개발자가 AppConfig 에 객체를 생성하고 직접 DI를 구현했다. 
  • 이제 스프링 컨테이너가  @Configuration 애노테이션이 붙은 AppConfig를 설정(구성)정보로 사용한다. 여기서 @Bean이 붙은 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 모두 등록한다. 이렇게 스프링 컨테이너에 등록된 메서드를 스프링 빈이라고 한다. 
  • 스프링 빈은  @Bean애노테이션이 붙은 메서드 이름을 스프링 빈의 이름으로 사용한다. 
  • 스프링 빈은 applicationContext.getBean() 메서드로 찾을 수 있다. 

 

코드가 더 복잡해진 것 같은데. 스프링 컨테이너를 사용하면 어떤 장점이 있을까 ? 다음 시간에 장점을 배우자.


다음 강의에서는 '스프링 컨테이너 생성 과정'을 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

+ Recent posts