IoC(Inversion Of Control)
스프링에서 일반적인 Java 객체를 new로 생성하여 개발자가 관리하는 것이 아닌 Spring Container에게 맡긴다.
즉, 개발자에서 프레임워크로 제어의 객체관리 권한이 넘어갔으므로 "제어의 역전"이라고 한다.
DI(Dependency Injection)
장점
- 의존성으로부터 격리시켜 테스트에 용이하다
- 코드를 확장하거나 변경할 때 영향을 최소화한다(추상화)
- 순환참조를 막을 수 있다.
순수히 객체를 생성하는 자바의 경우를 확인하자
Base64 와 Url 인코더 2개를 만들고 상황에 따라 적용하도록 코드를 짜본다.
1. Base64Encoder
public class Base64Encoder {
public String encode(String message) {
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
2.UrlEncoder
public class UrlEncoder {
public String encode(String message) {
try {
return UrlEncoder.encode(message, "UTF-8");
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
* 객체 생성 확인
public static void main(String[] args) {
String url ="www.naver.com";
Base64Encoder encoder = new Base64Encoder();
String result = encoder.encode(url);
UrlEncoder urlEncoder = new UrlEncoder();
String urlResult = urlEncoder.encoder(url);
}
마찬가지로 객체를 생성하고 원하는 결과값을 얻었다.
하지만, 이렇게 새로운 Encoder가 나올때마다 직접 객체를 무한정 생성해주어야 하는가?
공통된 부분은 interface에서 묶어서 관리하도록 변경한다.
public interface IEncoder {
String encode(String message);
}
* interface 상속으로 변경하기
public class UrlEncoder implements IEncoder {
public String encode(String message) {
try {
return UrlEncoder.encode(message, "UTF-8");
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
public class Base64Encoder implements IEncoder {
public String encode(String message) {
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
또한 IEncoder를 변수로 가지고 실제 인코딩을 진행하는 Encoder 클래스를 만든다.
특히, 이 Encoder 클래스는 기본적으로 Base64 인코딩 형식을 따른다.
public class Encoder {
private IEncoder iEncoder;
public Encoder() {
this.iEncoder = new Base64Encoder();
}
public String encode(String message) {
return iEncoder.encode(message);
}
}
public static void main(String[] args) {
String url ="www.naver.com";
Encoder encoder = new Encoder();
String result = encoder.encode(url) // 생성자에 정의 된 Base64 인코딩을 한다.
}
한가지 더 나아가. 기존에는 Base64 인코딩을 기본으로 설정했지만, Url 인코딩을 하고 싶은 경우는?
public class Encoder {
...
public Encoder() {
this.iEncoder = new Base64Encoder();
this.iEncoder = new UrlEncoder();
}
...
}
계속 Encoder 클래스의 생성자 안에서 Base64와 Url 인코딩을 번갈아 가면서 수정해야하는 불편함이 있다.
하지만, DI를 쓰면 Encoder 클래스를 건드리지 않고도 자유자재로 변경할 수 있다. 생성자에 IEncoder를 매개변수로 두어 내가 실제 사용할 때 IEncoder를 구현한 하나의 클래스를 선택해서 넣어주면 그 클래스의 방식으로 인코딩이 진행된다. 즉, Encoder 내부를 변경하지 않고, 인코딩을 할 수 있다.
public class Encoder {
private IEncoder iEncoder;
public Encoder(IEncoder iEncoder) {
this.iEncoder = iEncoder;
}
public String encode(String message) {
return iEncoder.encode(message);
}
}
public static void main(String[] args) {
String url ="www.naver.com";
Encoder encoder = new Encoder(new Base64Encoder());
String result = encoder.encode(url); // 매개변수에 넣은대로 Base64 인코더 진행
Encoder encoder2 = new Encoder(new UrlEncoder());
String result = encoder2.encode(url); // 매개변수에 넣은대로 Url 인코더 진행
}
이제, Encoder 클래스를 변경하지 않고 단순히 매개변수에 따라서 인코딩 방식이 바뀐다. 외부에서 주입받은대로 따라서 작동한다. 또한 새로운 Encoder를 만들어서 적용하고 싶다면, Encoder 클래스를 변경할 필요없이 주입시켜주면 된다.
의존성 주입에는 성공했으니 이제, Spring에서 객체를 관리하도록 해주어야 한다. 따라서 각 클래스 상단에 @Component를 붙여주면 스프링이 실행될 때 ApplicationContext에서 해당 클래스들을 객체로 관리한다.
@Component
public class UrlEncoder implements IEncoder {
public String encode(String message) {
try {
return UrlEncoder.encode(message, "UTF-8");
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
@Component
public class Base64Encoder implements IEncoder {
public String encode(String message) {
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
Encoder 클래스의 경우 에러가 난다. iEncoder는 Base64Encoder와 UrlEncoder 2개를 가지고 있기 때문에, Spring에서 어떤 클래스를 매칭해주어야 하는지 정하지 못한다.
@Component // 에러!
public class Encoder {
private IEncoder iEncoder;
public Encoder(IEncoder iEncoder) { //해당 부분 에러!
this.iEncoder = iEncoder;
}
public String encode(String message) {
return iEncoder.encode(message);
}
}
따라서, IEncoder 중에서 어떤 것을 사용할지 @Qualifier를 통해서 명시해주어야 한다. 보통 클래스 위에 @Component를 붙이면, 클래스명 이름 그대로 객체로 등록된다. 아래는 Base64Encoder를 사용하도록 만든 경우이다.
public Encoder(@Qualifier("Base64Encoder") IEncoder iEncoder) { //해당 부분 에러!
this.iEncoder = iEncoder;
}
하지만, Base64Encoder와 UrlEncoder 2개 모두 등록하고 싶은 경우에는 @Configuration을 사용한다.
@Configuration
public AppConfig {
@Bean("Base64Encode")
public Encoder encoder(Base64Encoder base64Encoder) {
return new Encoder(base64Encoder);
}
@Bean("UrlEncode")
public Encoder encoder(UrlEncoder urlEncoder) {
return new Encoder(urlEncoder);
}
}
'학습 > Java' 카테고리의 다른 글
AOP (0) | 2021.08.11 |
---|---|
Spring MVC (0) | 2021.08.08 |
ObjectMapper (0) | 2021.08.06 |
HTTP vs. WWW (0) | 2021.08.05 |
프록시 패턴 (0) | 2021.08.03 |