Nest.js 빌드 후 진입점이 이상한 위치에 생기는 이유는?

Nest.js 빌드 후 진입점이 이상한 위치에 생기는 이유는?

레거시 Nest.js 백엔드를 재작성하는 솔루션을 진행하던 중, 특이한 일이 발생했다. 재사용성과 유지보수성을 위해 공용 라이브러리 및 DB 스키마 등을 별도의 GitHub Private Packages로 분리해두고 있었는데, 그러던 중 갑자기 Nest.js 백엔드 빌드가 안 되는 문제가 발생한 것이다.

처음에는 Error: Cannot find module '[CENSORED]/dist/admin' 같은 에러가 떴다. 문제는 소비측 프로젝트와 라이브러리 패키지 프로젝트 둘 다에서 package.json, tsconfig.json, nest-cli.json 등 빌드에 관련된 파일을 하나도 건드리지 않았다는 것이다.


첫 번째 시도: nest start 진입점 변경

시간에 쫓기는 상황이라, 급하게 dist/ 디렉터리 구조를 파악한 뒤 소스 루트를 통째로 옮겨보려고 했다. 그런데 여기서 또 특이한 점이 있었는데, 디렉터리 구조가 평탄화된 상태가 아니라 재귀적인 구조로 바뀌어 있었다.

예를 들어, 이전에는 빌드하면 다음과 같은 디렉터리 구조가 나타났다.

proj
- dist
  - main.ts
  - ...

그런데 지금은 아래와 같이 변경되어 있었다.

proj
- dist
  - lib
    - [라이브러리 프로젝트명]
      - src
        - ...
  - [프로젝트명]
    - src
      - main.ts
      - ...

뭔가 괴상하다는 느낌은 있었지만, 시간이 없어서 npm 스크립트에서 nest start 부분의 진입점을 변경해 시작해보려 했다. 당연히 문제가 발생했다.


문제 발생: This is likely not portable...

문제는 바로 아래와 같은 에러였다.

src/decorators/public.decorator.ts:3:14 - error TS2742: 
The inferred type of 'IsPublic' cannot be named without a reference to 
'../../../lib/[라이브러리 프로젝트명]/node_modules/@nestjs/common'. This is likely not portable. A type annotation is necessary.

지금 생각해보면 여기서 이미 해결의 실마리를 다 제공해줬었는데 — 당시 24시간 넘게 깨어있고 4시간도 채 자지 못한 채 지진 재난 알림에 깬 상태였으니 그런 걸 곰곰이 생각할 여유가 없었다.

그래서 내가 한 것은 단순히 해당 소스에서 명시적으로 타입을 지정해 주는 것이었지만, 당연히 해결되지 않았다.


삽질, 그리고 진정한 원인 파악

ChatGPT o3-mini-high와 o1 pro는 둘 다 tsconfig, package.json, nest-cli 설정을 건드려보라는 개소리를 하던 와중에, 피곤하고 답답한 상태에서 이 문제가 계속 풀리지 않으면 자다가 뒤질 수도 있겠다는 생각이 들었다. 그러던 중 ChatGPT o1 pro가 제안한 방법을 적용하다가 뜻밖에 실마리를 잡게 되었다.

ChatGPT는 내게 tsconfig.build.json에서 compilerOptions.rootDir"src"로 지정하라는 얘기를 해줬다. 지푸라기라도 잡듯이 일단 적용하고 빌드를 돌려보니 다음과 같은 에러가 나더라.

src/interceptors/common-api-response.interceptor.ts:1:35 - error TS6059: 
File '[CENSORED]/lib/[라이브러리 프로젝트명]/src/dto/api.dto.ts' is not under 'rootDir' '[CENSORED]/[프로젝트명]/src'. 'rootDir' is expected to contain all source files.

1 import { CommonApiResponse } from "../../../lib/[라이브러리 프로젝트명]/src/dto/api.dto";

이때 아차 싶었다. CENSORED를 남발해서 상황이 헷갈릴 수 있겠지만, 라이브러리를 패키지로 참조하는 게 아니라 상대 경로로 파일 시스템 상에서 직접 참조하고 있었던 것이었다.

즉, 원래라면 @priv-package-name/library-name에서 import되어야 하는데, ../../../lib/library-name으로 import되고 있던 것이다. 물론 내가 일부러 그런 건 아니었다. 기존 프로젝트에 있던 소스를 잘라내어 붙여넣는 과정에서 IDE(WebStorm)가 경로를 자기 멋대로 수정해버린 결과였다.

또한, 이렇게 상대 경로로 import된 라이브러리는 TypeScript에 의해 동일한 로컬 프로젝트 내의 소스로 간주되어, 마치 모노레포처럼 처리된 것이다.

하...


해결

해결은 간단했다. 해당 소스에서 상대 경로로 import되던 부분을 올바른 패키지에서 import하도록 수정하였다. 그러고 나서 빌드를 다시 돌리니 문제가 해결되었고, 한 시간이 날아갔다.

수면 부족이 이렇게 위험하다는 걸 시즌 131,072번째 깨닫고, 다시 일하러 간다.

댓글

이 블로그의 인기 게시물

C# 남아도는 메모리에도 불구하고 OutOfMemoryException이 발생한다면?

USB를 뒤는 괜찮은데 앞에 꽂으면 인식이 힘들다?

테일즈위버 OST 전곡 모음