Spring의 강력한 무기
DI(Dependency Injection), IoC(Inversion of Control)
이전 글에서 스프링은 다음 기술로 다형성과 OCP/DIP를 가능하게 지원한다고 했다.
- DI(Dependency Injection): 의존관계, 의존성 주입
- DI 컨테이너 제공
기술의 적용
코드로 어떻게 DI라는 기술을 적용하는지 보자
기존의 코드를 변경하기 위해선 구현체에 의존하고 있기 때문에 Memory에서 Jdbc로 변경하기위해서는 클라이언트쪽 코드의 변경이 불가피한 상황
public interface MemberRepository{
...
}
public class MemoryMemberRepository implements MemberRepository {
...
}
public class JdbcMemberRepository implements MemberRepository {
...
}
public class MemberService {
// 기존코드
public class MemberService {
private MemberRepository memberRepository = new MemoryMemberRepository();
}
// 변경코드
public class MemberService {
// private MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();
}
그럼 어떻게 해야한다는 거야??
- 클라이언트 코드인 MemberService는 MemberRepository의 인터페이스 뿐 아니라 구체 클래스도함께 의존중이다. 그래서 구체 클래스를 변경할 때 클라이언트 코드도 함께 변경해야 한다.
- DIP 위반 -> 추상에만 의존하도록 변경해보자(인터페이스에만 의존)
public class MemberService {
private MemberRepository memberRepository;
}
인터페이스에만 의존하도록 변경했다. 그런데 구현체가 없는데 어떻게 코드를 실행될까??(지금 상태에 실행하면 NPE(null pointer exception)이 발생한다)
AppConfig의 등장
애플리케이션의 전체 동작 방식을 구성(config)하기위해 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스 생성
@Configuration
public class AppConfig{
@Bean
public MemberService memberService() {
return new MemberService(new MemoryMemberRepository());
}
}
- AppConfig에 설정을 구성한다는 뜻의 @Configuration을 붙임
- 메서드에 @Bean을 붙여준다. 이렇게 하면 스프링 컨테이너에 스프링 빈으로 등록한다. (스프링 컨테이너는 다음 글에서 알아보자.)
AppConfig에 실제 동작에 필요한 구현 객체를 생성
- MemoryMemberRepository
AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통하여 주입(연결)한다.
public class MemberService {
private MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
- 설계 변경으로 MemberService는 MemoryRepository를 의존하지 않고 MemberRepository라는 인터페이스에만 의존
- MemberService 입장에서 생성자를 통해 어떤 구현 객체가 들어올지(주입될지) 알 수 없다.
- MemberService의 생성자를 통해서 어떤 구현 객체를 주입할지는 외부(AppConfig)에서 결정
- MemberService는 이제 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 됨
- 객체의 생성과 연결은 AppConfig가 담당
- 클라이언트인 MemberService 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.
- AppConfig의 등장으로 애플리케이션의 크게 사용 영역과, 객체를 생성하고 구성(Configuration)하는 영역으로 분리되었다.
IoC, DI, 그리고 컨테이너
제어의 역전 IoC(Inversion of Control)
- 기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행. 한마디로 구현 객체가 프로그램의 제어 흐름을 스스로 조종했다.
- 반면에 AppConfig가 등장한 이후 구현 객체는 자신의 로직을 실행하는 역활만 담당하고 프로그램의 제어 흐름은 AppConfig가 담당한다.
- 이렇게 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라 한다.
IoC 컨테이너, DI 컨테이너
AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IOC 컨테이너 또는 Di 컨테이너라 한다.
프레임워크 vs 라이브러리
- 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크
- 반면에 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 라이브러리
참고: Spring Core
*틀린 부분이 있으면 언제든지 말씀해 주시면 공부해서 수정하겠습니다.