TypeScript 타입만 정의해도 JavaScript 런타임에서 타입 검증이 되는 typescript-json 라이브러리를 봤다. TypeScript 타입이 런타임에 영향을 미친다는 것은 내 상식으로는 잘 이해되지 않아서 어떤 원리인지 살펴보니 ttypescript라는 툴을 사용한다는 것을 알아냈다. ttypescript는 처음보는 툴이었지만 babel에서 jsx를 js로 변환하는 것과 유사하게 TypeScript 컴파일할 때 변환이 이루어지는 것 같았다. 평소와 같았으면 여기서 끝났을 수도 있었지만, 당시 Node.js 사이드 프로젝트에서 의존성 주입을 깔끔하게 하는 방법을 고민하고 있던 때여서 ttypescript와 DI를 연결지어 생각하게 되었다. ttypescript에서는 tsconfig의 compilerOptions에 plugins라는 옵션을 사용하는데, 이 옵션은 tsc compilerOptions 문서에 없는 것을 보아 표준이 아닌 듯 했다. 심지어 ttypescript를 사용한 라이브러리 중 하나인 ts-nameof는 README에 `Recommend: Don't use this package` 라고 크게 박아두기도 했다. 그렇지만 나는 JS/TS의 불편한 DI 도구를 사용하지 않아도 된다는 유혹을 이기지 못했다. InversifyJS, TypeDI, TSyringe 등 TS/JS의 DI 도구는 인터페이스와 클래스를 연결하는 코드를 따로 작성해야 하는데, 이 부분은 번거롭기도 하고 타입 안정성을 해치는 주범이 되기도 한다. TypeScript가 컴파일할 때 인터페이스를 없애버리는 것이 불편한 형태로 코드를 작성해야 하는 근본적인 원인이기 때문에 ttypescript로 해결할 수 있다는 생각을 한 것이다. 그렇지만 아무리 불법적인 ttypescript를 사용하더라도 인터페이스 타입을 직접 구현제로 치환하는 것은 부작용이 심할 것 같아서 다른 방법을 강구했다. 그래서 떠올린 방법이 인터페이스를 구현하는 클래스에 어떤 인터페이스를 구현했는지에 대한 메타데이터와 파라미터에 어떤 타입의 인스턴스를 주입해야 하는지에 대한 메타데이터를 남기는 것이었다. 메타데이터가 런타임에 영향을 거의 주지 않고 정보를 줄 수 있는 유일한 방법이라고 생각했기 때문이다. 기본 아이디어는 생각해냈지만, 당시에는 TypeScript compiler API와 ttypescript애 대해 개념만 알고 있고 사용하는 방법은 전혀 모르는 상태였기 때문에 일단 PoC를 진행했다. ttypescript에 있는 예제들을 보면서 어떻게 변환해야 하는지 감을 잡았고, TypeScript AST Viewer로 구문을 입력해보면서 무엇을 변환해야하는지 알아냈다. 그리고 원하는 클래스와 파라미터에 메타데이터를 넣는 것에 성공하여 실현 가능한 아이디어라는 것을 증명할 수 있었다. 메타데이터를 붙이는 tramsform 코드를 작성한 후에는 실제로 런타임에 의존성을 주입하는 데코레이터를 작성헀다. 사이드 프로젝트에서만 사용할 것이기 때문에 최대한 심플하게 자동으로 의존성을 주입하는 방법을 택했다. TSyringe의 autoInjectable 데코레이터를 참고해서 대상 class에 인스턴스를 생성해서 주입하는 코드까지 완성했다. 라이브러리 이름으로 di, inject에 관련된 것으로 여러 가지를 생각했는데 이미 많은 이름이 선점당한 상태였다. 그래도 다행히 autoinjection이라는 그럭저럭 괜찮은 이름을 지어서 npm에 배포했다. 아직 인터페이스에 여러 구현체가 있을 때 동작이 정해지지 않았고 추상 클래스에서 작동하지 않는 등 문제점이 많다. 그래도 일단은 내 사이드 프로젝트에서만 사용할 것이기 때문에 YAGNI 원칙을 충실하게 따랐다. 그리고 원래 목적인 사이드 프로젝트에 적용했는데 당연히 여러 이슈가 발견되었다. 가장 골머리를 썩인 문제는 TypeScript incremental 컴파일이 ttypescript를 지원하지 않는 것이었다. 원래 개발 환경에서 TypeScript의 incremental 옵션을 사용해서 변경 사항만 컴파일을 하고 있었는데, 이제는 수정할 때마다 전체 컴파일을 돌려야할 판이었다. 그래서 ts-node를 transpileOnly 옵션을 켜서 적용해 보았지만 ts-node도 ttypescript를 지원하지 않았다. 결국에는 vite를 적용해서 개발 환경을 개선할 수 있었는데 굉장히 많은 시간이 필요했다. 처음 만들 때는 혹시 프로덕션에서도 사용할 수 있지 않을까 하는 약간의 기대가 있었는데 한참 삽질을 한 지금은 프로덕션의 ㅍ자도 생각나지 않는다. 모든 기술에는 트레이드오프가 있다고 하지만 ttypescript를 사용한 개조는 득보다 실이 훨씬 큰 것 같다. 역시 어른들이 하지 말라는 것은 다 이유가 있었다.