의존성 주입(Dependency Injection, DI)

cabbage·2023년 1월 28일
0

기타

목록 보기
20/26

의존성 주입(Dependency Injection)

의존성 주입이란 하나의 객체 또는 함수가 의존하는 다른 객체 또는 함수들을 전달 받는 디자인 패턴이다. IoC(Inversion of Control, 제어 역전)의 한가지 형태인 의존성 주입은 객체 구성과 객체 사용을 분리해서, 느슨한 결합 상태의 프로그램을 만드는 것을 지향한다.

의존성 주입은 주입 받은 서비스를 사용하는 객체나 함수가 서비스를 구성하는 방법을 알 필요가 없게 한다. 그 대신, ’주입기(injector)’를 사용해 서비스를 전달 받는 클라이언트(객체나 함수) 에게 의존성을 제공한다. 의존성 주입은 암시적인 의존성을 명시적으로 만들어 아래 문제들을 해결하는데 도움을 준다.

  • 어떤 클래스가 의존하고 있는 객체의 생성으로부터 어떻게 독립할 수 있을까
  • 코드를 직접 수정하지 않고 어떻게 코드의 동작을 변경할 수 있을까

기본적으로 의존성 주입은 메서드의 파라미터로 전달하는 것으로 이루어진다. 클라이언트는 스스로 서비스를 빌드하지 않기 때문에 클라이언트가 사용하는 서비스에 대한 인터페이스를 선언하기만 하면 된다.

의존성 주입의 구성요소

의존성 주입의 구성요소 4가지

  • 서비스
  • 클라이언트
  • 인터페이스
  • 주입기

서비스와 클라이언트

  • 서비스는 유용한 기능을 포함하고 있는 클래스이다.
  • 클라이언트는 서비스를 사용하는 클래스이다.
  • 어떤 객체라도 서비스 또는 클라이언트가 될 수 있다.
  • 같은 객체라도 클라이언트와 서비스 둘 다 될 수 있다.

인터페이스

  • 클라이언트는 의존성의 구현 방법에 대해서는 알아서는 안된다. 클라이언트는 의존성 이름과 API만 알아야 한다.
  • 예를 들어, 이메일을 수신하는 서비스의 경우 내부적으로 IMAP 또는 POP3 프로토콜을 사용할 수 있다. 하지만 이런 세부사항들은 이메일을 수신하려는 코드와는 관련이 없다고 볼 수 있다.
  • 구현 세부사항을 무시함으로써, 의존성이 변경될 때 클라이언트를 변경할 필요가 없어진다.

주입기(Injector)

  • 주입기는 어셈블러, 컨테이너, 프로바이더 또는 팩토리라고도 한다.
  • 주입기는 클라이언트에게 서비스를 소개한다.
  • 주입기의 역할은 클라이언트와 서비스 객체들의 복잡한 그래프를 구성하고 연결하는 것이다. 주입기는 많은 객체들과 함께 작동하지만 클라이언트가 될 수는 없다.

의존성 주입의 장점

  • 클래스와 의존성 사이의 결합을 줄일 수 있다. (클래스와 의존성 사이의 느슨한 결합)
  • 의존성 구현에 대해 클라이언트가 신경 쓰지 않기 때문에 프로그램의 재사용성이 높아지고 유지보수가 편리해진다.
  • 클라이언트는 인터페이스를 지원하는 모든 작업을 수행할 수 있다.
  • 모든 의존성 생성이 단일 구성요소에 의해 처리되므로 boilerplate 코드를 제거할 수 있다.
  • 동시 개발을 할 수 있게 해준다.
    • 서로를 사용하는 클래스들을 독립적으로 개발할 수 있다.
    • 클래스가 커뮤니케이션할 수 있는 인터페이스만 알고 있으면 된다.

의존성 주입의 단점

  • 코드가 분리되어 있기 때문에 코드 추적이 어렵다.
  • 일반적으로 개발의 사전 노력이 더 많이 요구된다.
  • 프레임워크에 대한 의존도를 높인다.

의존성 주입의 종류

의존성 주입의 3가지 종류

  • 생성자 주입(Constructor Injection) - 의존성은 클라이언트의 생성자(constructor)로 제공된다.
  • Setter 주입(Setter Injection) - 클라이언트는 의존성을 받아 들이는 setter 메서드를 노출한다.
  • 인터페이스 주입(Interface Injection) - 의존성의 인터페이스가 injector 메서드를 제공한다. Injector 메서드는 클라이언트에게 의존성을 주입한다.

의존성 주입을 사용하지 않은 경우

아래 예제에서 Client 클래스는 Service 변수를 생성자를 통해 초기화한다.
클라이언트는 하드코딩된 의존성을 생성하여 서비스를 직접 구성하고 제어한다.

class Client {
	service: Service;

	constructor() {
		this.service = new Service();
	}
}

생성자 주입(Constructor Injection)

생성자 주입은 의존성 주입의 가장 일반적인 형태로 생성자를 통해 의존성을 요청하는 방식이다.
클라이언트는 필수적인 의존성과 함께 인스턴스화된다.

class Client {
	// 의존성이 생성자를 통해 주입된다.
	constructor(private service: Service) {
		this.service = service;
	}
}

Setter 주입(Setter Injection)

Setter 주입은 생성자가 아니라 setter 메서드를 통해 의존성을 주입한다.
언제든지 의존성을 클라이언트에 주입할 수 있다.
유연성이 높은 방식이지만, 모든 의존성이 주입됐는지 확인하기 어렵다.

class Client {
	service;

	// 의존성이 setter 메서드를 통해 주입된다.
	setService = (service) => {
		this.service = service;		
	}
}

조합하기

의존성 주입을 구현하는 가장 간단한 방법은 프로그램의 루트에서 직접 클라이언트에게 서비스를 주입하는 것이다.

// 서비스 생성
const service = new Service();

// 클라이언트에 서비스 주입
const client = new Client(service);

참고

profile
캐비지 개발 블로그입니다. :)

0개의 댓글