프로그래밍/Spring-boot

IOC와 DI 에 대하여 (스프링 개념 이해하기 쉽게 설명)

vell_zero 2021. 10. 14. 21:46

https://mo-world.tistory.com/entry/IOC%EC%99%80-DI-%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%84%A4%EB%AA%85

 

IOC와 DI 에 대하여 (스프링 개념 이해하기 쉽게 설명)

Inversion of Control (제어의 역전) DI(의존성 주입)을 이해하기 위해서는 IoC(제어의 역전) 의 개념을 알고 넘어가야 할 필요가 있다. 스프링을 쓰기 전에는 개발자가 프로그램의 흐름(애플리케이션

mo-world.tistory.com

출처: http://jhleed.tistory.com/61

 

Spring 의 DI란?

Dependency Injection(의존성 주입) 객체간의 의존성을 자신이 아닌 외부에서 주입하는 개념이다. 스프링에서 이런 개념을 사용하는데에는 이유가 있을 것이다. 의존성 주입이 왜 필요할까? 이걸 사

jhleed.tistory.com

 

Inversion of Control (제어의 역전)

DI(의존성 주입)을 이해하기 위해서는 IoC(제어의 역전) 의 개념을 알고 넘어가야 할 필요가 있다.

스프링을 쓰기 전에는 개발자가 프로그램의 흐름(애플리케이션 코드)을 제어하는 주체였었다.

스프링에서는 프로그램의 흐름을 프레임워크가 주도하게 된다. (ex : 뒤에도 나오겠지만 @Autowired 등으로 Bean을 자동 주입)

객체의 생성~ 생명주기 관리를 컨테이너가 도맡아서 하게 된 것이다.

즉, 제어권이 컨테이너로 넘어가게 되고,  이것을 제어권의 흐름이 바뀌었다고 하여 IoC(Inversion of Control : 제어의 역전)이라고 하게 된

제어권이 컨테이너로 넘어옴으로써 DI(의존성 주입), AOP(관점 지향 프로그래밍)등이 가능하게 된다. 

반대로 말하면 스프링에게 애플리케이션의 흐름을 제어하는 권한(IOC)이 없다면? Autowired를 통한 의존성 주입을 할 수 없을 것이다.

 

Dependency Injection(의존성 주입)

객체간의 의존성을 자신이 아닌 외부에서 주입하는 개념이다.

스프링에서 이런 개념을 사용하는데에는 이유가 있을 것이다.

의존성 주입이 왜 필요할까? 이걸 사용하게 되면 무엇이 편할까? 아니, 그 전에 의존성이란 무엇일까? 

나는 처음 공부할때 이런 것들이 궁금했다.

우선 가장 간단한 예제를 들어보자. 

 

아래의 HelloApp은 MessageBean이라는 클래스에서 sayHello라는 메소드를 호출한다.

실행 결과 : Hello, Spring!

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class HelloApp {
public static void main(String[] ars){
MessageBeanEn bean = new MessageBeanEn();
bean.sayHello("Spring");
}
}

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class MessageBeanEn {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}

 

간단하다.

하지만 소프트웨어에는 변화가 불가피하다는 불변의 진리가 있다.

그리고 소프트웨어 기능 변경은 곧 코드의 변화와 연결된다.

기능이 추가될 때마다 매번 코드를 변경하고, 다시 컴파일하는 것은 비용이 많이 들게 되므로, 가급적이면 코드의 변화가 적어지도록 프로그램을 작성하는게 유지보수 측면에서 유용할 것이다.

 

어떻게 하면 소스 코드의 변경을 최소화 할 수 있을까?

반대로 생각해 본다면, 어떤 경우에 소스 코드의 변경이 자주 일어날까?

 

기존의 Hello Spring을 출력하는 기능에서 한글로 안녕 스프링!을 출력해 본다.

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class HelloApp {
public static void main(String[] ars){
MessageBeanKr bean = new MessageBeanKr(); //MessageBeanEn -> MessageBeanKr로 변경
bean.sayHello("Spring");
}
}

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class MessageBeanKr {
public void sayHello(String name) {
System.out.println("안녕, " + name + "!");
}
}

 

 

메인의 MessageBeanEn을 MessageBeanKr로 바꿔줘야 한다.

이런 경우에 HelloApp이 MessageBeanEn(Kr)에 의존성을 가지고 있다고 한다.

HelloApp의 기능을 바꾸고자 할 때 MessageBean도 함께 바꿔줘야 하기 때문이다.

 

이러한 의존성을 해결해주기 위해서는 객체의 인스턴스를 외부에서 생성받을 필요가 있다.

마틴 파울러가 저술한 Inversion of Control Containers and the Dependency Injection pattern에서는 의존성 주입을 아래의 3가지로 분류한다.

  1. 생성자를 이용한 의존성 주입
  2. setter메소드를 이용한 의존성 주입
  3. 초기화 인터페이스를 이용한 의존성 주입

이중 스프링이 지지하는 방식은 2. setter메소드를 이용한 의존성 주입이라고 하는데.. 예제와 자세한 설명은 아래 블로그를 참조하면 좋을 것 같다.

http://www.nextree.co.kr/p11247/

 

두번째 코드를 보자.

MessageBeanKr과 MessageBeanEn에 대한 인터페이스를 구현하여 그 전보다 HelloApp의 소스 변경을 줄였다. 

출력되는 내용을 영문으로 변경하고 싶으면 구현체를 MessageBeanEn으로 바꿔주기만 하면 된다.

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public interface MessageBean {
public void sayHello(String name);
}

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class MessageBeanKr implements MessageBean{
public void sayHello(String name) {
System.out.println("안녕하세요, " + name + ".");
}
}

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class MessageBeanEn implements MessageBean{
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}

 

package sample1;

/**
* Created by james on 16. 2. 10.
*/
public class HelloApp {
public static void main(String[] ars){
MessageBean bean = new MessageBeanKr();
bean.sayHello("Spring"); //위의 대입문을 MessageBeanEn으로 바꾸면 영문이 출력된다.
}
}

 

하지만 이 역시 소스코드의 변경이 약간은 일어나게 된다.

마지막 코드를 보자.

아래와 같은 설정 파일이 추가되었다. 이 파일은 외부에서 MessageBean에 주입할 객체에 대한 설정 정보가 담겨있다.

 

beans.xml

<?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="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--class MessageBeanEn으로 바꾸면 영문이 출력된다-->
<bean id="messageBean" class="sample1.MessageBeanKr" />
</beans>

 

HelloApp.java

package sample1;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

/**
* Created by james on 16. 2. 10.
*/
public class HelloApp {
public static void main(String[] ars){
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("resources/beans.xml"));

//bean이 사용하는 클래스 이름(MessageBeanEn 혹은 MessageBeanKr)을 코드에 직접 기술하지 않는다. , 의존성이 없다.
MessageBean bean = factory.getBean("messageBean", MessageBean.class);
bean.sayHello("Spring");
}
}

결과는 아래와 같이 출력된다.

안녕하세요, Spring 씨

 

만약 출력되는 코드를 영문으로 바꾸고 싶다면 beans.xml에서 아래 부분을

<bean id="messageBean" class="sample1.MessageBeanKr" />

다음과 같이 바꾸면 된다.

<bean id="messageBean" class="sample1.MessageBeanEn" />

 

그러면 출력되는 결과는 

Hello, Spring! 이 될 것이다.

소스 코드의 변경 없이 환경설정만으로도 프로그램을 제어할 수 있다.

이것이 가능한 이유는, 맨 윗 단락에서 언급한 객체의 생명주기를 스프링 컨테이너가 관리하는 IoC개념이 있기 때문이다.

 

결론 : 스프링에서의 의존성 주입은 객체의 실체를 외



출처: http://jhleed.tistory.com/61 [개발자노트]