맨위로가기

의존성 주입

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

의존성 주입은 애플리케이션이나 클래스가 객체 생성 방식에 독립적이도록 하고, 객체 생성 방식을 분리된 구성 파일에서 지정하며, 다양한 환경을 지원하도록 돕는 프로그래밍 기법이다. 이는 클래스 간의 결합도를 낮추고 유연성, 재사용성, 테스트 용이성을 향상시킨다. 의존성 주입은 서비스, 클라이언트, 인터페이스, 주입자라는 네 가지 역할을 포함하며, 생성자 주입, 세터 주입, 인터페이스 주입 등의 방법으로 구현된다. DI 컨테이너는 의존성 주입을 자동화하여 개발을 용이하게 하며, Java의 스프링, .NET의 Microsoft.Extensions.DependencyInjection, PHP의 Symfony 등 다양한 프레임워크에서 지원된다.

더 읽어볼만한 페이지

  • 컴포넌트 기반 소프트웨어 공학 - 컴포넌트 오브젝트 모델
    컴포넌트 오브젝트 모델(COM)은 마이크로소프트에서 개발한 소프트웨어 컴포넌트 기술로, 서로 다른 애플리케이션이 객체를 통해 통신하고 기능을 공유할 수 있도록 하는 이진 인터페이스 표준을 제공하며, 다양한 프로그래밍 언어로 작성된 객체들의 상호 운용을 지원한다.
  • 컴포넌트 기반 소프트웨어 공학 - 공통 객체 요구 매개자 구조
    공통 객체 요구 매개자 구조(CORBA)는 객체 관리 그룹(OMG)에서 분산 컴퓨팅 환경에서 객체 지향 기술을 통합하기 위해 제정한 표준 아키텍처로서, 객체 요청 브로커(ORB)를 통해 객체 간 통신을 제공하고 다양한 서비스를 지원한다.
  • 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
    모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다.
  • 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
    스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.
  • 소프트웨어 구조 - Ajax
    Ajax는 웹 페이지 전체를 새로고침하지 않고 비동기적으로 서버와 통신하여 웹 애플리케이션의 일부를 업데이트하는 웹 개발 기술로, XMLHttpRequest 객체의 등장으로 가능해졌으며 HTML, CSS, DOM, JavaScript, JSON 등의 기술을 통합하여 동적인 사용자 인터페이스를 구현한다.
  • 소프트웨어 구조 - 멀티테넌시
    멀티테넌시는 단일 애플리케이션 인스턴스로 여러 고객에게 서비스를 제공하여 SaaS 및 클라우드 환경에서 비용과 관리 효율성을 높이고 데이터 활용 가치를 창출하는 소프트웨어 아키텍처 방식이다.
의존성 주입
개요
유형디자인 패턴
하위 유형소프트웨어 디자인 패턴, 디자인 패턴
문제컴포넌트가 자신의 의존성을 생성, 조회, 또는 계산하는 방법
컴포넌트의 구체적인 의존성을 변경하지 않고 재사용하는 방법
의존성을 구성하는 방법
해결책클래스에 필요한 의존성을 전달한다.
클라이언트가 어떤 의존성이 사용될지 알 필요가 없다.
클라이언트가 의존성을 어떻게 구성하는지 제어하지 않는다.
정의
설명의존성 주입은 객체의 의존성을 제공하는 기술이다.
다른 이름
다른 이름의존성 삽입
제어의 역전 (IoC)
목표
목표의존성 관리를 쉽게 한다.
적용
적용 시점단위 테스트를 수행할 때
코드를 재사용해야 할 때
클래스 간의 느슨한 결합을 만들고 싶을 때
방법
방법생성자 주입
세터 주입
인터페이스 주입
주요 원칙
주요 원칙단일 책임 원칙
의존성 역전 원칙
장점
장점단위 테스트가 쉬워진다.
재사용성이 높아진다.
결합도가 낮아진다.
단점
단점복잡성이 증가할 수 있다.
런타임 에러가 발생할 수 있다.
참고 사항
참고 사항할리우드 원칙
서비스 로케이터 패턴
관련 패턴
관련 패턴추상 팩토리 패턴
서비스 로케이터 패턴
플러그인

2. 의도

의존성 주입은 다음 문제들을 해결하기 위해 사용된다.[45]


  • 애플리케이션이나 클래스가 객체 생성 방식과 독립적으로 동작하도록 한다.
  • 객체 생성 방식을 별도의 구성 파일에서 지정할 수 있도록 한다.
  • 애플리케이션이 다양한 구성을 지원할 수 있도록 한다.


객체를 직접 생성하는 방식은 클래스를 특정 객체에 종속시키고, 나중에 변경하기 어렵게 만들어 유연성을 떨어뜨린다.[45] 이는 다른 객체가 필요한 경우 클래스를 재사용하기 어렵게 하고, 실제 객체를 모의 객체(Mock Object)로 대체할 수 없어 테스트도 어렵게 만든다.

클래스는 객체 생성 책임을 추상 팩토리[46]에 위임할 필요 없이, 의존성 주입을 통해 해결할 수 있다.

의존성 주입은 프로그램 디자인이 결합도를 느슨하게[50] 만들고, 의존관계 역전 원칙단일 책임 원칙을 따르도록 돕는다. 이는 클라이언트의 생성과 행위를 분리한다.[51] 이는 클라이언트가 의존성을 찾기 위해 시스템을 알아야 하는 서비스 로케이터 패턴과 반대된다.

"매개변수 전달"은 주입의 기본 단위이며, 클라이언트를 세부 사항과 분리한다. 주입은 전달 제어 주체(클라이언트가 아님)와 전달 방식(참조 또는 값)에 독립적이다.

의존성 주입은 다음 네 가지 역할의 객체와 인터페이스를 기반으로 한다.

  • 사용될 '''서비스''' 객체
  • 서비스를 사용하는 '''클라이언트''' 객체
  • 클라이언트의 서비스 사용 방식을 정의하는 인터페이스
  • 서비스를 생성하고 클라이언트에 주입하는 '''주입자'''


이를 비유하면 다음과 같다.

  • 서비스: 전기, 가스, 하이브리드, 디젤 자동차
  • 클라이언트: 엔진 종류와 관계없이 차를 운전하는 사람
  • 인터페이스: 운전자가 엔진 세부 사항(기어 등)을 몰라도 되도록 보장하는 '''자동변속기'''
  • 주입자: 자녀에게 어떤 차를 사줄지 결정하고 구매하는 부모


'''서비스'''는 사용 가능한 모든 객체, '''클라이언트'''는 다른 객체를 사용하는 모든 객체를 의미한다. 이름은 객체의 용도가 아닌 주입 시점의 역할을 나타낸다.

'''인터페이스'''는 클라이언트가 의존하는 타입이다. 서비스에 의해 구현된 인터페이스, 추상 클래스, 또는 concrete 서비스 자체가 될 수 있다. (후자는 DIP[52] 위반, 테스트를 위한 동적 결합도 저하를 희생) 클라이언트는 인터페이스의 구성, 확장을 알 필요 없이 이름과 API만 알면 된다. 인터페이스 변경 시 클라이언트는 수정될 필요가 없지만, 인터페이스와 클래스 간 리팩토링 시 재컴파일이 필요할 수 있다. 이는 클라이언트와 서비스가 독립적으로 배포될 때 중요하며, 의존성 주입으로 해결할 수 없는 높은 결합도 문제를 야기한다.

'''주입자'''는 클라이언트에 서비스를 제공하며, 종종 클라이언트도 생성한다. 주입자는 객체를 클라이언트, 서비스로 다루며 복잡한 객체 그래프를 연결한다. 주입자는 assembler, provider, container, factory, builder, spring, construction code, main 등 다양한 이름으로 불릴 수 있다.

의존성 주입은 구성과 동작을 분리하는 규칙으로 적용될 수 있다. DI 프레임워크는 '''new''' 키워드 사용을 금지하거나, 값 객체의 직접 생성을 허용할 수 있다.[53][54][55][56]

DI를 사용하면 컴포넌트 간 관계는 인터페이스로 기술되며, 구체적인 컴포넌트는 외부(다른 컴포넌트, 파일 등)에서 지정하여 의존 관계를 약화시킨다.

프로그램에서 의존 관계가 제거되면 다음과 같은 장점이 있다.[42]

마틴 파울러가 Dependency injection영어 용어를 만들었다. 이전에는 제어 반전(IoC) 개념이 있었으나, DI는 이를 정리하고 범위를 한정했다. 스프링 프레임워크는 초기에는 DI 대신 IoC 용어를 사용했다. DI는 2000년대자바 개발에서 복잡한 Java EE (Jakarta EE)와 EJB에 대한 비판을 배경으로 널리 사용되었다.[42] 이후 표준 사양에 포함되어, 2007년 Java EE 5는 제한적인 EJB 3.0을, 2009년 Java EE 6는 범용적인 DI 컨테이너 기능을 갖춘 CDI를 정의했다.[43]

3. 역할

의존성 주입에는 서비스, 클라이언트, 인터페이스, 주입자라는 네 가지 역할이 있다.[45]


  • 서비스: 사용될 객체이다.
  • 클라이언트: 서비스를 사용하는 객체이다.
  • 인터페이스: 클라이언트가 의존하는 서비스 사용 방법의 계약이다.
  • 주입자: 서비스를 생성하여 클라이언트에 주입한다.


객체는 한 번의 주입에서 어떤 역할을 하는지에 따라 서비스 또는 클라이언트가 된다.

John Munsch영어는 2009년 10월 28일에 5세 아이에게 의존성 주입을 설명하는 비유를 제시했다.[47][48][49]

| 비유[45] | 설명 |

|---|---|

| 서비스 | 전기, 가스, 하이브리드, 디젤 자동차 |

| 클라이언트 | 엔진 종류와 관계없이 차를 운전하는 사람 |

| 인터페이스 | 운전자가 엔진 세부 사항(기어 등)을 몰라도 되는 '''자동변속기''' |

| 주입자 | 아이에게 어떤 차를 사줄지 결정하고 구매하는 부모 |

의존성 주입은 결합도를 느슨하게[50] 하고 의존관계 역전 원칙단일 책임 원칙을 따르도록 돕는다.[51] 이는 클라이언트가 사용하는 시스템을 알아야 의존성을 찾는 서비스 로케이터 패턴과 반대된다.

주입은 "매개변수 전달"과 같지만, 클라이언트를 세부 사항과 분리한다는 의미가 추가된다. 또한 전달 제어 주체가 클라이언트가 아니며, 전달 방식(참조/값)과도 독립적이다.

의존성 주입은 모든 객체가 구성과 동작을 분리하도록 한다. DI 프레임워크는 '''new''' 키워드 사용을 제한하거나, 값 객체 직접 생성을 허용할 수 있다.[53][54][55][56]

3. 1. 서비스 및 클라이언트

서비스(Service) 객체는 유용한 기능을 담고 있는 모든 클래스이다. 클라이언트(Client) 객체는 서비스를 사용하는 모든 클래스이다. 클라이언트가 필요로 하는 서비스는 클라이언트의 ''의존성''이다.[12]

어떤 객체든 서비스 또는 클라이언트가 될 수 있다. 이 이름은 객체가 주입에서 수행하는 역할과 관련이 있다. 동일한 객체가 클라이언트(주입된 서비스를 사용)이자 서비스(다른 객체에 주입됨)가 될 수도 있다. 주입 시 서비스는 클라이언트의 상태의 일부가 되어 사용할 수 있게 된다.[12]

3. 2. 인터페이스

클라이언트는 의존성의 특정 구현에 대한 구체적인 지식이 필요 없다. 인터페이스의 이름과 API만 알면 된다.[45] 결과적으로 인터페이스가 변경되더라도 클라이언트는 수정할 필요가 없어진다. 하지만 인터페이스가 클래스로 존재하다가 인터페이스 타입으로(그 반대의 경우에도) 리팩토링될 경우 클라이언트는 다시 컴파일이 필요할 수 있다. 이는 클라이언트와 서비스가 독립적으로 발행될 경우 중요하며, 이 경우 결합도가 높아지는 문제는 의존성 주입으로 해결할 수 없다.

클라이언트는 의존성의 구현 방식을 알 필요 없이, 이름과 API만 알면 된다.[12] 예를 들어, 이메일을 가져오는 서비스는 내부적으로 IMAP 또는 POP3 프로토콜을 사용할 수 있지만, 단순히 이메일을 가져오려는 호출 코드에는 이러한 세부 사항이 필요없다. 구현 세부 사항을 무시함으로써, 클라이언트는 의존성이 변경될 때 변경될 필요가 없다.

3. 3. 주입자

인젝터는 클라이언트에게 서비스를 제공하며, 종종 클라이언트를 생성하기도 한다. 인젝터는 객체를 클라이언트처럼 처리하고, 나중에 다른 클라이언트의 서비스로 처리하여 매우 복잡한 객체 그래프를 함께 연결할 수 있다. 인젝터는 실제로 함께 동작하는 많은 객체가 될 수 있지만, 순환 종속성을 유발할 수 있으므로 클라이언트가 될 수는 없다. 주입자는 assembler, provider, container, factory, builder, spring, construction code, or main 등 다양한 이름으로 불린다.[53][54][55][56]

3. 4. 비유

의존성 주입은 서비스, 클라이언트, 인터페이스, 주입기라는 네 가지 역할을 포함한다. 자동차는 사람들을 한 곳에서 다른 곳으로 수송하는 유용한 작업을 수행하는 서비스로 생각할 수 있다. 자동차 엔진은 휘발유, 경유, 또는 전기를 필요로 할 수 있지만, 이러한 세부 사항은 고객, 즉 운전자에게는 중요하지 않다. 운전자는 단지 목적지에 도착할 수 있는지 여부만 신경 쓴다.

자동차는 페달, 스티어링 휠 및 기타 제어를 통해 통일된 인터페이스를 제공한다. 따라서 공장에서 어떤 엔진이 '주입'되었는지는 더 이상 중요하지 않으며, 운전자는 필요에 따라 모든 종류의 자동차를 전환할 수 있다.

4. 분류

제어 반전(IoC)은 DI보다 더 일반적인 개념이다. DI는 컴포지션을 통해 IoC를 구현하므로 종종 전략 패턴과 동일시되기도 한다. 하지만 전략 패턴에서는 객체의 수명 동안 의존성을 교환할 수 있도록 하는 반면, 의존성 주입에서는 단일 의존성 인스턴스만 사용될 수 있다.[58] 다형성은 여전히 유지되지만, 이는 위임과 컴포지션을 통해 이루어진다.

5. 장점 및 단점

의존성 주입은 프로그램 디자인이 결합도를 느슨하게[50] 하고 의존관계 역전 원칙단일 책임 원칙을 따르도록 클라이언트의 생성에 대한 의존성을 클라이언트의 행위로부터 분리하는 것이다.[51] 이는 클라이언트가 의존성을 찾기 위해 그들이 사용하는 시스템에 대해 알도록 하는 서비스 로케이터 패턴과 정반대되는 것이다.

의존성 주입에는 다음과 같은 네 가지 역할이 있다.


  • 사용될 '''서비스''' 객체
  • 사용하는 서비스에 의존하는 '''클라이언트''' 객체
  • 클라이언트의 서비스 사용 방법을 정의하는 인터페이스
  • 서비스를 생성하고 클라이언트로 주입하는 책임을 갖는 '''주입자'''


의존성 주입은 모든 객체가 구성과 동작을 분리하도록 요구하는 하나의 규율로 적용될 수 있다. 구성을 수행하는 DI 프레임워크에 의존하면 '''new''' 키워드의 사용을 금지하거나 덜 엄격하게 값 객체의 직접 생성을 허용할 수 있다.[53][54][55][56]

5. 1. 장점

의존성 주입은 클라이언트의 구성 가능성을 유연하게 만들고, 시스템 구성 세부 사항을 외부 구성 파일에서 사용하여 리컴파일 없이 시스템을 재구성할 수 있게 한다.[45] 코드 동작 변경 없이 레거시 코드에도 적용 가능하며, 재사용성, 테스트 가능성, 유지 가능성을 향상시킨다.[61]

의존성 주입의 장점은 다음과 같다:

  • 유연성 증가: 클라이언트는 클라이언트가 기대하는 인터페이스를 지원하는 모든 것에 대해 작동할 수 있다.[20]
  • 결합도 감소: 의존 관계 설정이 컴파일 시가 아닌 실행 시에 이루어져 모듈 간의 결합도를 낮출 수 있다.
  • 코드 재사용 증가: 작성된 모듈을 소스코드 수정 없이 여러 곳에서 사용할 수 있다.
  • 단위 테스트 용이성: 모의 객체 등을 이용한 유닛 테스트가 쉬워진다.
  • 상용구 코드 감소: 모든 의존성 생성이 단일 구성 요소에 의해 처리되므로 상용구 코드를 줄여준다.[19]
  • 동시 개발 가능: 두 명의 개발자가 서로를 사용하는 클래스를 독립적으로 개발할 수 있으며, 인터페이스만 알면 된다.[21] 플러그인 개발 시에도 유용하다.


의존성 주입은 마틴 파울러가 Dependency injection영어이라는 용어를 만들었으며, 그 이전에는 제어 반전(IoC)라는 유사한 개념이 있었다. 스프링 프레임워크는 초기에 IoC라는 표현을 사용했지만, 이후 DI 개념이 널리 퍼지게 되었다.[42] 자바의 Java EE (현 Jakarta EE) 표준 사양에도 포함되어, EJB 3.0 및 CDI와 같은 기술이 등장했다.[43]

5. 1. 1. 테스팅

의존성 주입은 시스템 구성 세부 정보를 구성 파일로 외부화하여 재컴파일 없이 시스템을 재구성할 수 있게 한다.[22] 구성 요소의 여러 구현이 필요한 다양한 상황에 대해 별도의 구성을 작성할 수 있으며, 여기에는 테스팅도 포함된다.

의존성 주입은 코드 동작 변경 없이 리팩터링으로 레거시 코드에 적용할 수 있다. 이는 클라이언트 독립성을 높이고, 테스트 대상이 아닌 다른 객체를 가장하는 스텁 또는 모의 객체를 사용해 독립된 유닛 테스트를 더 쉽게 만드는 결과를 가져온다. 이러한 테스트 용이성은 의존성 주입을 사용할 때 종종 처음으로 인식되는 이점이다.[23]

DI를 이용하는 프로그램에서는 컴포넌트 간 관계를 인터페이스를 사용하여 기술하며, 구체적인 컴포넌트는 지정하지 않는다. 어떤 컴포넌트를 사용할지는 다른 컴포넌트나 외부 파일 등을 이용하여 의존 관계를 얇게 할 수 있다. 의존 관계가 프로그램 외부로 제거되면서 단위 테스트 효율이 높아진다.[42]

DI를 사용하면 단위 테스트에서 의존성을 테스트용 클래스(모의 객체 등)로 쉽게 바꿀 수 있다. 다음은 DI를 사용한 예시이다. 이 예시에서는 `IOnlineBrokerageService`, `IStockAnalysisService` 인터페이스를 구현한 테스트용 클래스를 생성하고, DI를 통해 주입함으로써 실제 클래스를 사용하지 않고 단위 테스트를 구현한다.

```java

public class VerySimpleStockBrokerTest {

// IOnlineBrokerageService를 구현한 단순한 스텁

public class StubBrokerageService implements IOnlineBrokerageService {

public String[] getStockSymbols() {

return new String[] {"ACME"};

}

public double getBidPrice(String stockSymbol) {

return 100.0; // (테스트에 충분한 값)

}

public double getAskPrice(String stockSymbol) {

return 100.25;

}

public void putBuyOrder(String stockSymbol, int shares, double buyPrice) {

Assert.Fail("Should not buy ACME stock!");

}

public void putSellOrder(String stockSymbol, int shares, double sellPrice) {

// 이 테스트에서는 사용하지 않음

throw new NotImplementedException();

}

}

public class StubAnalysisService implements IStockAnalysisService {

public double getEstimatedValue(String stockSymbol) {

if (stockSymbol.equals("ACME"))

return 1.0;

return 100.0;

}

}

public void TestVerySimpleStockTraderImpl() {

// 이 테스트 전용 의존성을 지정하기 위해 DI 컨테이너에 직접 등록

DependencyManager.register(

IOnlineBrokerageService.class,

StubBrokerageService.class);

DependencyManager.register(

IStockAnalysisService.class,

StubAnalysisService.class);

IAutomatedStockTrader stockTrader =

(IAutomatedStockTrader) DependencyManager.create(IAutomatedStockTrader.class);

stockTrader.executeTrades();

}

}

```

구현이 DB나 네트워크에 접근하는 경우, 또는 오래된 EJB와 같은 무거운 컴포넌트의 경우, 그대로는 단위 테스트를 수행하기 어렵다. 하지만 위와 같이 DI를 사용하여 의존 관계만 테스트용으로 바꾸면, 원래 테스트 대상 프로그램에 손을 대지 않고도 쉽게 단위 테스트를 수행할 수 있다.[42]

5. 2. 단점


  • 명백한 기본값이 있는 경우에도 클라이언트가 설정 세부 정보를 요구하여 번거로울 수 있다.[21]
  • 동작과 생성을 분리하므로 코드를 추적하기 어렵게 만든다.[21]
  • 일반적으로 IDE 자동화를 방해하는 리플렉션 또는 동적 프로그래밍으로 구현된다.[24]
  • 일반적으로 더 많은 사전 개발 노력이 필요하다.[25]
  • 프레임워크에 대한 종속성을 장려한다.[26][27][28]

6. 적용 유형

마틴 파울러는 생성자 주입, 세터 주입, 인터페이스 주입의 세 가지 의존성 주입 패턴을 제시하였다.[63]


  • 생성자 주입: 클래스의 생성자를 통해 의존성을 주입한다.
  • 세터 주입: 의존성을 입력받는 세터(Setter) 메서드를 통해 의존성을 주입한다.
  • 인터페이스 주입: 의존성을 주입하는 함수를 포함한 인터페이스를 작성하고, 이를 구현하여 실행 시에 의존성을 주입한다.


이러한 기법들을 통해 프로그램에 의존성을 주입할 수 있다.

6. 1. 생성자 주입

클래스의 생성자를 만들고 그 생성자를 통해 필요한 의존성을 주입하는 방식이다.[63]

가장 흔한 형태의 의존성 주입은 클래스가 생성자를 통해 의존성을 요청하는 것이다. 이는 필요한 의존성 없이는 인스턴스화될 수 없으므로 클라이언트가 항상 유효한 상태에 있도록 보장한다.

```java

public class Client {

private Service service;

// 의존성은 생성자를 통해 주입된다.

Client(Service service) {

if (service == null) {

throw new IllegalArgumentException("service는 null이 아니어야 합니다");

}

this.service = service;

}

}

```

아래는 수동으로 의존성 주입(DI)을 수행하도록 리팩토링한 예시이다.

```java

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

private IStockAnalysisService analysisService;

private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl(

IStockAnalysisService analysisService,

IOnlineBrokerageService brokerageService) {

this.analysisService = analysisService;

this.brokerageService = brokerageService;

}

public void executeTrades() {



}

}

public class MyApplication {

public static void main(String[] args) {

IStockAnalysisService analysisService = new StockAnalysisServiceImpl();

IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl(

analysisService,

brokerageService);

stockTrader.executeTrades();

}

}

```

위 예제에서 `MyApplication.main()`은 의존성 주입을 수행하며, `VerySimpleStockTraderImpl` 자체는 특정 구현에 의존하지 않는다. 또한, 이 구현에서는 '''생성자 주입''' 기법이 사용되었다.

클라이언트가 주입된 서비스를 받을 수 있는 또 다른 주요 방식으로 생성자 주입이 있다.[29]

6. 2. 세터(Setter)를 통한 주입

의존성을 입력받는 세터(Setter) 메소드를 만들고 이를 통해 의존성을 주입하는 방식이다.[63]

클라이언트가 주입된 서비스를 받을 수 있는 주요 방식 중 하나는 세터 주입이다. 세터 주입은 클라이언트가 의존성을 허용하는 세터 메서드를 노출하는 방식이다.[29]

생성자 주입 대신 setter 메서드를 통해 의존성을 받아들임으로써, 클라이언트는 주입기가 언제든지 의존성을 조작할 수 있도록 할 수 있다. 이는 유연성을 제공하지만, 클라이언트가 사용되기 전에 모든 의존성이 주입되고 유효한지 확인하기 어렵게 만든다.

```java

public class Client {

private Service service;

// 의존성은 setter 메서드를 통해 주입된다.

public void setService(Service service) {

if (service == null) {

throw new IllegalArgumentException("service는 null이 아니어야 합니다.");

}

this.service = service;

}

}

```

프로그램에 의존성을 주입하는 방법 중 하나는 세터 주입이며, 세터 메서드를 정의하여 주입한다.

6. 3. 인터페이스를 통한 주입

의존성을 주입하는 함수를 포함한 인터페이스를 작성하고 이 인터페이스를 구현하도록 함으로써 실행 시에 이를 통하여 의존성을 주입한다.[63] 이는 클라이언트가 주입된 서비스를 받을 수 있는 세 가지 주요 방식 중 하나이다.[29]

인터페이스 주입을 사용하면 의존성은 클라이언트에 대해 완전히 알지 못하지만, 여전히 새로운 클라이언트에 대한 참조를 보내고 받는다.

이런 방식으로, 의존성은 주입자가 된다. 핵심은 주입하는 메서드가 인터페이스를 통해 제공된다는 것이다.

클라이언트와 해당 의존성을 도입하기 위해 어셈블러가 여전히 필요하다. 어셈블러는 클라이언트에 대한 참조를 가져와 해당 의존성을 설정하는 설정자 인터페이스로 캐스팅하고, 해당 의존성 객체에 전달하며, 의존성 객체는 다시 클라이언트에 자체에 대한 참조를 전달한다.

인터페이스 주입이 가치를 가지려면, 의존성은 단순히 자체에 대한 참조를 다시 전달하는 것 외에 다른 것을 수행해야 한다. 이것은 다른 의존성을 해결하기 위한 팩토리 또는 하위 어셈블러 역할을 하여 일부 세부 정보를 주 어셈블러로부터 추상화할 수 있다. 의존성이 몇 개의 클라이언트가 사용하고 있는지 알 수 있도록 참조 개수를 셀 수도 있다. 의존성이 클라이언트 컬렉션을 유지하는 경우, 나중에 자체의 다른 인스턴스를 사용하여 모두 주입할 수 있다.

```java

public interface ServiceSetter {

void setService(Service service);

}

public class Client implements ServiceSetter {

private Service service;

@Override

public void setService(Service service) {

if (service == null) {

throw new IllegalArgumentException("service must not be null");

}

this.service = service;

}

}

public class ServiceInjector {

private final Set clients = new HashSet<>();

public void inject(ServiceSetter client) {

this.clients.add(client);

client.setService(new ExampleService());

}

public void switch() {

for (Client client : this.clients) {

client.setService(new AnotherExampleService());

}

}

}

public class ExampleService implements Service {}

public class AnotherExampleService implements Service {}

7. 예제

자바를 사용한 의존성 주입(DI) 예시를 통해 DI를 사용하지 않는 경우, 수동 DI, 그리고 DI 컨테이너를 사용한 자동 DI를 비교해 보자.

먼저, 주식 매매를 예제로 각 컴포넌트의 인터페이스를 정의한다.

```java

public interface IOnlineBrokerageService {

String[] getStockSymbols();

double getBidPrice(String stockSymbol);

double getAskPrice(String stockSymbol);

void putBuyOrder(String stockSymbol, int shares, double buyPrice);

void putSellOrder(String stockSymbol, int shares, double sellPrice);

}

public interface IStockAnalysisService {

double getEstimatedValue(String stockSymbol);

}

public interface IAutomatedStockTrader {

void executeTrades();

}

```
DI를 사용하지 않는 경우:

```java

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

private IStockAnalysisService analysisService = new StockAnalysisServiceImpl();

private IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

public void executeTrades() {

…. // omitted

}

}

public class MyApplication {

public static void main(String[] args) {

IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl();

stockTrader.executeTrades();

}

}

```

`VerySimpleStockTraderImpl` 클래스는 `IStockAnalysisService`와 `IOnlineBrokerageService` 인터페이스 구현 클래스의 인스턴스를 직접 생성하여 구현에 강하게 의존한다.
수동 DI:

```java

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

private IStockAnalysisService analysisService;

private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl(

IStockAnalysisService analysisService,

IOnlineBrokerageService brokerageService) {

this.analysisService = analysisService;

this.brokerageService = brokerageService;

}

public void executeTrades() {



}

}

public class MyApplication {

public static void main(String[] args) {

IStockAnalysisService analysisService = new StockAnalysisServiceImpl();

IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl(

analysisService,

brokerageService);

stockTrader.executeTrades();

}

}

```

`MyApplication.main()`에서 의존성을 주입하고, `VerySimpleStockTraderImpl`는 특정 구현에 의존하지 않는다. 생성자 주입 방식이다.
DI 컨테이너를 사용한 자동 DI:

```xml



VerySimpleStockTraderImpl





StockAnalysisServiceImpl





NewYorkStockExchangeBrokerageServiceImpl



```

```java

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

private IStockAnalysisService analysisService;

private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl(

IStockAnalysisService analysisService,

IOnlineBrokerageService brokerageService) {

this.analysisService = analysisService;

this.brokerageService = brokerageService;

}

public void executeTrades() {

… // omitted

}

}

public class MyApplication {

public static void main(String[] args) {

IAutomatedStockTrader stockTrader =

(IAutomatedStockTrader) DependencyManager.create(IAutomatedStockTrader.class);

stockTrader.executeTrades();

}

}

```

DI 컨테이너가 `IAutomatedStockTrader` 구현을 결정한다. 인터페이스 요청 시 설정 파일(XML)을 기반으로 `VerySimpleStockTraderImpl` 인스턴스를 반환하고, 생성자 주입을 통해 `IStockAnalysisService`와 `IOnlineBrokerageService` 의존성도 해결한다.

DI 컨테이너는 다양하며, 위 예시는 일부에 불과하다. 실제로는 컨테이너마다 다양한 기법을 사용한다.

7. 1. AngularJS

다음은 AngularJS 컴포넌트가 의존성 주입을 통해 인사 서비스를 받는 예시이다.



function SomeClass(greeter) {

this.greeter = greeter;

}

SomeClass.prototype.doSomething = function(name) {

this.greeter.greet(name);

}



각 AngularJS 애플리케이션은 의존성 구축 및 조회를 담당하는 서비스 로케이터를 포함한다.



// 모듈에서 배선 정보를 제공한다.

var myModule = angular.module('myModule', []);

// 인젝터에게 인사 서비스 구축 방법을 가르친다.

// greeter는 $window 서비스에 의존한다.

myModule.factory('greeter', function($window) {

return {

greet: function(text) {

$window.alert(text);

}

};

});



그런 다음 greeter 서비스를 포함하여 myModule 모듈에 정의된 컴포넌트를 제공하는 새로운 인젝터를 생성할 수 있다.



var injector = angular.injector(['myModule', 'ng']);

var greeter = injector.get('greeter');



서비스 로케이터 안티패턴을 피하기 위해 AngularJS는 HTML 템플릿에서 선언적 표기법을 허용하여 컴포넌트 생성을 인젝터에게 위임한다.



MyController

Hello





function MyController($scope, greeter) {

$scope.sayHello = function() {

greeter.greet('Hello World');

};

}



ng-controller 디렉티브는 인젝터가 컨트롤러 및 해당 종속성의 인스턴스를 생성하도록 트리거한다.

7. 2. C#

다음은 C#에서 생성자 주입의 예시이다.

```csharp

using System;

namespace DependencyInjection;

// 클라이언트는 어떤 구체적인 게임 패드를 사용하는지 알지 못하고 이 인터페이스만 알게 된다.

interface IGamepadFunctionality {

string GetGamepadName();

void SetVibrationPower(float power);

}

// 다음 서비스는 위 인터페이스의 구체적인 구현을 제공한다.

class XboxGamepad : IGamepadFunctionality {

float vibrationPower = 1.0f;

public string GetGamepadName() => "Xbox controller";

public void SetVibrationPower(float power) => this.vibrationPower = Math.Clamp(power, 0.0f, 1.0f);

}

class PlaystationJoystick : IGamepadFunctionality {

float vibratingPower = 100.0f;

public string GetGamepadName() => "PlayStation controller";

public void SetVibrationPower(float power) => this.vibratingPower = Math.Clamp(power * 100.0f, 0.0f, 100.0f);

}

class SteamController : IGamepadFunctionality {

double vibrating = 1.0;

public string GetGamepadName() => "Steam controller";

public void SetVibrationPower(float power) => this.vibrating = Convert.ToDouble(Math.Clamp(power, 0.0f, 1.0f));

}

// 이 클래스는 서비스를 받는 클라이언트이다.

class Gamepad {

IGamepadFunctionality gamepadFunctionality;

// 서비스는 생성자를 통해 주입되고 위의 필드에 저장된다.

public Gamepad(IGamepadFunctionality gamepadFunctionality) => this.gamepadFunctionality = gamepadFunctionality;

public void Showcase() {

// 주입된 서비스가 사용된다.

var gamepadName = this.gamepadFunctionality.GetGamepadName();

var message = $"We're using the {gamepadName} right now, do you want to change the vibrating power?";

Console.WriteLine(message);

}

}

class Program {

static void Main() {

var steamController = new SteamController();

// XboxController, PlaystationJoystick 등을 전달할 수도 있다.

// 게임 패드는 무엇을 사용하고 있는지 알 필요도 없고, 알 필요도 없다.

var gamepad = new Gamepad(steamController);

gamepad.Showcase();

}

}

```

위 코드를 수동으로 DI를 수행하도록 리팩토링하면 다음과 같다.

```java

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

private IStockAnalysisService analysisService;

private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl(

IStockAnalysisService analysisService,

IOnlineBrokerageService brokerageService) {

this.analysisService = analysisService;

this.brokerageService = brokerageService;

}

public void executeTrades() {



}

}

public class MyApplication {

public static void main(String[] args) {

IStockAnalysisService analysisService = new StockAnalysisServiceImpl();

IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl(

analysisService,

brokerageService);

stockTrader.executeTrades();

}

}

```

이 예제에서는 `MyApplication.main()`이 의존성 주입을 수행하며, `VerySimpleStockTraderImpl` 자체는 특정 구현에 의존하지 않게 되었다. 또한, 이 구현에서는 생성자 주입 기법이 사용되었다.

7. 3. Go

Go는 클래스를 지원하지 않으며, 일반적으로 의존성 주입은 반사 또는 제네릭(Go 1.18부터 지원됨[37])을 활용하는 전용 라이브러리에 의해 추상화됩니다.[38] 의존성 주입 라이브러리를 사용하지 않는 더 간단한 예시는 다음 MVC 웹 애플리케이션의 예시로 설명됩니다.

먼저, 필요한 의존성을 라우터에 전달한 다음, 라우터에서 컨트롤러로 전달합니다.

```go

package router

import (

"database/sql"

"net/http"

"example/controllers/users"

"github.com/go-chi/chi/v5"

"github.com/go-chi/chi/v5/middleware"

"github.com/redis/go-redis/v9"

"github.com/rs/zerolog"

)

type RoutingHandler struct {

// 호출 스택 아래로 포인터로 값을 전달하면

// 새 복사본을 생성하지 않아 메모리를 절약할 수 있습니다.

log *zerolog.Logger

db *sql.DB

cache *redis.Client

router chi.Router

}

// 일반적으로 main 함수에서 초기화된 연결, 로거 및 캐시

func NewRouter(

log *zerolog.Logger,

db *sql.DB,

cache *redis.Client,

) (r *RoutingHandler) {

rtr := chi.NewRouter()

return &RoutingHandler{

log: log,

db: db,

cache: cache,

router: rtr,

}

}

func (r *RoutingHandler) SetupUsersRoutes() {

uc := users.NewController(r.log, r.db, r.cache)

r.router.Get("/users/:name", func(w http.ResponseWriter, r *http.Request) {

uc.Get(w, r)

})

}

```

그런 다음, 구조체의 비공개 필드에 해당 포인터 리시버인 모든 메서드에서 캡슐화를 위반하지 않고 접근할 수 있습니다.

```go

package users

import (

"database/sql"

"net/http"

"example/models"

"github.com/go-chi/chi/v5"

"github.com/redis/go-redis/v9"

"github.com/rs/zerolog"

)

type Controller struct {

log *zerolog.Logger

storage models.UserStorage

cache *redis.Client

}

func NewController(log *zerolog.Logger, db *sql.DB, cache *redis.Client) *Controller {

return &Controller{

log: log,

storage: models.NewUserStorage(db),

cache: cache,

}

}

func (uc *Controller) Get(w http.ResponseWriter, r *http.Request) {

// 로깅을 미들웨어로 래핑할 수도 있다는 점에 유의하세요. 이것은 데모 목적입니다.

uc.log.Info().Msg("Getting user")

userParam := chi.URLParam(r, "name")

var user *models.User

// 캐시에서 사용자 가져오기

err := uc.cache.Get(r.Context(), userParam).Scan(&user)

if err != nil {

uc.log.Error().Err(err).Msg("Error getting user from cache. Retrieving from SQL storage")

}

user, err = uc.storage.Get(r.Context(), "johndoe")

if err != nil {

uc.log.Error().Err(err).Msg("Error getting user from SQL storage")

http.Error(w, "Internal server error", http.StatusInternalServerError)

return

}

}

```

마지막으로, 데이터 접근 계층에서 main 함수에서 초기화된 데이터베이스 연결을 사용할 수 있습니다.

```go

package models

import (

"database/sql"

"time"

)

type (

UserStorage struct {

conn *sql.DB

}

User struct {

Name string 'json:"name" db:"name,primarykey"'

JoinedAt time.Time 'json:"joined_at" db:"joined_at"'

Email string 'json:"email" db:"email"'

}

)

func NewUserStorage(conn *sql.DB) *UserStorage {

return &UserStorage{

conn: conn,

}

}

func (us *UserStorage) Get(name string) (user *User, err error) {

// 'name'이 고유 키라고 가정합니다.

query := "SELECT * FROM users WHERE name = $1"

if err := us.conn.QueryRow(query, name).Scan(&user); err != nil {

return nil, err

}

return user, nil

}

7. 4. HTML

HTML(HTML)과 같은 마크업 언어에서도 의존성 주입이 수행된다.

Web Components(사용자 정의 요소 + Template 요소 + Shadow DOM)의 등장으로, 거대한 HTML 파일을 작은 HTML 요소 컴포넌트의 집합으로 작성하는 것이 가능해졌다. 그러나 큰 컴포넌트 Big이 작은 컴포넌트 Small을 감싸는 형태로 코딩하면 Big이 Small에 의존하게 된다. 이때 ''slot 요소''를 사용한 의존성 주입이 수행된다. ''slot 요소''는 약한 인터페이스로 작동하며, ''slot 요소''를 사용하여 정의된 사용자 정의 요소를 사용할 때 의존성을 태그로 감싸서 주입할 수 있다.

다음 예제에서는 큰 컴포넌트 ``가 두 개의 수용 가능한 slot을 가지고 있다. 사용 시 slot을 지정한 span 요소를 삽입함으로써, ``는 span 요소에 직접 의존하지 않고 span 요소를 사용할 수 있다. 프로그래밍 언어와 같은 명시적인 인터페이스가 없기 때문에 (인터페이스에 의한 타입 지정 slot 요소가 없음) 타입 지원을 받는 안전한 의존성 주입은 현재 시점에서는 수행할 수 없지만, 적절하게 설계함으로써 의존성을 분리하는 것은 가능하다.

```html





dependency-one

dependency-two


8. 의존성 주입 프레임워크 (DI 컨테이너)

의존성 주입(DI) 기능을 제공하는 프레임워크를 DI 컨테이너라고 부른다.[42] DI 컨테이너는 수동으로 의존성 주입을 하는 과정에서 발생하는 번거로움과 오류 가능성을 줄이기 위해 사용된다. 의존성 주입 과정을 자동화하여 개발자가 직접 의존성을 관리하는 코드를 작성하지 않아도 되게 한다.[31]

DI 컨테이너는 자바, .NET, PHP 등 다양한 프로그래밍 언어에서 사용할 수 있다. 외부 설정 파일(예: XML)이나 메타데이터를 사용하여 프로그램 구성을 정의하고, 이를 바탕으로 의존성을 자동으로 주입하는 방식을 사용한다.

DI 컨테이너가 의존성 주입을 자동화하지만, 의존성 주입을 위해 DI 컨테이너가 반드시 필요한 것은 아니다.[32][33]

8. 1. Java

8. 2. .NET

 .NET Framework의 의존성 주입 컨테이너의 클래스 다이어그램.
Ninject 또는 StructureMap과 같은 컨테이너는 객체 지향 프로그래밍 언어에서 의존성 주입 및 제어 반전을 달성하기 위해 일반적으로 사용된다.


.NET에서 의존성 주입을 지원하는 주요 프레임워크는 다음과 같다.

8. 3. PHP

젠드 프레임워크 2와 Symfony 2는 PHP에서 의존성 주입을 지원하는 주요 프레임워크이다.

참조

[1] 웹사이트 Dependency Injection is Loose Coupling http://blog.ploeh.dk[...] 2015-07-28
[2] 논문 "Seuss: Decoupling responsibilities from static methods for fine-grained configurability" 2012-04
[3] 웹사이트 HollywoodPrinciple http://c2.com/cgi/wi[...] 2015-07-19
[4] 웹사이트 The Dependency Injection design pattern – Problem, Solution, and Applicability http://w3sdesign.com[...] 2017-08-12
[5] 웹사이트 Dependency Inversion vs. Dependency Injection https://betterprogra[...] 2022-12-06
[6] 웹사이트 You are Simply Injecting a Dependency, Thinking that You are Following the Dependency Inversion... https://levelup.gitc[...] 2022-12-06
[7] 웹사이트 Spring IoC Container https://docs.spring.[...] 2023-05-23
[8] 웹사이트 Inversion of Control Containers and the Dependency Injection pattern https://martinfowler[...] 2023-06-04
[9] 서적 Dependency Injection in .NET Manning Publications 2011-10
[10] 웹사이트 Dependency Injection in NET http://philkildea.co[...] 2015-07-18
[11] 웹사이트 How to explain dependency injection to a 5-year-old? https://stackoverflo[...] 2015-07-18
[12] 웹사이트 James Shore: Dependency Injection Demystified http://www.jamesshor[...] 2015-07-18
[13] 웹사이트 To "new" or not to "new"... http://misko.hevery.[...] 2015-07-18
[14] 웹사이트 How to write testable code http://www.loosecoup[...] 2015-07-18
[15] 웹사이트 Writing Clean, Testable Code http://www.ethanresn[...] 2015-07-18
[16] 웹사이트 When to inject: the distinction between newables and injectables - Invisible to the eye http://www.giorgiosi[...] 2015-07-18
[17] 웹사이트 the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns http://www.bryancook[...] 2015-07-18
[18] 웹사이트 The Dependency Injection Design Pattern https://msdn.microso[...] 2015-07-18
[19] 웹사이트 The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330 https://jcp.org/en/j[...] 2015-07-18
[20] 웹사이트 3.1. Dependency injection — Python 3: from None to Machine Learning https://python.astro[...]
[21] 웹사이트 How Dependency Injection (DI) Works in Spring Java Application Development - DZone Java https://dzone.com/ar[...]
[22] 웹사이트 Dependency injection and inversion of control in Python — Dependency Injector 4.36.2 documentation http://python-depend[...]
[23] 웹사이트 How to Refactor for Dependency Injection, Part 3: Larger Applications https://visualstudio[...]
[24] 웹사이트 A quick intro to Dependency Injection: What it is, and when to use it https://www.freecode[...] 2018-10-18
[25] 웹사이트 Dependency Injection {{!}}Professionalqa.com https://www.professi[...]
[26] 웹사이트 What are the downsides to using Dependency Injection? https://stackoverflo[...] 2015-07-18
[27] 웹사이트 Dependency Injection Inversion – Clean Coder https://sites.google[...] 2015-07-18
[28] 웹사이트 Decoupling Your Application From Your Dependency Injection Framework http://www.infoq.com[...] 2015-07-18
[29] 웹사이트 Inversion of Control Containers and the Dependency Injection pattern – Forms of Dependency Injection http://www.martinfow[...] Martinfowler.com 2014-03-22
[30] 웹사이트 AccessibleObject (Java Platform SE 7) http://docs.oracle.c[...] 2015-07-18
[31] 간행물 Framework Design: A Role Modeling Approach http://www.riehle.or[...] Swiss Federal Institute of Technology
[32] 웹사이트 Dependency Injection != using a DI container http://www.loosecoup[...] 2015-07-18
[33] 웹사이트 Black Sheep » DIY-DI » Print http://blacksheep.pa[...] 2015-07-18
[34] 웹사이트 Spring Tips: A POJO with annotations is not Plain http://springtips.bl[...] 2015-07-18
[35] 웹사이트 Annotations in POJO – a boon or a curse? {{!}} Techtracer http://techtracer.co[...] 2007-04-07
[36] 서적 Pro Spring Dynamic Modules for OSGi Service Platforms https://books.google[...] APress 2009-02-17
[37] 웹사이트 Go 1.18 Release Notes - The Go Programming Language https://go.dev/doc/g[...] 2024-04-17
[38] 웹사이트 Awesome Go – dependency injection https://github.com/a[...] 2024-04-17
[39] 웹사이트 Dependency Definition & Meaning - Merriam-Webster https://www.merriam-[...] Merriam-Webster 2022-09-03
[40] 서적 Seasar2で学ぶ DIとAOP アスペクト指向によるJava開発 技術評論社 2006-08-09
[41] 서적 オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 技術評論社 2016-09-02
[42] 웹사이트 Java開発を変える最新の設計思想「Dependency Injection(DI)」とは https://xtech.nikkei[...] ITPro 2005-02-18
[43] 웹사이트 Java EE 6: Understanding Contexts and Dependency Injection (CDI), Part 1 https://blogs.oracle[...] 오라클 2010-05-25
[44] Microsoft Docs Microsoft.Extensions.DependencyInjection Namespace https://docs.microso[...]
[45] 웹인용 The Dependency Injection design pattern - Problem, Solution, and Applicability http://w3sdesign.com[...] 2017-08-12
[46] 서적 인용 Design Patterns: Elements of Reusable Object-Oriented Software https://archive.org/[...] Addison Wesley
[47] 서적 인용 Dependency Injection in .NET Manning Publications 2011-10
[48] 웹인용 Dependency Injection in NET http://philkildea.co[...] 2015-07-18
[49] 웹인용 How to explain dependency injection to a 5-year-old? https://stackoverflo[...] 2015-07-18
[50] 웹인용 Dependency Injection is Loose Coupling http://blog.ploeh.dk[...] 2015-07-28
[51] 간행물 "Seuss: Decoupling responsibilities from static methods for fine-grained configurability”" 2012-04
[52] 웹인용 A curry of Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI) and IoC Container - CodeProject http://www.codeproje[...] 2015-08-08
[53] 웹인용 To "new" or not to "new"… http://misko.hevery.[...] 2015-07-18
[54] 웹인용 How to write testable code http://www.loosecoup[...] 2015-07-18
[55] 웹인용 Writing Clean, Testable Code http://www.ethanresn[...] 2015-07-18
[56] 웹인용 When to inject: the distinction between newables and injectables - Invisible to the eye http://www.giorgiosi[...] 2015-07-18
[57] 웹인용 Inversion of Control vs Dependency Injection https://stackoverflo[...] 2015-08-05
[58] 웹인용 What is the difference between Strategy pattern and Dependency Injection? https://stackoverflo[...] 2015-07-18
[59] 웹인용 Dependency Injection != using a DI container http://www.loosecoup[...] 2015-07-18
[60] 웹인용 Black Sheep » DIY-DI » Print http://blacksheep.pa[...] 2015-07-18
[61] 웹인용 The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330 https://jcp.org/en/j[...] 2015-07-18
[62] 웹인용 The Dependency Injection design pattern - Structure and Collaboration http://w3sdesign.com[...] 2017-08-12
[63] 웹인용 Inversion of Control Containers and the Dependency Injection pattern http://www.martinfow[...] 2004-01-23



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com