관리 메뉴

프론트엔드 정복하기

모노 레포 - yarn workspaces 본문

레파지토리 구성하기

모노 레포 - yarn workspaces

GROWNFRESH 2023. 9. 16. 17:29

레파지토리를 구성하는 다양한 방식이 있습니다. 그 중 대표적인 것이 1) 멀티 레포와 2) 모노 레포 입니다.

 

멀티 레포(Multi Repo)는 ...

▾ 간단하게 장/단점을 알려드리겠습니다. 이런 단점이 있기 때문에 모노레포라는 방식이 탄생하여 오늘 날 많이 사용되고 있습니다.

더보기

먼저 장점은 각 프로젝트가 다른 프로젝트에 의존성을 가지고 있지 않아 독립적으로 빠르게 개발이 가능하다는 것입니다. 그리고 해당 프로젝트에 필요한 도구로만 구성되어 있기 때문에 크기가 가벼워 프로젝트 관리가 수월한 편입니다.

하지만 관리할 프로젝트가 많아질수록 단점이 발생합니다. 프로젝트별로 사용하는 모듈과 버전 스택이 달라질 수 있고요. 오랫동안 건드리지 않은 프로젝트는 관리가 힘들어지고, 레거시 파악이 어려워집니다. 또한 새로운 레파지토리를 생성할 때마다 동일한 서비스 환경과 린트 환경을 반복적으로 셋팅해줘야 합니다.

 

 

이제 모노 레포에 대해 알아보도록 하겠습니다.

모노레포를 구성할 수 있는 패키지 도구들 중 yarn workspace 를 보겠습니다.

 

yarn workspace 필드를 이용해 모노레포를 구성할 수 있습니다. yarn link 기능을 선언적으로 사용해서 루트 node_modules 디렉토리에 workspace에 대한 심볼릭 링크가 생성됩니다. 이를 통해 하나의 저장소에 있는 여러 프로젝트가 서로 쉽게 상호 참조할 수 있습니다.

 

여기서 Symbolic Link 란 무엇일까요?

더보기

특정 파일이나 디렉토리에 대한 참조를 포함하는 특별한 파일이다. 윈도우에서 '바로가기'와 비슷하다고 보면 된다. 바로 가기 링크는 원본 파일 자체가 아니라, 실행파일과 연결된 링크인 것과 비슷한 개념이다.

 

예를 들어 packages 디렉토리 안에 client, server, common 3개의 워크스페이스를 추가하고 루트에서 yarn 명령어를 실행했다고 하자. 그럼 루트 경로 node_modules 디렉토리 내부에 workspace들에 대한 심볼릭 링크가 생성된다.

 

 

아래와 같이 라이브러리 형식의 프로젝트를 서비스용 프로젝트에 디펜던시로 선언하면 라이브러리 프로젝트를 심볼릭 링크로 참조해 디펜던시 모듈처럼 사용할 수 있게 된다. (목적성에 맞게 library, service 로 이름을 붙인 것 뿐이지, 명칭은 얼마든지 변경할 수 있다.)

 

빌드 또한 심볼릭 링크로 참조되는 라이브러리 형식의 프로젝트 먼저 빌드가 시작되고 이후 서비스 프로젝트가 빌드된다.

 

# libraries/qanda-design-system

{
  "name": "qanda-design-system",
  "version": "1.0.1",
  "dependencies": {
    ...
  }
}

# services/project-a

{
  "name": "projectA",
  "dependencies": {
    "qanda-design-system": "1.0.1"
  }
}

 

 

 

yarn  workspaces 에는  Dependency Module Hoisting 라는 개념이 있습니다.

 

 Dependency Module Hoisting 

최상단 루트에서 yarn install 을 이용해 모든 프로젝트의 패키지를 한꺼번에 설치할 수 있습니다. 이 때 각 프로젝트 별로 중복으로 의존성을 가지는 모듈이 존재할 수 있습니다. 해당 모듈은 호이스팅되어 최상단 node_modules 에 설치되고 각 하위 프로젝트에서 공유해 함께 사용하게 됩니다. 하위 프로젝트의 package.json 에 선언되었더라도, 실제로 설치하는 과정 중에는 중복 모듈이라고 판단되어 최상단 node_modules 에 한번만 설치되는 것이죠. 이에 따라 모듈 사이즈를 줄일 수 있는 이점이 있습니다.

 

하지만 아쉽게도 현재 yarn workspace에서 Hoisting을 이용한 디펜던시 관리는 아직 많이 불안정한 상태라고 합니다.

모노레포 내 각 프로젝트를 빌드하는 경우, 가끔 아래의 에러 메세지를 겪을 수 있는데요.

  • can’t find module “B@2.0” from project root “monorepo” (not able to follow symlink)
  • can’t find module “A@1.0” from “package-1” (unaware of the module tree above in “monorepo”)

루트 node_modules 로 호이스팅된 모듈들을 하위 프로젝트에서 모두 엑세스할 수 있는 것처럼 보였으나, 실제로 각 하위 프로젝트에서 빌드를 시도할 때 특정 모듈은 해당 로컬 node_modules만을 참조하려는 경우가 많습니다. 이때 root node_modules로 호이스팅된 모듈은 로컬 node_modules에는 실제로 존재하지 않기 때문에 Not Fount 에러가 발생하게 됩니다.

 

yarn 공식문서에서도 모든 타사 라이브러리들이 아직 monorepo 환경에 최적화가 되어 있는 상태가 아닌 만큼, 이러한 문제는 쉽게 해결될 수 없다고 설명하고 있습니다. 따라서 yarn 에서는 별도로 호이스팅되지 않도록 noHoist 옵션을 제공하고 있습니다.

 

"workspaces": {
  "nohoist": ["react-native", "react-native/**"]
}

 

 

** npm 버전 3에서도 호이스팅 기법을 사용하고 있다.

** 호이스팅의 문제점: 유령 의존성 발생

호이스팅으로 좌측 트리를 우측 트리처럼 바꾸게 될텐데, 그럼 package-1에서 원래 직접 의존하고 있지 않았던 B 1.0 을 require() 할 수 있게 된다. 이를 유령 의존성이라고 말한다. 이는 다른 의존성을 package.json 에서 제거했을 때 소리없이 같이 사라지기도 한다. 의존성 관리 시스템을 혼란스럽게 만든다.

 

 

 

참고 사이트