Spring & Springboot/올인원 스프링 프레임워크

계산기 프로그램 만들기

YJ_ma 2023. 9. 11. 22:56

스프링 DI와 IoC

계산기 프로그램의 전체적인 구성은 다음과 같다.

MainClass : main(), MyCalculator

MyCalculator : CalAdd, CalSub, CalMul, CalDiv

 

MainClass의 main()에서 프로그램이 시작하면 MyCalcuator를 생성한다.

MyCalculator는 덧셈, 뺄셈, 곱셈, 나눗셈을 위한 각각의 객체(CalAdd, CalSub, CalMul, CalDiv)를 생성하고, 생성된 객체는 내부에서 사칙연산을 실행한다.

 

계산기 프로그램 만들기

ch02_pjt_01 프로젝트 생성하기

File > Project > Java Project 

프로젝트 생성 결과

클래스와 인터페이스 만들기

클래스 인터페이스
MainClass.java
MyCalculator.java
CalAdd.java
CalSub.java
CalMul.java
CalDiv.java
ICalculator.java

 

클래스 및 인터페이스 생성 결과

클래스 코딩하기

다음 코드를 각 클래스 파일에 코딩한다.

package ch02_pjt_01;

public class MainClass {
	public static void main(String[] args) {
		MyCalculator calculator = new MyCalculator();
		calculator.calAdd(10, 5);
		calculator.calSub(10, 5);
		calculator.calMul(10, 5);
		calculator.calDiv(10, 5);
	}

}
package ch02_pjt_01;

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);
	}

}
package ch02_pjt_01;

public interface ICalculator {
	public int doOperation(int firstNum, int secondNum);
}
package ch02_pjt_01;

public class CalAdd implements ICalculator {
	@Override
	public int doOperation(int firstNum, int secondNum) {
		return firstNum + secondNum;
	}

}
package ch02_pjt_01;

public class CalSub implements ICalculator{
	@Override
	public int doOperation(int firstNum, int secondNum) {
		return firstNum - secondNum;
	}

}
package ch02_pjt_01;

public class CalMul implements ICalculator{
	@Override
	public int doOperation(int firstNum, int secondNum) {
		return firstNum * secondNum;
	}
}
package ch02_pjt_01;

public class CalDiv implements ICalculator{
	@Override
	public int doOperation(int firstNum, int secondNum) {
		return secondNum != 0 ? (firstNum / secondNum) : 0;
	}

}

이제 MainClass.java를 실행하여 결과가 제대로 출력되는지 확인해본다.

실행결과

DI의 개념

DI : Dependency Injection, 의존성 주입

의존성 주입 : 필요한(의존하는) 객체를 직접 생성하지 않고 외부에서 주입하는 방식

계산기 프로그램을 실행하면 main()에서 MyCalculator를 생성하고 calAdd(), calSub(), calMul(), calDiv()를 호출한다.

이 메서드들은 동일하게 연산에 필요한 객체를 직접 생성한다. ( ex, new calAdd(); )

이를 'MyCalculator는 CalAdd, CalSub, CalMul, CalDiv 객체를 이용한다'라고 표현한다.

즉, MyCalculator는 자신이 직접 연산하지 않고, 각각의 연산 객체들(calAdd, calSub, calMul, calDiv)에게 연산 업무를 전달하는 것이다.

CalAdd 입장에서는 main()  함수가 MyCalculator에 지시한 덧셈 연산 업무를 MyCalculator를 대신해서 덧셈 연산을 처리하고 있으므로 'MyCalculator는 CalAdd에 의존한다'고 표현할 수 있다.

 

따라서, 'A 객체가 B 객체를 이용한다.' == 'A 객체가 B 객체에 의존한다.' 를 의미한다.

 

계산기 프로그램을 DI방식으로 변경하기

ch02_pjt_02 프로젝트 생성하기

ch02_pjt_01 프로젝트를 복사해서 ch02_pjt_02 프로젝트를 생성한다.

 

MyClculator 클래스 수정하기

MyCalculator가 의존하는 CalAdd, CalSub, CalMul, CalDiv를 MyCalculator가 직접 생성하지 않고 외부에서 주입하는 방식으로 변경한다. 이를 매개변수를 이용해서 연산에 필요한 객체를 외부에서 받으면 된다.

package ch02_pjt_01;

public class MyCalculator {

	public void calAdd(int fNum, int sNum, CalAdd calAdd) {
		int value = calAdd.doOperation(fNum, sNum);
		System.out.println("result : " + value);
	}

	public void calSub(int fNum, int sNum, CalSub calSub) {
		int value = calSub.doOperation(fNum, sNum);
		System.out.println("result : " + value);
	}

	public void calMul(int fNum, int sNum, CalMul calMul) {
		int value = calMul.doOperation(fNum, sNum);
		System.out.println("result : " + value);
	}

	public void calDiv(int fNum, int sNum, CalDiv calDiv) {
		int value = calDiv.doOperation(fNum, sNum);
		System.out.println("result : " + value);
	}

}

 

MainClass 수정하기

package ch02_pjt_01;

public class MainClass {
	public static void main(String[] args) {
		MyCalculator calculator = new MyCalculator();
		calculator.calAdd(10, 5, new CalAdd());
		calculator.calSub(10, 5, new CalSub());
		calculator.calMul(10, 5, new CalMul());
		calculator.calDiv(10, 5, new CalDiv());
	}

}

 

인터페이스 활용하도록 수정하기

위와 같은 형태는 외부에서 필요한 객체를 얻지만 인터페이스를 사용하지 않으므로 동일한 코드를 계속 반복 작성해야한다.

이러한 불편함을 없애기 위해 다음과 같이 수정해준다.

package ch02_pjt_01;

public class MyCalculator {
	
	public void calculate(int fNum, int sNum, ICalculator calculator) {
		int value = calculator.doOperation(fNum, sNum);
		System.out.println("result : " + value);
	}
}
package ch02_pjt_01;

public class MainClass {
	public static void main(String[] args) {
		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());
	}

}

 

필요한 객체를 외부에서 주입하는 방법은 프로그램의 유연성을 확보하고 객체 간의 결합도를 낮출 수 있게 해준다!!

 

 IoC의 개념

IoC : Inversion of Contorl, 제어의 역전

프로그램의 제어권을 개발자가 컨트롤하는 것이 아니라 외부에서 컨트롤하는 방식이다.

제어의 주체가 개발자 → 스프링으로 바뀐 것

 

계산기 프로젝트를 IoC 방식으로 변경하기

ch02_pjt_03 프로젝트 생성하기

ch02_pjt_02 프로젝트를 복사해서 ch02_pjt_03 프로젝트를 생성한다.

모든 객체를 main에서 생성하는 것이 아니라 별도 클래스에서 생성하고 수정하는 CalAssembler 클래스를 생성한다.

CalAssembler에 다음 코드를 입력한다.

CalAssembler는 생성자에서 프로그램에 필요한 객체들(Mycalculator, CalAdd, CalSub, CalMul, CalDiv)을 모두 생성하고 연산을 자동으로 실행(assemble())한다.

package ch02_pjt_01;

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);
	}
}

MainClass를 다음과 같이 수정한다.

package ch02_pjt_01;

public class MainClass {
	public static void main(String[] args) {
		new CalAssembler();
	}

}

 

CalAssembler와 같이 객체를 생성하고 조립하는 특별한 공간을 스프링에서는 IoC 컨테이너라고 한다.

IoC 컨테이너의 객체를 빈(Bean)이라고 한다.

스프링의 IoC 컨테이너는 빈을 생성하고 필요한 곳에 주입(DI)하는 특별한 공간이다.