Inversion of Control(IoC) : 제어의 역전
- 프로그램의 제어 흐름을 직접 제어하는 것이 아니라, 외부에서 관리하는 것
- 객체의 생성과 소멸, 그리고 객체간의 의존관계를 설정하는 행위
- 모두 프레임워크가 대신 해준다 : 주도권이 역전됨 → 제어의 역전
- 프레임워크가 흐름을 주도하면서 개발자가 만든 어플리케이션 코드를 사용한다.
- 이렇게 IoC가 일어나는 공간을
IoC 컨테이너
라고 부른다.
도대체 무슨 말이야?
분명 코드를 작성한건 나인데, 왜 자꾸 주도권이 프레임워크에 있다는 거지? 🤔
→ 이를 이해하기 위해, 프레임워크를 사용할때와 사용하지 않을 때를 비교해보자
(1) 직접 객체를 생성하고 의존관계를 설정해주는 경우
public class BeanKor { public void sayBean(String name){ System.out.println("한국 콩!"); } } public class BeanEng { public void sayBean(String name){ System.out.println("English Bean!"); } }
public class YummyBean { public static void main(String[] ars){ // BeanKor bean = new BeanKor(); // 한국 콩! BeanEng bean = new BeanEng(); // English Bean! bean.sayBean(); } }
- main에서 BeanEng를 BeanKor로 바꿔줘야 함 : YummyBean은 BeanEng/Kor에 의존성을 가지고 있다
- YummyBean의 기능을 바꾸고자 할 때, BeanEng/Kor도 함께 바꿔줘야 하기 때문이다.
- 이러한 번거로움을 해결해주기 위해서는 객체의 인스턴스를 외부에서 생성받을 필요가 있다. (의존성 주입)
(2) 의존관계를 외부로부터 받아오는 경우 (의존성 주입)
public interface Bean { public void sayBean(); } public class BeanKor implements Bean{ public void sayBean(){ System.out.println("한국 콩!"); } } public class BeanEng implements Bean{ public void sayBean(){System.out.println("English Bean!");} }
public class YummyBean { public static void main(String[] ars){ Bean bean = new BeanKor(); // 한국 콩! // Bean bean = new BeanEng(); // English Bean! bean.sayBean(); } }
- 하지만 여전히 출력되는 값을 바꾸기 위해선 소스코드의 수정이 필요하다.
- 더 개선시킬 수 있을까?
(3) 프레임워크가 대신 객체를 생성하고 의존관계를 설정해주는 경우 (IoC)
public interface Bean { public void sayBean(); } public class BeanKor implements Bean{ public void sayBean(){ System.out.println("한국 콩!"); } } public class BeanEng implements Bean{ public void sayBean(){ System.out.println("English Bean!"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=".../beans/spring-beans.xsd"> <bean id="Bean" class="BeanKor"/> <!--<bean id="Bean" class="BeanEng"/>--> </beans>
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource; public class YummyBean { public static void main(String[] ars){ BeanFactory factory = new XmlBeanFactory (new FileSystemResource("resources/beans.xml")); Bean bean = factory.getBean("bean", Bean.class); bean.sayBean(); // 한국 콩! } }
- 소스 코드의 변경 없이 환경설정만으로도 프로그램을 제어할 수 있다.
- 이것이 가능한 이유는, 객체의 생성과 주입을 모두 스프링 프레임워크가 주도하고 있기 때문 (IoC)
- 프로그래머는 BeanKor 클래스와 BeanEng클래스를 정의하기만 했다.
- 기존 코드에서처럼 bean에 BeanKor를 넣어줄지, BeanEng를 넣어줄지 결정하지도 않았고,
new
BeanKor
따위의 생성자로 객체를 생성하지도 않았다. - 스프링이 스스로 설정 파일을 읽고, 거기 써져 있는대로 객체를 생성한 다음 반환해줬다 :
주도권
을 가짐
프레임워크가 생산성을 높여주는 이유
- 프레임워크는 뼈대를 제공하고, 그 위에 개발자가 코드를 올려 동작하게끔 한다.
- 개발자 입장에선 로직에 더 집중할 수 있고, 객체지향 개발에 있어서 시스템을 일관성있게 관리할 수 있다.
- 여러 개발자들이 각자의 취향에 따라 설계하고 코딩한다면 관리가 어렵지만, 프레임워크가 제공하는 뼈대와 가이드에 따라 개발하면 시스템의 유지/보수가 용이하다.
- 예) 기존에 쓰던 객체를 버리고, 새로운 객체를 사용해야 하는 경우
- [상황] 5000원 할인권만 뿌리고 있었는데, 50% 할인권을 뿌려야 한다고요?
- (1)의 방법 : 새 클래스 정의하고, 코드 일일이 돌면서,
ctrl+f
해가면서, 일일이 수정 작업ㅠ - (3)의 방법 : 새 클래스를 정의한 다음, 설정 파일만 수정해주면 끝!
참고자료
함께 나눈 이야기
- IoC는 Profile/Property/Resource 등의 외부 설정파일 주입에 주로 사용되고(사용 DB 등) 위 예제에서처럼 객체에서는 IoC가 잘 안일어나는 것 아닐까? 상품권 객체에 IoC를 사용하는 것이 맞을까?
- 하지만 DI 기능은 IoC 기능의 일부 아닌가?
- 하지만 그럼 객체의 생명주기와 관계 설정을 담당하는 IoC 컨테이너는 무엇인가?
- 정확히 언제 IoC의 이득을 볼 수 있는지, 정확히 언제 IoC의 기능을 사용할 수 있는지를 정리해보면 좋을 것 같다!