오늘은 아침에 일찍 나가야 했어서, 늦게 자고 일찍 일어나는 수명단축 어른이가 되었음에도 그저 아침에 공부시간을 낼 수는 없었습니다. 오늘이 지나가려고 하니 급하네요...
오늘의 학습내용은 스프링 살짝더보기의 01~03 영상입니다.
DI와 IoC 는 제가 처음으로 입사했을 때 1~2년차 사이에 궁금해서 공부를 따로 했던 내용입니다. 비록 그게 스프링은 아니지만요. 제품에 ninject 라는 C# DI 컨테이너도 넣어봤구요... 이게 대체 뭘까를 이해하게 되니 만들라면 간단한 것 정도는 만들수도 있을 것 같습니다.
스프링의 핵심에는 IoC 의 역할을 해주는 DI 컨테이너가 있습니다. 어... 이 부분은 Pluralsight 에서 깨작깨작 스프링을 공부했을 때 배웠던 내용이네요.
DI - 밖에서 필요한 객체를 쑤셔넣어주다
프로그램이 커지면 커질수록 특정 클래스(Base64Encoder)를 딱 지정해서 멤버로 has-a 관계로 가지게 되면, 테스트를 하려고 봤더니 그 클래스도 같이 테스트해야 하는 일이 벌어집니다.
class MyClass {
Base64Encoder encoder = new Base64Encoder;
}
이런 부분은 외부에서 받아오도록 수정하면 먼저 필드로 가지고 있어서 전혀 분리가 안 된다는 참사는 벗어날 수 있습니다.
class MyClass {
Base64Encoder encoder = null; // 불필요하지만 이해를 돕기 위해
MyClass(Base64Encoder encoder) {
this.encoder = encoder;
}
}
하지만 이렇게 한다고 "Base64Encoder" 라는 클래스가 필요하다는 사실은 바뀌지 않죠. 그렇다면 필요한 기능만 뽑아놓은 추상 부모 클래스로 바꾸면 어떨까요?
class MyClass {
AbstractEncoder encoder = null;
MyClass(AbstractEncoder encoder) { // AbstractEncoder로 변경
this.encoder = encoder;
}
}
psvm {
MyClass myclass = new MyClass(new Base64Encoder());
}
공통 기능만 뺀 추상 클래스라고 해도 코드가 들어있다면 거기에 의존하게 된다면, Base64Encoder 를 테스트할 때 다른 걸로 갈아치우겠다는 야망이 말짱 도루묵이 되어버립니다. 추상 부모 클래스를 여전히 의존하니까요. 추상 부모 클래스 대신에 인터페이스를 쓰면 완벽합니다.
class MyClass {
IEncoder encoder = null;
MyClass(IEncoder encoder) { // IEncoder로 변경, 인터페이스에는 의존대상이 될 수 있는 함수 본문이 없음
this.encoder = encoder;
}
}
psvm {
MyClass myclass = new MyClass(new Base64Encoder());
}
이제 테스트를 할 때에는 가짜 인코더를 만들어서 넣어주면 됩니다.
class FakeEncoder extends IEncoder { ... }
psvm {
MyClass myclass = new MyClass(new FakeEncoder());
}
IoC - 의존성의 선언적 정의
자, 다시 한 번 보세요, 이제 생성자만 보면 이 객체가 뭘 필요로 하는지 인터페이스 목록을 알 수 있어요.
그러면 그 인터페이스에 필요한 객체를 모두 아는 어떤 게임마스터가 있다면? 이 마스터 객체가 알아서 MyClass를 만들 때 필요한 객체들을 인터페이스를 보고 찾아와서 넣어줄 수 있을겁니다. 리플렉션과 어노테이션을 활용해서요. (언어마다 다르겠지만 말이죠.)
객체를 만들기 위해 필요한 게 뭔지 다 미리 정의되어 있으면 우리가 직접 new를 호출해줄 필요가 없습니다. 그걸 다 아는 게임마스터, 그러니까 DI 컨테이너가 대신 객체를 만들어줄테니까요. 이렇게 객체를 개발자가 직접 만드는 것에서 프레임워크에 해당하는 DI 컨테이너가 객체를 만들어주는 것으로 객체를 만드는 주체가 옮겨가기 때문에, 제어가 (제어흐름이) 역전되었다고 합니다. 그래서 IoC 인거죠.
psvm {
// 선언적 정의
게임마스터야.(IEncoder 가 필요한데에는 Base64Encoder 를 만들어서 넣어줘);
게임마스터야.(MyClass 생성자를 보고 필요한 게 뭐가 있는지 기억해줘);
// 객체 생성
MyClass class = 게임마스터야.<나 MyClass 필요한데 만들어줘>();
}
"스프링 DI 컨테이너" 의 사용방법
스프링은 어노테이션에 크게 의존하는 경향이 있는 거 같습니다. 어노테이션만 익혀도 반은 익혔다고 볼 수 있는 거 같아요.
각설하고.
- @Component - DI 의 대상이 되는 클래스에 붙입니다.
- @SpringBootApplication - 스프링의 본질 중 하나는 DI 컨테이너입니다. 그러니까 메인 앱에 이걸 붙여줘야 하죠
- class ApplicationContext - "앱의 문맥 내에서..." 즉, DI 컨테이너입니다. getBean(클래스이름.class) 로 객체를 받아올 수 있음
- 강의의 내용에서 유추하자면, Bean 이라고 불리는 건 DI 관점에서 "스프링 DI 컨테이너"가 관리하고 만들어주는 객체입니다. JavaBeans와는 미묘하게 약간은 다른 개념인듯 (원래는 같은 개념이었는데 시대가 지나면서 바뀐건지?)
- DI 방법 3가지
- 생성자에 넣어주세요 (기본, 권장, 딴데서도 다 이 방법이고 명시적임. 레거시 아니면 이거 하세요.)
- 필드에 넣어주세요 (PluralSight 강의에 의하면 어노테이션을 썼던듯)
- setMethod 로 넣어주세요 (저는 싫어요 ㅎㅎ..)
- 같은 인터페이스를 상속받는 두 개 이상의 Bean 을 같이 쓰려면 @Configuration 을 설정용 클래스에 붙여 각각의 @Bean 을 정의합니다.
-
@Configuration class AppConfig { @Bean("base64Encoder") public Encoder encoder(Base64Encoder b64encoder) { ... new Encoder(b64encoder) } @Bean("fakeEncoder") public Encoder encoder(생략) { ... } } 예시가 살짝 부적절해보이는데...?
-
- 아니면 생성자의 패러미터 앞부분에 이전에 리퀘스트에서 했던거처럼 @Qualifier("bean이름인데 자동 이름은 앞에께 소문자가된 camel임") 을 붙이던지요.
본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC
#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online
시간이 있었다면 좀 빠르게 보고 건너뛰고 더 봤을텐데... AOP는 학생 때 국비지원 교육에서 들은 거 밖에 모른단말이에요...
나 잘거야
'FastCampus - 한번에 끝내는 Java|Spring 웹 개발 > 03 스프링 입문' 카테고리의 다른 글
스프링 Ch 5 스프링 좀 더 - AOP (2) - 패스트캠퍼스 챌린지 6일차 (0) | 2022.01.29 |
---|---|
스프링 Ch 5 스프링 좀 더 - AOP (1) - 패스트캠퍼스 챌린지 5일차 (0) | 2022.01.28 |
스프링 Ch 04 스프링 부트 시작하기 - 패스트캠퍼스 챌린지 3일차 (0) | 2022.01.26 |
스프링 Ch03 웹 개발 개론 4 (2회차) - 패스트캠퍼스 챌린지 2일차 (0) | 2022.01.25 |
스프링 Ch03 웹 개발 개론 3 (2회차) - 패스트캠퍼스 챌린지 1일차 (0) | 2022.01.24 |