태그
목차

의존성 관리하기

생성일: 2024-06-10

수정일: 2024-06-10

모노레포에서 의존성은 두 종류로 나눌 수 있다.

{
  "dependencies": {
    "next": "latest", // 외부 의존성
    "@acme/ui": "*" // 내부 의존성
  }
}

의존성 설치 모범사례

의존성은 사용되는 곳에 직접 설치한다

모노레포에서 의존성을 설치할 때는, 그 의존성을 실제로 사용하는 패키지에 직접 설치하는 게 좋다.

이 원칙은 외부 패키지든 내부 패키지든 동일하게 적용된다.

이렇게 하면 각각의 package.json 파일이 해당 패키지가 필요로 하는 의존성을 정확하게 명시하게 된다.

Note

몇몇 패키지 매니저(예: Yarn, pnpm)는 의존성을 설치할 때 각 패키지의 디렉토리가 아닌 프로젝트 루트에 node_modules 를 만든다.

하지만 이는 단지 패키지 매니저의 동작 방식일 뿐, 우리가 의존성을 어디에 명시해야 할지와는 별개의 문제다.

여전히 의존성은 그것을 실제로 사용하는 패키지의 package.json 에 명시하는 것이 원칙이다.

다음과 같이 여러 패키지에 동시에 의존성 설치할 수 있다.

npm install jest --workspace=web --workspace=@repo/ui --save-dev

여기에는 몇 가지 이점이 있다.

Note

Docker를 사용할 때는 이미지의 크기를 고려해야한다.

이미지가 클수록 빌드, 배포 시간이 길어지며, 레지스트리의 스토리지 비용까지 늘어난다.

문제는, 도커 이미지를 만들 때 사용하지도 않는 파일들까지 함께 포함되기 쉽다는 것이다.

Dockerfile 에서 COPY 명령으로 패키지 디렉토리를 통째로 복사하면, 쓰지도 않는 의존성까지 이미지에 들어가 버린다.

바로 여기서 Turborepo의 prune 이 도움이 된다.

prune 기능을 사용하면, Turborepo는 각 패키지의 실제 의존성을 분석해서, 정말로 필요한 것들만 도커 이미지에 포함시킨다.

예를 들어 레포지토리에 web, api, db 세 개의 패키지가 있는데, 지금 도커 이미지를 빌드하는 건 web 뿐이라고 해보자.

이때 Turborepo는 web 패키지의 의존성만 따로 추려서 도커 이미지에 포함시킨다. apidb 에서 쓰이는 의존성은 과감히 버린다.

이 과정에서 Turborepo는 의존성 정보를 정확히 알고 있어야 한다. 이를 위해 Turborepo는 lockfile을 사용한다.

만약 각 패키지별로 의존성이 잘 나뉘어 설치되어 있다면, Turborepo는 lockfile만 보고도 어떤 패키지가 어떤 의존성을 쓰는지 정확히 판단할 수 있다.

하지만 만약 모든 의존성이 루트에 뭉텅이로 설치되어 있다면? lockfile만 봐서는 어떤 의존성이 web 에서 쓰이는지 알 수 없다.

즉 Turborepo의 prune 을 효과적으로 사용하려면, 의존성을 각 패키지에 잘 분배해서 설치해야 한다.

루트의 의존성은 최소화한다

각 패키지에서 사용하는 의존성을 그 패키지에 직접 설치하는 원칙을 따른다면, 자연스레 루트 디렉토리에는 의존성이 거의 없게 될 것이다.

루트에는 오직 레포지토리 전체를 관리하기 위한 도구들, 예를 들어 turbo, husky, lint-staged 같은 것들만 설치하면 된다.

의존성 관리하기

Turborepo는 의존성을 관리하지 않는다

Turborepo 자체는 의존성 관리에 직접 관여하지 않는다.

의존성 관리는 전적으로 npm, yarn, pnpm 같은 패키지 매니저의 역할이다.

패키지 매니저가 하는 일을 좀 더 구체적으로 말하자면 다음과 같다.

반면 Turborepo는 이런 일들에 직접 관여하지는 않는다.

Turborepo의 주된 관심사는 태스크 실행과 캐싱, 증분 빌드 같은 것들이다. 의존성 관리는 어디까지나 패키지 매니저에게 위임한다.

앞서 언급한 내용들은 모노레포를 효과적으로 관리하기 위한 제안일 뿐, Turborepo가 강제하는 사항은 아니다.

node_modules 위치

패키지 매니저(npm, yarn 등)는 프로젝트에 필요한 라이브러리(의존성)를 관리한다.

이 의존성들은 package.json 파일에 명시되어 있다.

패키지 매니저는 package.json을 읽고, 명시된 의존성을 다운로드하여 node_modules 디렉토리에 저장한다.

node_modules 는 프로젝트 루트 또는 개별 패키지 내에 위치할 수 있다.

프로젝트 구조와 패키지 매니저 설정에 따라 node_modules의 위치가 달라질 수 있다.

스크립트와 작업이 필요한 의존성을 찾을 수 있다면, node_modules 의 위치에 상관없이 패키지 매니저는 제대로 작동하고 있는 것이다.

코드에서 `node_modules` 참조

코드에서 node_modules 를 직접 참조하는 것은 좋지 않은 방법이다. 그 이유는 다음과 같다:

  • node_modules 의 위치는 변경될 수 있다. 패키지 매니저나 프로젝트 구조에 따라 node_modules 의 위치가 달라질 수 있기 때문이다.
  • 직접 참조하면 코드의 이식성이 떨어진다. 다른 개발자나 다른 환경에서는 node_modules 의 위치가 다를 수 있기 때문에, 코드가 제대로 작동하지 않을 수 있다.
  • 패키지 매니저의 기능을 제대로 활용할 수 없다. 패키지 매니저는 의존성을 자동으로 관리해주는 도구인데, node_modules를 직접 참조하면 이 기능을 사용할 수 없게 된다.

따라서 node_modules 를 직접 참조하기보다는, Node.js 모듈 시스템을 사용하는 것이 좋다.

의존성을 동일한 버전으로 유지하기

모노레포는 여러 개의 패키지나 프로젝트가 하나의 저장소에 함께 있는 구조다.

이 경우, 각 패키지들이 서로 다른 버전의 의존성을 가지고 있으면 관리가 복잡해질 수 있다.

때문에 모노레포 관리자들은 모든 패키지에서 의존성 버전을 동일하게 유지하는 것을 선호하는 경우가 있다.

이를 달성하는 방법은 다음과 같다.

관련 도구 사용

syncpack, manypkg, sherif 와 같은 도구들은 모노레포 내의 패키지들 간 의존성 버전을 동기화하는 데 특화되어 있다.

패키지 매니저 사용

npm, yarn 과 같은 패키지 매니저들은 의존성 버전 관리 기능을 제공한다.

npm install typescript@latest --workspaces

IDE 사용하기

IDE의 리팩터링 도구를 사용하면 저장소의 모든 package.json 파일에서 한 번에 의존성의 버전을 찾아 바꿀 수 있다.

검색 패턴에 "next": ".*" 와 같은 정규식을 package.json 파일에 사용하여 원하는 버전으로 교체할 수 있다.

완료되면 npm install 등 패키지 매니저의 설치 명령을 실행하여 lockfile을 업데이트해야 한다.