DI 와 IoC
DI 와 IoC

DI 와 IoC

Tags
Spring
Web Dev
Published
February 2, 2024
Author
gozneokhan

IoC (Inversion of Control)란?

IoC (Inversion of Control)은 개발 중인 애플리케이션의 제어 흐름이 개발자가 아닌 외부에서 주도되는 디자인 패턴을 의미합니다. 이 용어는 GoF(Gang of Four)의 디자인 패턴에서 언급되었으며, Spring과 같은 프레임워크에서 강조되는 중요한 컨셉 중 하나입니다. IoC는 제어의 역전으로 해석되며, 여기에서 '제어'는 객체의 생성, 관리, 생명주기 등을 말합니다. 따라서 IoC는 제어의 주도권이 개발자가 아닌 프레임워크나 컨테이너로 넘어감을 의미합니다.
간단한 예시를 통해 IoC의 개념을 이해해보겠습니다.

직접 객체를 생성하여 의존성을 나타내는 코드

javaCopy code public class A { private B b; public A() { b = new B(); } }
위 코드에서는 A 클래스가 B 클래스에 직접 의존하며, A 객체 내에서 B 객체를 생성하고 있습니다. 이는 개발자가 직접 객체를 제어하여 A 객체가 B 객체에 의존하고 있음을 나타냅니다.

IoC를 활용한 코드

javaCopy code public class A { @Autowired private B b; }
이번에는 Spring 프레임워크의 IoC를 활용한 코드입니다. B 클래스가 스프링 컨테이너에서 관리되는 Bean이라면 @Autowired 어노테이션을 통해 A 객체가 자동으로 B 객체를 주입받을 수 있습니다. 이것은 개발자가 직접 객체를 생성 및 관리하지 않고, 스프링 컨테이너가 제어하고 주입해주는 모델입니다. 이로써 제어의 역전이 발생하게 됩니다.

템플릿 메소드 패턴에서의 IoC

javaCopy code public abstract class IronFactory { private IronMan ironMan; public IronMan getIronMan() { return assemble(); } private IronMan assemble() { // do assemble.. from head, body, arms, legs return ironMan; } protected abstract void head(); protected abstract void body(); protected abstract void arms(); protected abstract legs(); } public class HulkBuster extends IronFactory { @Override protected void head() { // do something.. } @Override protected void body() { // do something.. } @Override protected void arms() { // do something.. } @Override protected void legs() { // do something.. } }
템플릿 메소드 패턴에서도 IoC 개념을 찾아볼 수 있습니다. IronFactory 클래스에서 제어의 주도권이 있으며, 하위 클래스인 HulkBuster는 상위 클래스의 메소드를 구현하는 것에 집중하고, 언제 어떻게 실행될지는 모릅니다.
IoC는 프로그램의 제어 흐름이 개발자가 아닌 외부에서 주도되는 디자인 패턴으로, 코드의 느슨한 결합, 테스트 용이성, 확장성 등을 향상시킵니다. 프레임워크나 컨테이너가 객체 간의 의존성을 주입하여 제어의 주도권을 개발자로부터 빼앗아 관리하게 됩니다.

IoC 도입 이유와 Spring에서 이점

IoC 도입 이유

객체 지향 프로그래밍과 책임 분리

IoC는 객체 지향 프로그래밍에서 중요한 책임의 분리를 장려합니다. 각 객체는 자신의 역할과 책임에만 집중하게 되어 응집도가 높아지고, 변경에 유연한 코드를 작성할 수 있습니다.

유연성과 확장성

IoC는 변경에 유연한 코드를 작성할 수 있도록 도와줍니다. 외부에서 객체의 생성, 소멸, 의존성 주입을 관리하므로, 필요에 따라 객체를 교체하거나 새로운 객체를 추가하는 작업이 용이해집니다.

객체의 생명 주기 관리

IoC 컨테이너는 객체의 생명 주기를 관리하므로, 객체의 생성, 초기화, 소멸에 대한 부분을 신경쓰지 않아도 됩니다. 이로써 개발자는 핵심 비즈니스 로직에 더 집중할 수 있습니다.

의존성 주입(Dependency Injection)

IoC는 의존성 주입을 통해 객체 간의 결합도를 낮춥니다. 객체가 직접 의존 객체를 생성하지 않고 외부에서 주입받기 때문에, 변경이 발생할 경우 해당 객체만 수정하면 되며, 전체 시스템에 미치는 영향이 줄어듭니다.

Spring에서 IoC의 이점

Bean 관리와 의존성 주입

Spring은 IoC 컨테이너를 통해 모든 객체를 관리하고, 의존성 주입을 쉽게 처리할 수 있습니다. 이는 코드의 가독성을 높이고, 유지보수를 용이하게 만듭니다.

코드의 간결성과 모듈화

IoC를 통해 객체의 생명 주기 및 의존성 관리를 외부로 위임함으로써 코드가 간결해지고 모듈화가 용이해집니다. 각 객체는 자신의 역할에만 충실하면 되므로 코드의 이해와 유지보수가 편리해집니다.

유연한 확장성

IoC는 객체 간의 결합도를 낮추고, 객체의 생성 및 관리를 외부에서 담당하므로 시스템에 새로운 기능을 추가하거나 변경할 때 유연성이 크게 향상됩니다.

테스트 용이성

의존성 주입을 통해 테스트하기 쉬운 코드를 작성할 수 있습니다. Mock 객체를 주입하거나 특정 기능을 테스트하는 데 필요한 객체를 쉽게 대체할 수 있어 테스트 용이성이 증가합니다.

엔터프라이즈 개발에 적합

Spring IoC는 엔터프라이즈급 애플리케이션에서 다양한 객체를 효과적으로 관리하고 의존성을 주입하는 데 적합합니다. 대규모 프로젝트에서 일관성 있고 효율적인 개발을 가능하게 합니다.
그래서
Spring에서 IoC는 코드의 유연성, 확장성, 유지보수성을 향상시키기 위한 핵심적인 개념으로 사용됩니다. 개발자는 핵심 비즈니스 로직에 집중하며, Spring이 객체의 생명 주기 및 의존성을 관리함으로써 효과적인 엔터프라이즈급 애플리케이션을 개발할 수 있습니다.

Spring에서의 DI

 
Spring은 IoC를 구현하기 위해 DI를 사용합니다. DI는 객체 간의 의존성을 외부에서 주입받는 것을 의미하며, 이를 통해 객체가 직접 의존 객체를 생성하거나 관리하지 않고 외부에서 주입받아 사용합니다.
DI는 스프링의 핵심 기능 중 하나로, 여타 프레임워크와의 차별화된 기능 중 하나입니다. 이는 마틴 파울러와 다른 개발자들이 IoC에 범용적인 의미를 부여하고자 만든 용어로, 의도가 명확하게 드러나는 이름인 "Dependency Injection"이라는 용어를 사용하기 시작했습니다.

DI의 핵심 개념

DI는 오브젝트 레퍼런스를 외부로부터 제공받고 이를 통해 여타 오브젝트와 동적하게 의존관계가 만들어지는 것이 핵심입니다. 주입 받는 메소드의 파라미터는 특정 클래스 타입이 고정되어 있지 않고, 인터페이스 타입으로 다이나믹하게 구현 클래스를 결정해서 제공 받을 수 있도록 인터페이스 타입의 파라미터를 사용합니다.

Spring에서의 DI 예시

javaCopy code // IronManController.java public class IronManController { private IronManService ironManService; // 생성자를 통한 의존성 주입 public IronManController(IronManService ironManService) { this.ironManService = ironManService; } // 생략 }
javaCopy code // IronManService.java public interface IronManService { void attack(); void flying(); }
javaCopy code // HulkBuster.java public class HulkBuster implements IronManService { @Override public void attack() { System.out.println("헐크버스터 공격!"); } // 생략 }
javaCopy code // Mark15.java public class Mark15 implements IronManService { @Override public void attack() { System.out.println("나노입자 아이언맨 공격!"); } // 생략 }

DI의 개념 정리

DI는 스프링에서 나온 개념이 아닙니다. DI는 의존성을 외부에서 주입 받는 디자인 패턴으로, 객체 간의 결합도를 낮추어 유연하고 확장 가능한 코드를 작성하는 데 기여합니다. 스프링은 DI를 구현한 프레임워크 중 하나로, IoC 컨테이너를 통해 객체의 생명 주기와 의존성을 효과적으로 관리합니다.

글을 마치며

IoC (제어의 역전)

IoC는 객체의 흐름과 생명주기 관리 등을 독립적인 제3자에게 위임하는 프로그래밍 모델을 의미합니다. 이는 개발자가 작성한 코드에서 제어의 흐름이 외부의 컨테이너로 역전되어, 컨테이너가 객체의 생성, 관리, 생명 주기 등을 담당하게 됩니다. 디자인 패턴에서도 이러한 IoC 개념을 찾아볼 수 있으며, 다양한 컨테이너를 가진 프레임워크들에서 일반적으로 적용되는 범용적인 프로그래밍 모델입니다.

DI (의존관계 주입)

DI는 IoC의 한 형태로, 객체 사이의 의존성을 외부에서 주입하는 디자인 패턴입니다. 여기서 의존성은 객체가 다른 객체를 사용하거나 참조하는 관계를 의미합니다. DI를 통해 인터페이스를 통해 다이나믹하게 객체를 주입 받아 유연한 프로그래밍이 가능하게 됩니다. DI를 통해 객체 간의 결합도를 낮추고 유지보수성 및 테스트 용이성을 향상 시킬 수 있습니다.
따라서, IoC는 제어의 역전을 강조하는 프로그래밍 모델을 의미하며, 이를 구현하는 방법 중 하나가 DI입니다. DI는 좀 더 구체적으로 의존 관계 주입에 중점을 둔 패턴으로, 객체의 의존성을 외부에서 주입 받아 유연한 프로그래밍을 가능하게 합니다.

Reference