개발일기장

ExpressJS에 inversify적용해서 객체 주입받기. 본문

node.js

ExpressJS에 inversify적용해서 객체 주입받기.

게슬 2022. 8. 4. 23:33
728x90

https://tlqckd0.tistory.com/56

 

express.js 를 class형식으로 실행

Spring boot같은 프레임워크로 웹서버를 만드는 경우에는 이미 틀은 다 만들어져 있어서 기능을 집어넣는다? 라는 표현을 쓰는게 맞는것 같음. Node.js에서는 웹서버 개발을 위한 프레임워크가 많은

tlqckd0.tistory.com

여기 마지막 부분인 아래 코드 부분을 해결하기 위해서 inversify를 사용해봤음.

routerList.push(new HelloController(new HelloService(new HelloRepository(new Map<Number, String>)), "/user"));

 

https://inversify.io/

 

InversifyJS

InversifyJS is a lightweight inversion of control (IoC) container for TypeScript and JavaScript apps.

inversify.io

NodeJS에서 객체(Class)를 주입하고 관리하는것을 도와주는 패키지...고, Typescript이어야함.


일단 주입받고 주입할 객채들은 총 4가지였음 <Controller, Service, Repository, dataSoucre(Map<Number,String>) >

1. 각각의 객채를 Symbol을 통해서 정의하기

const TYPES = {
    HelloController: Symbol.for("HelloController"),
    HelloRepository: Symbol.for("HelloRepository"),
    HelloService: Symbol.for("HelloService"),
    DataSource: Symbol.for("DataSource")
}

export { TYPES }

이렇게 사용할 객체들의 이름들을 정의해둬야 나중에 필요한것을 주입받을때 사용할 수 있음.


2. inversify Container에 담기

지금까지 만들었던 객채들을 bind해주는 작업이 필요함.

container.bind<{name of class}>({type of class}).to({value of class})...{option}

1. class에는 아까 만들었던 Controller, Service 등등 Class또는 Interface로 정의된 것을 넣으면 되고

2. type에는 Symbol로 정의했던 그 값을 집어넣는다.

3. 그리고 value에는 그 실제값이 들어가면 되는다.  밑에는 name과 value가 같이 넣어놨는데(귀찮아서) 일반적으로 구현체는 직접 사용하면 확장성이나 그런면에서 불리함이 있기 때문에 interface를 name으로 넣고, value에는 class를 넣는 방식을 사용한다.

import { Container } from "inversify";
import { HelloController } from "../controller/hello.controller";
import { HelloRepository } from "../repository/hello.repository";
import { HelloService } from "../service/hello.service";
import { TYPES } from "./types";

const myContainer = new Container();
myContainer.bind<HelloService>(TYPES.HelloService).to(HelloService).inSingletonScope();
myContainer.bind<HelloController>(TYPES.HelloController).to(HelloController).inSingletonScope();
myContainer.bind<HelloRepository>(TYPES.HelloRepository).to(HelloRepository).inSingletonScope();
myContainer.bind<Map<Number, String>>(TYPES.DataSource).toConstantValue(new Map<Number, String>);
export { myContainer };

 

1. 끝에 inSingletonScope()를 하면 Spring처럼 싱글톤으로 구성이 됨.

2. 그리고 DataSource의 경우에는 일반적으로 DB pool을 사용하면 되겠지만, 당장은 간단하게 Map<Number,Stirng> 으로 정의했음. -> 바로 값을 집어넣고 싶으면 toConstantValue( {value} )를 집어넣으면 된다.


3. Class에 inject하기

ex) HelloRepository

import "reflect-metadata";

@injectable()
class HelloRepository {
	...
    constructor(@inject(TYPES.DataSource) dataSource: Map<Number, String>) {
        this.dataSource = dataSource;
    }
    ....
    
  }

일단 import "reflect-metadata"를 해줘야한다. 그래야 런타임때에 어노테이션(@)가 적용된다.

Class위에는 @injectable()를 작성하고,

constructor에는 주입받고 싶은 값 앖에 @inject({Symbol정의한것})을 집어넣어주면 된다.


4. 직접 객체 가지고오기

위에서 작성했던 container에서 원하는 Class와 Type을 작성하면 된다.

BEFORE

routerList.push(new HelloController(new HelloService(new HelloRepository(new Map<Number, String>)), "/user"));

AFTER

    const helloConrtoller = myContainer.get<HelloController>(TYPES.HelloController);
    routerList.push(helloConrtoller);

훨신 깔끔해졌다.

지금까지 했던 프로젝트들 이런 방식으로 조금씩 리펙토링 해봐야할것같다.

 

전체코드는

https://github.com/tlqckd0/inversify-express

 

GitHub - tlqckd0/inversify-express: study-example

study-example. Contribute to tlqckd0/inversify-express development by creating an account on GitHub.

github.com


아직 궁금한점은

1. Nodejs에서는 원래 import(require), export(module.export)를 사용할떄 cache가 되어서 singleton이 기본적으로 적용되는것으로 봤는데 큰 차이가 있는가.? toSingleton은 어떻게 작동함?

2. toConstantValue를 사용했을떄 Singleton이 됨?

3. "reflact-metadata"가 작동하는것.. 

 

이거 찾아봐야겠다. 

 

728x90
Comments