2023. 9. 9. 16:38ㆍ학부 강의/웹프로그래밍 (Spring)
0. 참고 자료
아직 배우고 있는 중이라 부정확한 정보가 포함되어 있을 수 있습니다!
주의하세요!
올인원 스프링 프레임워크 참고.
1. 의존성 주입 (DI)
가. 의존성 (Dependency)
MyCalculator
내부에 CalAdd
를 생성한다.
MyCalculator
는 CalAdd
을 “이용”한다.
MyCalculator
는 자신이 직접 연산하지 않고, 각각의 연산 객체들(CalAdd
, CalSub
, …)에게 위임한다.
이는 곧 “MyCalculator
가 CalAdd
에 의존한다”라고 할 수 있다.
CalAdd
가 변하면 MyCalculator
에 영향을 미친다.
나. 의존성 주입 (DI)
- 의존성 주입 (DI, Dependency Injection)
: 필요한 (의존하는) 객체를 직접 생성하지 않고 외부에서 주입하는 방식.
: Dependency Injector가 간접적으로 의존성을 주입하는 방식.
MyCalculator
가 의존하는 CalAdd
, CalSub
, CalMul
, CalDiv
를 외부에서 주입하는 방식으로 변경했다.
매개변수를 이용해서 외부에서 전달받았다.
다. 인터페이스를 활용하도록 수정
package calc_1;
public class MyCalculator {
public void calAdd(int fNum, int sNum, CalAdd calAdd){
//ICalculator calculator = new CalAdd();
int value = calAdd.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calSub(int fNum, int sNum, CalSub calSub){
//ICalculator calculator = new CalSub();
int value = calSub.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calMul(int fNum, int sNum, CalMul calMul){
//ICalculator calculator = new CalMul();
int value = calMul.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calDiv(int fNum, int sNum, CalDiv calDiv){
//ICalculator calculator = new CalDiv();
int value = calDiv.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
}
중복되는 코드를 인터페이스를 활용해서 제거한다.
package calc_1;
public class MyCalculator {
public void calculate(int fNum, int sNum, ICalculator calculator){
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
}
이제 MyCalculator
안에는 각각의 연산 객체들(CalAdd
, CalSub
, …)에 대한 직접적인 언급가 없다.
package calc_1;
public class MainClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCalculator calculator = new MyCalculator();
calculator.calculate(10,5, new CalAdd());
calculator.calculate(10,5, new CalSub());
calculator.calculate(10,5, new CalMul());
calculator.calculate(10,5, new CalDiv());
}
}
MainClass
에서 같은 calculator.calculate()
메서드에 다른 연산 객체를 전달하면 다른 연산을 수행한다.
2. 의존 역전 원칙 (DIP)
첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
둘째, 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
- 의존 역전 원칙
: Dependency Inversion Principle
: 객체지향설계 5원칙(SOLID)의 D에 해당한다.
: 객체에서 어떤 Class를 참조해서 사용해야하는 상황이 생긴다면, 그 Class를 직접 참조하는 것이 아니라 그 대상의 상위 요소(추상 클래스 or 인터페이스)로 참조하라는 원칙.
“이제 MyCalculator
안에는 각각의 연산 객체들(CalAdd
, CalSub
, …)에 대한 직접적인 언급가 없다.” 라는 말의 의미를 조금 더 깊이 이해해 보자.
직접적인 언급을 피하면서 외부에서 주입하기 위해서 인터페이스(ICalculator
)를 매개변수로 활용했다.
즉, 구체적인 각각의 연산 객체들(CalAdd
, CalSub
, …)보다는 추상화된 상위 요소인 ICalculator
에 의존하도록 했다.
package calc_1;
public class MyCalculator {
public void calAdd(int fNum, int sNum){
ICalculator calculator = new CalAdd();
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calSub(int fNum, int sNum){
ICalculator calculator = new CalSub();
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calMul(int fNum, int sNum){
ICalculator calculator = new CalMul();
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
public void calDiv(int fNum, int sNum){
ICalculator calculator = new CalDiv();
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
}
인터페이스를 활용하기 전 두 MyCalculator
코드에는 각각의 연산 객체들이 구체적으로 언급된다.
MyCalculator
는 CalAdd
등 각각의 연산 객체에 크게 의존한다.
package calc_1;
public class MyCalculator {
public void calculate(int fNum, int sNum, ICalculator calculator){
int value = calculator.doOperation(fNum, sNum);
System.out.println("result : " + value);
}
}
의존성 주입 + 인터페이스를 활용하면서 의존성 역전을 이뤄 객체 간의 결합도를 낮출 수 있었다.
DI는 DIP를 달성하는 하나의 방식이다.
3. 제어의 역전 (IoC)
- IoC
: Inversion of Control
: 제어의 역전
: 전통적인 프로그래밍에서 흐름은 프로그래머가 작성한 프로그램이 외부 라이브러리의 코드를 호출해 이용한다. 하지만 제어 반전이 적용된 구조에서는 외부 라이브러리나 프레임워크의 코드가 프로그래머가 작성한 코드를 호출한다.
만약 택시를 탔다고 가정하자.
택시 기사에게 자동차의 목적지 외에도 사소한 것 하나하나 모두 지시해야 한다면 번거로울 것이다.
탑승자 입장에서 “이런 것까지 우리가 알아야하나?” 라는 생각이 들 것이다.
탑승자는 그저 가고 싶은 목적지만 알고 있고, 그곳으로 가고 싶을 뿐이다.
위와 같은 상황이 “일반적인 제어 흐름”이다.
상위 모듈(손님)이 하위 모듈(택시 기사)에게 모든 행동을 지시해야 하므로 명령적 프로그래밍이라고 할 수 있다.
제어의 역전이 일어나면 …
- 손님은 목적지만 전달한다.
- 택시 기사는 어떻게든 스스로 목적지까지 도달한다.
- 택시가 목적지에 도착하면 택시 기사는 손님에게 도착 사실을 알린다.
손님이 택시 기사에게 명령하던 것이 택시 기사가 손님에게 도착함을 알리는 방식으로 제어가 역전된 것이다.
출처 : https://tecoble.techcourse.co.kr/post/2021-05-14-inversion-of-control/
가. IoC 구현하기.
프로그램에서 main()
는 프로그램의 시작을 나타낸다.
따라서 main()
에 MyCalculator
, CalAdd
, CalSub
, CalMul
, CalDiv
를 생성하는 것은 main()
에게 과도한 업무를 부여한 것이다.
이때 “과도한 업무”란 “실제 수행하는 연산 수행의 양, 복잡성 그리고 부담 크다”라는 뜻이 아니다.
“복잡한 코드, 떨어지는 가독성, 높은 결합도, 목적을 알 수 없음”을 의미한다.
모든 작업을 main()
이 하나 하나 “제어”해야 한다.
프로그램 실행에 필요한 객체는 별도의 클래스(CalAssembler
)에서 생성하도록 수정한다.
main()
은 이 CalAssembler
만 생성하면 CalAssembler
가 모든 일을 맡아서 처리한다.
package calc_1;
public class CalAssembler{
MyCalculator calculator;
CalAdd calAdd;
CalSub calSub;
CalMul calMul;
CalDiv calDiv;
public CalAssembler(){
calculator = new MyCalculator();
calAdd = new CalAdd();
calSub = new CalSub();
calMul = new CalMul();
calDiv = new CalDiv();
assemble();
}
public void assemble() {
calculator.calculate(10, 5, calAdd);
calculator.calculate(10, 5, calSub);
calculator.calculate(10, 5, calMul);
calculator.calculate(10, 5, calDiv);
}
}
package calc_1;
public class MainClass {
public static void main(String[] args){
new CalAssembler();
}
}
new CalAssembler();
: 다음과 같이 객체를 생성하면 해당 객체는 변수나 참조로 바인딩되지 않음. 이는 "익명 객체" 생성으로 볼 수 있으며, 이렇게 생성된 객체는 해당 라인 이후로 접근이 불가능.
생성자 안에서 assemble
메소드를 호출하여 연산을 수행한다.
이렇게 따로 CalAssembler
클래스를 사용하면, 각 객체의 생성 및 연산의 조합이 CalAssembler
내에 캡슐화되므로 MainClass
는 단순해진다.
MainClass
는 모든 작업을 직접 제어할 필요 없이 CalAssembler
가 알아서 처리하고 결과를 알려준다.
MainClass
의 업무가 간단해졌다.
나. IoC 컨테이너와 Bean
이제 main()
메서드는 CalAssembler
객체만 그것도 익명 객체만 생성한다. (게으른 놈)
CalAssembler
와 같이 객체를 생성하고 조립하는 특별한 공간을 스프링에서는 “IoC 컨테이너”라고 한다.
IoC 컨테이너 속의 객체를 “Bean”이라고 한다.
스프링의 IoC 컨테이너는 Bean을 생성하고 이를 DI(의존성 주입)한다.
4. 정리
- DI : 방식, 테크닉
- DIP : 원칙
- IoC : 디자인 패턴
DI를 통해서 DIP와 IoC를 만족하는 프로그램을 구현할 수 있다.
실습 코드
'학부 강의 > 웹프로그래밍 (Spring)' 카테고리의 다른 글
[Spring] 학사 정보 시스템 (0) | 2023.10.07 |
---|---|
[Spring] 스프링으로 계산기 만들기 (0) | 2023.09.22 |
[Spring] Maven (0) | 2023.09.14 |
[Spring] 개발 환경 구축 (0) | 2023.09.07 |
[Spring] model1, model2, MVC 패턴 (0) | 2023.09.07 |