며칠 전 nestjs 주차가 시작되고 한 팀원이 '의존성 주입'에 대해 질문했을 때 내 머리 속은 그저 '객체.. 주입..'만 가득했다. 지금 돌아보니 이전까지 나는 '의존성 주입'이 무엇인지 완벽하게 오해하고 있었던 것 같다.
'의존성 주입'은 express.js를 처음 배울 때 테스트 코드 강의를 들으면서 알게 된 용어였는데, 단어 자체에서 느껴지는 뉘앙스(?) 때문에 객체 간 의존성을 높인다는 뜻으로 이해오해하고 있었던 것이다. ༎ຶ‿༎ຶ
그땐 대충 넘어갔지만 nest.js를 배우면서는 피할 수 없는 개념이므로 확실하게 이해해보자.
IoC(Inversion of Control): 제어 역전. 개발자가 직접 라이브러리와 같은 소스 코드를 불러와서 제어하는 게 아니라 프레임워크에 위임하는 것. 제어권이 역전되었다는 표현. 객체의 생명주기를 개발자가 아닌 프레임워크가 관리하므로 개발자는 비즈니스 로직에 더욱 집중할 수 있음.
DI(Dependency Injection): 의존성 주입. IoC의 방법 중 하나. 클래스를 직접 생성하지 않고 주입함으로서 객체 간 결합도를 느슨하게 함. 이를 통해 유지보수가 쉽고 재사용성을 높이는 코드를 작성할 수 있음.
(예를 들어 nest.js에서는 express.js에서 했던 것처럼 new 생성자를 통해 직접 인스턴스를 생성하는 대신, IoC 컨테이너에 provider로 등록하여 해당 클래스를 주입하여 사용함)
DTO(Data Trasfer Object): 계층 간 데이터 전송을 위한 객체.
여기서 계층이란 3계층 아키텍처 구조에서 분리되는 controller - service - repository 계층을 말한다. DTO를 직접 만들어보면서 어떤 형태의 데이터를 주고 받을지 정의하는 객체 정도로 이해하고 있었는데...
nestjs 공식문서를 보다가 새로운 사실을 알게 되었다❕
But first (if you use TypeScript), we need to determine the DTO (Data Transfer Object) schema. A DTO is an object that defines how the data will be sent over the network. We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here. Why? Classes are part of the JavaScript ES6 standard, and therefore they are preserved as real entities in the compiled JavaScript. On the other hand, since TypeScript interfaces are removed during the transpilation, Nest can't refer to them at runtime. This is important because features such as Pipes enable additional possibilities when they have access to the metatype of the variable at runtime.
공식문서에 따르면 DTO는 계층을 넘어 어떻게 데이터를 보낼지 정의하는 객체다. 그런데 여기서 중요한 것은, DTO를 정의할 때 interface와 class를 모두 사용할 수 있는데 nestjs 팀은 class로 정의하는 것을 권장한다고 한다.
왜냐하면 class는 javascript ES6 표준이고, 자바스크립트로 컴파일될 때 entitiy로서 보존되기 때문이다. 반면 interface는 타입스크립트 문법이기 때문에 트랜스파일링할 때 사라지므로 런타임 시 nest가 이를 참조할 수 없다.
왜 개발 공부 제대로 하려면 공식문서를 읽으라고 하는지 깨달은 순간이었다..! 앞으로도 공식문서 틈틈이 읽어야겠다.. ㅠㅠ