핵심 키워드
IoC는 Inversion of Control. 즉, 제어의 역전이라는 뜻이다.
DI는 Dependency Injection, 즉, 의존성 주입이라는 뜻이다.
분리해서 이해해도 무방하지만, IoC와 DI모두 비슷한 의미를 가지고있어서 IoC 컨테이너 또는 DI 컨테이너라고 불린다.
요즘은 의존관계 주입에 초점을 맞추어 주로 DI 컨테이너라고 불린다.
둘의 공통적인 핵심 역할은 클라이언트의 코드 변경 없이도 기능이 확장이 가능하다는 특징이 있다.
이 둘을 이해하기 위해서는 아래 키워드들을 순차적으로 이해해야한다.
- Spring Container : 개발자가 작성한 코드를 스스로 참조한 뒤 알아서 객체의 생성과 소멸을 컨트롤 해줌
- Bean : 스프링 컨테이너에서 관리되는 객체
- DI : 외부에서 객체를 주입받음
- IoC : 스프링 컨테이너가 객체를 관리함
이 키워드들의 의미를 알고 IoC와 DI가 어떻게 작동되는지를 이해해보자.
DI를 이해하자
객체를 새로 만들어 Base64 Encoding와 URL Encoding을 해보자.
* Base 64 : 이진 데이터(예를 들어 실행 파일이나 Zip파일 등)를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자로 바꾸는 인코딩 방식
* URl Encoding : 퍼센트 인코딩(Percent-encoding)이라고도 불리며, URL에 문자를 표현하는 문자 인코딩 방법
String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
Encoder encoder = new Encoder(new Base64Encoder());
String result = encoder.encode(url);
Encoder urlEncoder = new Encoder(new UrlEncoder());
String urlResult = urlEncoder.encode(url);
System.out.println(result);
System.out.println(urlResult);
이 경우, 의존성을 개발자가 직접 만들어 쓴다고 볼 수 있다.
이렇게 코딩을 해서 프로그램이 돌아가지 않는 것은 아니다.
하지만 이 관계에서는 Encoder가 Base64Encoder와 UrlEncoder를 관리하게 된다.
즉, Encoder본인의 로직에 대한 책임 뿐 아니라 base64Encoder와 UrlEncoder의 구현에 대한 책임까지 지고 있다.
이 책임을 굳이 Encoder본인이 가질 필요가 있을까? 이렇게 될 경우 객체지향의 Single Responsibility Principle을 지킬 수 없게 된다. 이러한 논리에서 시작되어 의존 관계에 대한 책임을 제 3자에게 위임하는것이 IoC이다.
String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
Encoder encoder2 = context.getBean("base64Encode", Encoder.class);
String result = encoder2.encode(url);
System.out.println(result);
Encoder encoder = context.getBean("urlEncode", Encoder.class);
String result2 = encoder.encode(url);
System.out.println(result2);
위 코드에서 보면 스프링 context를 통해 객체들이 주입되었다. 이렇듯 외부에서 주입되는 객체들을 DI(Dependecny Injection)라고 한다.
IoC를 이해하자
스프링 컨테이너가 관리하는 객체를 Bean이라고 부른다. 스프링 컨테이너는 이러한 Bean들의 의존성을 관리하고, 객체를 만들어주며, 이렇게 만들어진 것들을 관리한다.
@Component
public class Encoder {
private IEncoder iEncoder;
public Encoder(@Qualifier("urlEncoder") IEncoder iEncoder){
this.iEncoder = iEncoder;
}
public String encode(String message){
return iEncoder.encode(message);
}
}
@Configuration // 한 개의 클래스에서 여러개의 빈을 등록
class AppConfig{
@Bean("base64Encode")
public Encoder encoder(Base64Encoder base64Encoder){
return new Encoder(base64Encoder);
}
@Bean("urlEncode")
public Encoder encoder(UrlEncoder urlEncoder){
return new Encoder(urlEncoder);
}
}
위 코드에서는 우리가 new객체와 엮어 흔히 사용해왔던 Getter, Setter 관련 메소드가 보이지 않는다.
대신에 아래 AppConfig를 보면 Bean(=객체)이 2개 있다.
이전에 new를 통해 생성했던 Encode객체는 Encode객체가 Base64Encode의 생성과 작동 로직에 대한 책임을 가지고 있었다.
하지만 위와 같이 IoC, DI를 통해 우리는 Single Responsibility Principle(SRP)을 지킬 수 있게 된다.
위 코드에서 보시다싶이, Base64Encoder class에 대한 변경이 발생하더라도, 이로인해 Encoder에 영향을 끼치지 않기 때문이다.
이렇게 스프링 컨테이너가 Bean의 의존성을 관리하고,. 객체를 만들어 주며, Bean을 등록해주고, 마지막으로 이렇게 만들어진 객체들을 관리한다. 개발자가 신경을 쓰지 않아도 Spring Framework가 알아서 해주는 것이다.
기존에는 개발자가 의존성을 만들고 관리했다면, 스프링 컨테이너는 알아서 객체를 관리해주는 모양이 된다. 이런 현상을 IoC(Inversion of Control), 즉, 제어의 역전이라고 부른다.
'👩🏻💻 Programming > SpringBoot' 카테고리의 다른 글
Bean vs Component (0) | 2021.08.10 |
---|---|
AOP (Aspect Oriented Programming) (0) | 2021.08.10 |
SpringBoot의 Object Mapper (0) | 2021.08.05 |
GET과 Query Parameter, POST와 Databody (0) | 2021.08.03 |
POJO(Plain Old Java Object)와 POJO Framework (0) | 2021.07.30 |