TypeScript와 typedi로 의존성 주입 이해하기

Alvin
5 min readApr 21, 2020

--

개요

얼마전, 이곳에서 의존성 주입을 접했습니다. TypeScript/Express로 개발할 때 도움이 많이 될 것 같아서 정리해보려고 합니다.

의존성

의존성은 구성 요소들이 서로 의존하는 성질입니다.

위 코드에서 Programmer 클래스는 Laptop 클래스에 의존성을 띕니다.

대표적인 문제

구성 요소가 의존성을 띄게되면 변경에 민감해집니다.

위의 예제에서 Programmer가 평범한 Laptop이 아닌 Macbook을 사용하도록 만들고 싶습니다. 이때 아래와 같이 코드를 수정할 수 있습니다.

Laptop 클래스를 상속받는 Macbook 클래스를 생성했습니다. Programmer 클래스의 laptop 변수가 Macbook 클래스의 객체를 할당받도록 수정해주면 됩니다.

하지만 문제가 하나 있습니다. Programmer 클래스가 Laptop 클래스에 의존하고 있었기 때문에 Programmer 클래스를 직접 수정해야 했다는 점입니다.

이렇게 의존성하나를 변경하면 그것에 의존하는 다른 것들도 모두 변경하도록 만듭니다.

그러나 다행히도 우리는 의존성 주입을 통해서 문제를 해결할 수 있습니다.

의존성 주입

의존성 주입이란 말그대로 외부에서 의존성을 주입해주는 것을 말합니다. 아래의 예제를 봅시다.

맨 처음에 보았던 예제에서 의존성 주입을 사용해 보았습니다. Programmer 클래스가 의존하던 Laptop 클래스의 객체를 외부에서 주입해주고 있습니다.

이번에는 Programmer가 Macbook을 사용하게 만들고 싶습니다. 아래의 예제를 봅시다.

위에서 문제가 발생했던 예제와 차이가 느껴지시나요? Laptop 클래스의 객체 대신 Macbook 클래스의 객체를 주입해주면 Programmer 클래스를 수정하지 않고도 매우 편리하게 변경사항을 적용할 수 있습니다. 이것이 의존성 주입을 사용하는 이유입니다.

하지만 의존성 주입만 사용하는 것도 문제를 만듭니다. 그래서 우리는 의존 관계 역전의 원칙을 알아볼 것입니다.

의존 관계 역전의 원칙

의존 관계 역전의 원칙이란 SOLID 원칙 중 하나로, 변화하기 쉬운 것 보단 변화하기 어려운 것에 의존하라는 원칙입니다.

예를 들면, 프로그래머들이 구체적으로 어떤 기종(Macbook, Gram 등등..)을 사용할지는 변하기 쉬운 것입니다. 그러나 여기서 프로그래머들이 노트북으로 코딩을 한다는 점은 변하기 어렵습니다.

아래의 예제를 봅시다.

Macbook 클래스와 Gram 클래스가 Laptop 인터페이스를 구현하고 있습니다. 그리고 Programmer 클래스의 생성자는 Laptop 타입의 인자를 받고 있습니다.

따라서 모든 클래스가 Laptop이라는 인터페이스에 의존하게 되고, 이는 일반적인 의존 관계(하위 클래스가 상위 클래스에 의존)와 다르다는 것을 알 수 있습니다.

여기서 Macbook, Gram과 함께 삼성 노트북도 사용하고 싶다면 Laptop 인터페이스를 구현하는 삼성 노트북 클래스를 만들면 됩니다. 삼성 노트북 클래스의 인스턴스를 Programmer에게 주입하더라도 어떤 문제도 발생하지 않습니다.

즉, 이러한 의존 관계는 유연한 확장이 가능하게 만들고, 변경이 불필요하도록 만듭니다. 이는 SOLID 원칙 중 개방-폐쇄 원칙이 지켜진 것입니다.

하지만 아직 만족하긴 이릅니다. 더 아름다운 개발을 위해서 제어권의 역전에 대해서 알아봅시다.

제어권의 역전

의존성 주입만 사용하게 되면 우리가 직접 의존성을 관리해야 합니다. 하지만 클래스가 가질 수 있는 의존성은 무한합니다. 따라서 새로운 객체를 생성할 때 마다 직접 주입해 주는 것은 지루하고 비효율적입니다.

그래서 의존성 주입 도구인 typedi가 존재합니다. 아래의 예제를 봅시다.

이전과 차이가 느껴지시나요? 인스턴스화가 필요한 클래스에 Service 데코레이터를 붙여주고, 필요한 의존성은 클래스 내부에 선언해주면 typedi가 알아서 의존성을 주입합니다.

우리는 직접 의존성의 인스턴스를 생성하지 않아도 됩니다. typedi에게 의존성 정보를 알려주고(constructor에 선언) 필요할 때 요구(Container.get)하면 됩니다.

즉, 우리가 아니라 typedi가 제어권을 갖는 주체로 동작합니다. 이를 제어권의 역전이라고 부릅니다.

마무리

처음으로 서버 인프라와 관련되지 않은 내용의 글을 쓴 것 같습니다. 개선 과정도 좋지만 중요한 개념을 정리하는 것도 좋은 것 같습니다. 덕분에 헷갈리던 부분을 확실하게 알아갈 수 있었습니다. 하루빨리 성장해서 더 많은 지식을 공유하고 싶네요.

typedi에 대한 자세한 내용은 이곳을 참고해주세요.

--

--