2022.01.05 - [TIL] - [2015-1-5]문제를 똑바로 읽자 / 스프링 핵심원리 기본편 AppConfig 구현
어제, 구현객체에서 다른 구현 객체를 생성, 연결하는 역할을 AppConfig 에게 위임하고, 구현 객체에서는 프로그램의 로직만을 담당하도록 리팩토링 했다. 이 과정에서 어떻게 OCP 가 지켜지는지, DIP가 어떻게 이루어질 수 있는지 확인했다.
제어의 역전
이처럼 프로그램의 제어 흐름을 AppConfig 처럼 외부에서 관리하는 것을 제어의 역전(IoC : Inversion of Controll) 라고 한다.
프레임워크 vs 라이브러리
프레임워크와 라이브러리의 차이에 비추어서도 생각해볼 수 있었다. AppConfig는 내가 직접 작성한 코드이지만, AppConfig 처럼 제어권을 내가 작성한 코드가 아닌 다른 무언가가 제어하고, 실행한다면 이를 프레임워크라고 한다. 반대로 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리 이다.
의존관계 주입(Dependency Injection : DI)
정적인 의존관계
정적인 의존관계는 프로그램이 실행되기전 코드만을 보고도 그 의존관계를 알 수 있다. '의존한다'라는 말이 처음에는 잘 와닫지 않았는데 '사용한다' 라는 말로 치환해서 이해해도 괜찮은 것 같다. 아래 코드처럼 클래스가 어떤 구현 객체를 사용하는지 아래와 같은 코드를 통해서 바로 확인할 수 있다.
private final MemberRepository memberRepository = new MemoryMemberRepository();
이미 객체는 MemoryMemberRepository 클래스에 의존한다.
하지만 아래와 같은 코드는 구현 클래스 MemberServiceImpl가 어떤 MemberRepository 의 구현객체를 의존하게 될지 실행 시점에 생성자를 통해 '외부에서' 구현 객체가 전달되면서 알 수 있다.
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
이처럼 런타임에 외부에서 구현객체를 생성하고 클라이언트에 전달하여 의존관계가 연결되는 것을 DI(Dependency Injection) 이라고 한다. DI를 사용하면 정적인 클래스의 의존관계를 수정하지 않고 의존관계를 쉽게 변경할 수 있다.
스프링 프레임워크
드디어 순수한 자바가 아닌 스프링 프레임워크를 사용하는 것을 배웠다. 기존 코드에서 아주 간단한 어노테이션만 추가했는데 다음과 같다.
@Configuration
public class AppConfig {
@Bean
public MemberService 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();
}
}
이처럼 AppConfig 클래스 위에 @Configuration 을 작성하고 메소드들 마다 @Bean 어노테이션을 작성한다.
그 다음 아래 코드처럼 실행시킬 수 있었다.(주석부분은 기존 코드)
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new Member = " + member.getName());
System.out.println("findMember = " + findMember.getName() );
ApplicationContext 에 AppConfig 의 Class 객체를 전달하여 객체를 생성한다.
이 ApplicationContext가 바로 스프링 컨테이너이다. AnnotationConfigApplicationContext는 ApplicationContext를 상속받은 구현체로, 이름에서도 알 수 있듯이 어노테이션을 통해 스프링 컨테이너를 구현해놓은 클래스이다. 이렇게 객체를 생성하면 applicationContext 속에 AppConfig에서 작성한 메소드들이 반환하는 객체를 getBean 메소드들을 통해 얻을 수 있고 이러한 객체를 Bean이라고 한다. getBean()의 첫번째 파라미터에는 빈의 이름으로 AppConfig에 작성한 메소드 명을 기준으로 결정된다. 두번째 매개변수는 반환 받을 타입을 의미한다.
이 렇게 코드 두줄을 수정하고 실행하면 다음과 같은 결과가 나온다.
아래 두줄은 원래 출력값과 일치하지만 그 위에 11줄의 알수없는 로그가 출력되었다. 이는 지금까지 순수자바를 통해 개발 해오다가 스프링 프레임워크를 사용하면서 생기는 코드들로, 위에서 설명한 Bean을 생성함을 알려주는 것이다. 아래 다섯줄 이외에는 스프링이 필요한 Bean을 등록한 것이고 아래 다섯줄은 AppConfig와 객체들이 메소드의 이름으로 컨테이너에 생성되는 것을 보여준다.
결론 및 느낀점
객체지향을 공부하면서 DIP, DI, IoC, OCP와 같은 것들이 다 연결되어있고 비슷한 것에 대해 관점을 조금씩 바꿔서 말하고 있는 것 같다는 느낌을 받았다. 그만큼 중요하다는 의미도 되지만 처음 공부할 때는 얘는 이거고 저거는 저거다라고 딱 구분이 머리속으로 쉽게되지 않었었다. 지금은 공부를 진행하면서 그렇게 흩어져있던 생각과 지식들이 퍼즐처럼 점점 맞춰지고 있다는 느낌이 든다. 조급해 하지말고 천천히, 하지만 꾸준히 공부해서 꼭 내 것으로 만들어야지.
그리고 오늘 왠지 집에오니까 집중이 너무안돼서 TIL을 온전히 내가 생각하고 느낀대로 만족할만큼 쓰지 못한것 같다. 나태해지말고 정신 똑바로 차려야겠다.
'TIL' 카테고리의 다른 글
[2022-1-8] 스프링 핵심 원리 - 기본편 : 스프링 컨테이너와 스프링 빈 (1/2) / 프로그래머스 연습문제 - 폰켓몬(Set, HashSet) (0) | 2022.01.09 |
---|---|
[2022-1-7] 프로그래머스 연습 문제 - 소수 만들기(조합, DFS 응용) (0) | 2022.01.08 |
[2022-1-5]문제를 똑바로 읽자 / 스프링 핵심원리 기본편 AppConfig 구현 (0) | 2022.01.05 |
[2022-1-4] Stack<E> 뜯어보기 (2) | 2022.01.05 |
[2022-1-3]프로그래머스 연습 문제 리팩토링 (0) | 2022.01.04 |