[스프링] 제어의 역전 (Inversion Of Control)

업데이트:



✅ 제어의 역전 (IoC)

제어의 역전이라는 건, 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.


일반적으로 프로그램의 흐름은 main() 메소드와 같이 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정하고, 결정한 오브젝트를 생성하고, 만들어진 오브젝트에 있는 메소드를 호출하고, 그 오브젝트 메소드 안에서 다음에 사용할 것을 결정하고 호출하는 식의 작업이 반복된다.


한마디로, 이런 프로그램 구조에서 각 오브젝트는 프로그램의 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다.


제어의 역전(IoC, 이하 IoC라고 부르겠다)은 이런 제어의 흐름을 뒤바꾸는 것이다.


  • 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다.
  • 오브젝트는 스스로 생성하지도 않는다.
  • 오브젝트 자신도 어떻게 만들어지고 어디서 사용되는지 알 수 없다.
  • 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다.
  • 우리는 이것을 IoC라고 부른다.





✅ JAVA의 IoC

📌IoC 적용 전 예제


public class Test01 {
	public static void main(String[] args) {
		Player p = new Player();
		p.play();
	}
}
class Player {
	Dice1 d = new Dice1();	// 객체의 생성을 객체가 하고 있음
	public void play() {
		for (int i = 0; i < 3; i++) {
			System.out.println(d.getDiceValue()); 
		}
	}
}

class Dice1{
	public String getDiceValue(){return "주사위알고리즘1로 play";}
}

class Dice2 {
	public String  getDiceValue(){return "주사위알고리즘2로 play";}
}

위 코드를 보면 우리가 일반적으로 작성하는 코드와 거의 비슷하다. main() 에서 Player 객체를 하나 만들어 주고, Player 클래스에서 사용할 Dice1() 객체를 정해주고 있는 구조이다.

이부분이 중요하다. Player 클래스의 d인스턴스가 자신이 사용할 Dice1() 클래스를 직접 정하고 알고 있다는 점이다. 이는 IOC가 적용되지 않은 코드를 의미한다.

그럼 IOC가 적용되지 않았을때 무엇이 문제가 되길래 IOC를 굳이 적용시켜주려는 것일까? 만일 Player에서 Dice1객체가 아닌 Dice2객체를 사용하고자 하면 어떨까? 우리는 Player 클래스에 접근해서 소스코드를 변경해주어야만 한다.

그렇다면 매번 사용할 객체가 변경되면 변경될때마다 Player에 접근해서 직접 소스코드를 변경해줘야하는 번거로움이 생긴다. 이를 해결하기위해 IOC를 적용해서 객체가 직접 객체를 선택하는 것이 아닌, 외부의 다른 요소를 통해 객체에 대한 생성 및 사용을 결정하게끔 하는것이다.

📌IoC 적용 후 예제

public class Test02 { 
	public static void main(String[] args) { 
		Player p = new Player(new Dice1()); 
		p.play(); 
	} 
}
public class Player {
	private Dice d = null;
	public Player(Dice d) { this.d = d; }

	public void play() {
		for (int i = 0; i < 3; i++) {
			System.out.println(d.getDiceValue()); 
		}
	}
}

interface Dice {  
	public String getDiceValue();               
}

class Dice1  implements Dice {
	public String getDiceValue(){return "주사위알고리즘1로 play";}
}

class Dice2   implements Dice  {
	public String  getDiceValue(){return "주사위알고리즘2로 play";}
}

위의 예제에서 변화된 부분은 Dice 인터페이스를 선언하고, 하위 Dice1, Dice2를 생성한 것이다.

그리고, Player는 Dice 인터페이스를 필드로 가지고 있으며, 생성자에서 Dice를 매개인자로 선언하고있다.

이것은 Player가 사용하는 주사위 객체를 고정하는 것이 아니라, Dice 하위의 여러 타입을 이용한다는 것을 의미한다.

이제 Player의 사용 주사위 객체를 변경하고 싶을 때, Player 클래스의 소스를 수정할 필요가 없다.

그런데, 이 예제에서도 문제점은 있다. Test02에서 Dice2객체를 Player의 생성자 매개인자로 넣고 있는데, Dice1으로 변경하려하면 Test02 소스코드를 수정해야 한다.

소스코드에 하드 코딩되어 있기 때문이다. 이것을 개선하는 방법으로, 클래스 로더나 리플렉션을 이용할 수 있으나, 이러한 기술을 몰라도 스프링 프레임워크에서는 의존성을 주입하는 방법을 이용하여 이를 해결할 수 있다.





✅ 스프링의 IoC

스프링 프레임워크는 IOC 개념을 기반으로 설계되었고, IOC가 적용된 다양한 기능과 개념을 제공하고 있다.

  1. 스프링 컨테이너 스프링 컨테이너를 통해 스프링 빈을 관리

  2. 의존성 주입(DI) 객체간의 의존관계를 스프링이 자동으로 처리

  3. 컴포넌트 스캔 스프링이 자동으로 빈을 찾아 등록

  4. 스프링 AOP 스프링이 자동으로 관심사를 분리하고 모듈화

  5. 트랜젝션 관리 스프링이 자동으로 트랜젝션의 시작, 커밋, 롤백 처리

위처럼 스프링에서 IOC 개념이 탑재된 수많은 기능들이 존재한다. 각각의 개념들은 따로 포스팅을 통해 설명되어 있으니 참고하면 좋겠다.

댓글남기기