Dependancy Injection 에 대해 알아본다.
의존성이란, 의존대상 B가 변하면, 그것이 A에 영향을 미친다.
예) A클래스에서 B클래스를 (임포트)가져와서 A클래스의 비즈니스 로직에 고정되어 사용되기 때문에 A 클래스 로직이 변경되면 B에 영향을 준다.
import { AppServie } from './app.service';
@Controller()
export class AppController {
@Get()
getHello() {
console.log('hello');
return new AppService().getHelllo();
}
}
위 코드를 보면 AppService 클래스가 import 되어 new 키워드로 인스턴스가 생성된 후 AppController 클래스의 구현부에 사용되고 있다.
크게 문제 없어 보이는 코드인데 아래 상황을 생각해보자.
Q. Controller 의 비즈니스 로직에서 전체 요구사항은 그대로 가져가면서 다른 B Service 클래스 를 사용해야 한다면 어떻게 할까?
A. B 클래스를 가져와 기존 service 와 교체해줘야 한다.
이게 쉽게 된다면 상관 없지만 로직이 복잡하다면 모든 부분을 일일히 교체해줘야 할 것이다.
이것을 두 클래스가 강결합되어 있다. 혹은 의존성을 가진다 라고 말한다.
만약 절대 바뀌지 않을 것이라는 확신이 있다면 그냥 둬도 된다.
하지만 미래에 어떻게 될지 아무도 모르기 때문에 가능하면 폼이 많이 들지 않는선에서 미래를 유연하게 대응할 필요도 있다.
이미 우리의 개발 선배님들이 많은 고민 끝에 생각한 방법이 존재한다.
의존관계를 외부에서 결정하고 주입하는 방법이다.
클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
위에서 말한대로 인터페이스만 의존하고 실제 알맹이(레퍼런스)는 외부에서 주입해준다.
import { AppServie } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService) {}
@Get()
getHello() {
console.log('hello');
return this.appService.getHello();
}
}
생성자를 통해 외부에서 주입된 appService 는 Controller 의 get 메소드에서 사용된다.
이는 함수로 본다면 매개변수(parameters)를 지정(interface)해두고 런타임에 주입되는 인자(arguments)에 따라 함수의 결과 생성이 달라진다.
완전히 같진 않지만, 간단히 생각해보면 함수에서의 인자를 받는 것과 같이 클래스도 다른 레퍼런스를 주입받는 것으로 생각해보면 왜 DI패턴을 사용하는지 쉽게 이해할 수 있다.