개요
이번 블로그 포스트에서는 Yarn의 Workspaces를 사용하여 모노레포를 구성하는 과정에서 각 프로젝트에 필요한 의존성(라이브러리)을 추가할 때, 의존성이 추가되는 과정에서 발생하는 Hoisting에 대해서 알아보도록 하겠습니다.
블로그 시리즈
이 블로그는 시리즈로 제작되었습니다. 다음 링크를 통해 다른 블로그 포스트도 확인해 보시기 바랍니다.
- [프로젝트 관리] 리포지토리 전략
- [JavaScript] 모노레포(Monorepo)를 위한 도구들
- [Monorepo] NodeJS의 모듈 불러오기
- [Monorepo] Symlink
- [Monorepo] Yarn Workspaces
- [Monorepo] Yarn Workspaces의 Hoisting
- [Monorepo] Yarn Workspaces의 명령어
- [Monorepo] pnpm을 사용하여 모노레포 만들기
NodeJS의 모듈 불러오기
NodeJS에서 import를 사용하여 모듈을 불러올 때, NodeJS는 우선 루트 폴더의 node_modules를 확인하고 해당 모듈이 없는 경우, 부모의 node_modules를 확인하고, 이를 반복하여 최상위 폴더의 node_modules까지 확인합니다.
이번 블로그 포스트에서 소개할 Hoisting은 NodeJS의 모듈 불러오기 과정을 활용한 기능입니다.
의존성 Hoisting
Yarn의 Workspaces를 사용하여 모노레포를 구성하는 과정에서 각 프로젝트에 필요한 의존성을 추가하면, 효율적으로 의존성을 관리하기 위해 해당 의존성은 Hoisting되어 최상위 폴더의 node_modules에 설치됩니다.
의존성의 Hoisting은 다음과 같은 장점을 가집니다.
- 각 프로젝트에서 공통으로 사용되는 의존성은 한번만 설치되어 메모리를 절약할 수 있습니다.
- 동일한 버전의 의존성과 다른 버전의 의존성을 분리하여 관리하기 때문에 의존성의 버전 충돌을 방지할 수 있습니다.
- 동일한 의존성을 설치하는 경우, 한번만 설치를 하므로 빠르게 설치가 가능하고 이미 설치된 의존성을 사용하는 경우 추가로 설치하지 않기 때문에 캐싱 효과를 얻을 수 있습니다.
예제
의존성의 Hoisting을 확인하기 위해 예제를 만들어 봅시다. 우선, 다음과 같이 폴더와 파일 구조를 생성합니다.
.
├── package.json
└── src
├── module-a
│ ├── index.js
│ └── package.json
└── module-b
├── index.js
└── package.json
module-a의 package.json은 다음과 같습니다.
// src/module-a/package.json
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js"
}
module-b의 package.json은 다음과 같습니다.
// src/module-b/package.json
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js"
}
그리고 module-b의 index.js는 다음과 같습니다.
// src/module-b/index.js
console.log('module-b');
module-a의 index.js는 다음과 같습니다.
// src/module-a/index.js
console.log('module-a');
require('module-b');
마지막으로 루트 폴더의 있는 package.json 파일을 다음과 같이 수정합니다.
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"]
}
}
Yarn의 Workspaces를 사용하기 위해 package.json의 workspaces에 packages가 설정된 것을 확인할 수 있습니다. Yarn의 Workspaces에 대한 자세한 내용은 다음 블로그 포스트를 참고하시기 바랍니다.
의존성 설치
이제 다음 명령어를 실행하여 의존성을 설치합니다.
yarn install
그럼 다음과 같이 node_modules에 module-a와 module-b의 Symlink가 생성된 것을 확인할 수 있습니다.
.
├── node_modules
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ └── package.json
└── yarn.lock
의존성 추가
이제 module-a에 의존성을 추가하여 의존성이 Hoisting되는지 확인해 봅시다. module-a의 package.json 파일인 src/module-a/package.json 파일을 열고 다음과 같이 수정합니다.
{
"name": "module-a",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^3"
}
}
그런 다음 다음 명령어를 실행하여 의존성을 설치합니다.
yarn install
그럼 다음과 같이 루트 폴더의 node_modules에 Hoisting된 lodash가 설치된 것을 확인할 수 있습니다.
.
├── node_modules
│ ├── lodash
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ └── module-b
└── yarn.lock
Hoisting된 의존성 사용
Hoisting된 의존성을 사용할 수 있는지 알아보기 위해 src/module-a/index.js 파일을 열고 다음과 같이 수정합니다.
const _ = require('lodash');
console.log('module-a');
require('module-b');
console.log(_.flatten([1, [2, 3, 4]]));
그리고 다음 명령어를 실행하여 module-a를 실행합니다.
node ./src/module-a
그럼 다음과 같이 lodash가 잘 불러와지는 것을 확인할 수 있습니다.
module-a
module-b
[ 1, 2, 3, 4 ]
이를 통해 Hoisting된 의존성을 사용할 수 있음을 확인할 수 있습니다.
의존성 버전 충돌
동일한 의존성의 다른 버전이 설치된 경우, 어떻게 동작하는지 확인해 봅시다. module-b의 package.json 파일인 src/module-b/package.json 파일을 열고 다음과 같이 수정합니다.
{
"name": "module-b",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^4"
}
}
그런 다음, 다음 명령어를 실행하여 의존성을 설치합니다.
yarn install
그럼 다음과 같이 버전이 다른 의존성은 모듈 폴더안에 node_modules에 설치되는 것을 확인할 수 있습니다.
.
├── node_modules
│ ├── lodash
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ ├── node_modules
│ │ └── lodash
│ └── package.json
└── yarn.lock
nohoist 옵션
Hoisting된 의존성을 사용하지 않고, 각 프로젝트의 node_modules에 의존성을 설치하고 싶은 경우, nohoist 옵션을 사용할 수 있습니다. 루트 폴더의 package.json 파일을 열고 다음과 같이 nohoist 옵션을 추가합니다.
{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["src/*"],
"nohoist": ["**/lodash"]
}
}
그리고 yarn.lock 파일을 삭제한 다음, 다음 명령어를 실행하여 의존성을 설치합니다.
# rm yarn.lock
yarn install
그럼 다음과 같이 lodash가 Hoisting되지 않고 각 프로젝트의 node_modules에 설치된 것을 확인할 수 있습니다.
.
├── node_modules
│ ├── module-a -> ../src/module-a
│ └── module-b -> ../src/module-b
├── package.json
├── src
│ ├── module-a
│ │ ├── index.js
│ │ ├── node_modules
│ │ │ └── lodash
│ │ └── package.json
│ └── module-b
│ ├── index.js
│ ├── node_modules
│ │ └── lodash
│ └── package.json
└── yarn.lock
완료
이것으로 Yarn의 Workspaces를 사용하여 모노레포를 구성하는 과정에서 각 프로젝트에 필요한 의존성을 추가할 때, 의존성이 추가되는 과정에서 발생하는 Hoisting에 대해서 알아보았습니다. 또한 의존성이 Hoisting되는 것을 방지하기 위한 nohoist 옵션에 대해서도 알아보았습니다.
여러분도 Yarn의 Workspaces를 사용하여 모노레포를 구성하는 과정에서 의존성의 Hoisting과 nohoist을 활용하여 효율적으로 의존성을 관리해 보시기 바랍니다.
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku가 개발한 앱을 한번 사용해보세요.Deku가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.














![[심통]현장에서 바로 써먹는 리액트 with 타입스크립트 : 리액트와 스토리북으로 배우는 컴포넌트 주도 개발, 심통](https://img1c.coupangcdn.com/image/affiliate/banner/7cba8cb0601eebaf88a17a0c3cf65a63@2x.jpg)