스프링은 POJO 프로그래밍 기법을 지향하고 있으며 POJO 프로그래밍을 위해서
IOC/DI , AOP, PSA세가지 기술을 지원하고 있다.
그 중 IOC/DI 스프링의 핵심 기술이자 중요한 개념이기에 다시 한번 정리하고자 한다.
이번 시간에는 IOC와 DI에 대해서 정리를 해보려고 한다.
- IOC : 제어의 역전이란 뜻인데, 평소 개발자가 Java 객체를 new를 통해 생성하여 개발자가 직접 관리하는게 아니라 spring framework에게 권한을 줌으로 객체를 Spring Container가 관리를 하는 것이다. 프레임워크로 제어의 객체 관리의 권한이 넘어감으로 이를 제어의 역전이라고 하는 것이다.
-> 객체를 스프링 컨테이너가 관리하는건 알겠는데..
그럼 개발자가 객체를 사용하려면 어떻게 해야해?
개발자가 객체를 사용하기 위해서 DI를 이용하는 것이다.
- DI : Framwork가 객체를 관리하면 개발자가 객체를 사용하기 위해 주입을 받는다. 외부로부터 개발자가 사용할 객체를 주입받고 주입을 해주는 애는 스프링 컨테이너이다.
상황은 주소를 인코딩 해야하는데 'Base64로 해주세요', 'UrlEncoder로 해주세요.'라고 요구사항이 달라질 수 있다. 그럼 요구사항이 확장되면 새로운 클래스를 만들고 Main 클래스의 코드를 또 수정해야 한다.
이럴 때 IEncoder 인터페이스를 만들어 역할을 만들어놓고 urlEncoder와 Base64Encoder 클래스가 구현을 한다.
Encoder 클래스에 IEncoder 인터페이스를 선언하고 외부로부터 개발자가 쓸 객체를 주입받기 위한 생성자를 선언한다.
public class Encoder {
private final IEncoder iEncoder;
//DI를 위한 생성자
public Encoder(IEncoder iEncoder){
this.iEncoder = iEncoder;
}
public String encode(String message){
return iEncoder.encode(message);
}
}
요구사항에 따라 요구사항에 맞는 IEncoder 인터페이스를 구현하는 클래스를 만들고 요구사항이 바뀔 때 마다 외부에서 사용할 객체를 주입하는 이것이 DI이다.
매우 유지보수에 용이하다는 것을 볼 수 있다.
public class Main {
public static void main(String[] args) {
String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
//외부에서 사용할 객체를 주입받는 것이 DI
Encoder encoder = new Encoder(new urlEncoder());
String result = encoder.encode(url);
System.out.println(result);
}
}
하지만 개발자가 직접 객체를 생성하고(new) Main 클래스에서 직접 객체를 주입하고 있다.
IOC는 개발자가 직접 관리하는게 아닌 스프링 프레임워크에서 직접 객체의 생명주기를 관리한다.
이를 어떻게 설정하는가?
스프링 컨테이너는 스프링 프로젝트 내에서 관리를 한다.
IEncoder를 구현하는 클래스이다. 이를 스프링에서 객체로 관리해달라는 요청이 필요하다.
public class Base64Encoder implements IEncoder{
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
@Component를 붙이게 되면 스프링 마크가 붙게 되면서 Bean으로 등록된다 라고 표시된다.
그럼 @SpringBootApplication 왼쪽 돋보기를 누르면 본인이 가지고 있는 빈이라 부르는 자바 객체가 나오게 된다.
@Component 왈 : '야 스프링아, 이 Base64 클래스는 스프링에서 관리를 좀 해줘' 라는 표시.
그럼 스프링이 실행되면 component 붙은 곳을 찾아서 직접 객체를 싱글톤 패턴으로 만들어서 스프링 컨테이너에서 관리한다.
Config 클래스에 정의해둔 MenuController 객체를 Spring의 도움을 받아서 CafeClient클래스에게 제공을 하고 있는 것이고 Spring이 의존 객체들을 주입해주기 때문에 애플리케이션 코드를 유연하게 구성할 수
참고
Bean : 스프링 컨테이너에서 관리되는 객체
@Configuration : 한개의 클래스에서 여러개의 빈을 등록
핵심 포인트
- 애플리케이션 흐름의 주도권이 사용자에게 있지 않고, Framework이나 서블릿 컨테이너 등 외부에 있는 것 즉, 흐름의 주도권이 뒤바뀐 것을 IoC(Inversion of Control)라고 한다.
- DI(Dependency Injection)는 IoC 개념을 조금 구체화 시킨 것으로 객체 간의 관계를 느슨하게 해준다.
- 클래스 내부에서 다른 클래스의 객체를 생성하게 되면 두 클래스 간에 의존 관계가 성립하게 된다.
- 클래스 내부에서 new를 사용해 참조할 클래스의 객체를 직접 생성하지 않고, 생성자 등을 통해 외부에서 다른 클래스의 객체를 전달 받고 있다면 의존성 주입이 이루어 지고 있는 것이다.
- new 키워드를 사용하여 객체를 생성할 때, 클래스 간에 강하게 결합(Tight Coupling)되어 있다고 한다.
- 어떤 클래스가 인터페이스 같이 일반화 된 구성 요소에 의존하고 있을 때, 클래스들 간에 느슨하게 결합(Loose Coupling)되어 있다고 한다.
- 객체들 간의 느슨한 결합은 요구 사항의 변경에 유연하게 대처할 수 있도록 해준다.
의존성 주입(DI)은 클래스들 간의 강한 결합을 느슨한 결합으로 만들어준다.- Spring에서는 애플리케이션 코드에서 이루어지는 의존성 주입(DI)을 Spring에서 대신 해준다.