서비스 개발에서의 yarn workspace

yarn workspace를 왜 사용해야 하는지를 정리해 봅니다.

2021-02-21

monorepo와 그 필요성

monorepo를 처음 접하고 듣게 된 계기는 babel과 관련한 npm 패키지들의 버전관리에 관한 글을 읽었을 때 입니다.

babel은 상당히 큰 프로젝트이고 여러 플러그인을 조합하여 사용하도록 설계하였습니다.

monorepo 방식 이전에는 npm 패키지 하나당 하나의 git 레포지터리를 사용했습니다. 이 경우 babel과 같이 여러 npm 패키지의 의존관계를 추적하고 버전을 업데이트 하는 경우 골치 아픈 상황이 발생합니다. 예를들어 @babel/core의 플러그인 인터페이스 변경에 따라 버전을 @7.x -> @8.x로 변경하고자 한다면, 의존하고 있는 여러 플러그인 또한 신규 인터페이스에 맞추어 개발 후 @8.x와 유사하게 버전을 업데이트 해 주어야 합니다.

lerna는 이러한 필요에 의해 만들어 졌습니다. 이후 대규모 오픈소스 프로젝트는 lerna를 사용하여 하나의 git repository에 관리하는 monorepo 기밥을 많이 도입하였습니다.

yarn workspace와 그 필요성, 그리고 oao, depcheck

yarn workspace는 monorepo를 별도의 패키지 설치 없이 지원하기 위한 기능 입니다.

다만 lerna 처럼 여러 패키지를 npm에 publish 한다거나 의존성을 파악하여 명령을 실행해 준다거나 하는 기능은 없습니다.

yarn workspace는 하나의 git 레포지터리에서 여러 npm 패키지 사이의 상호 참조가 가능하도록 합니다. 또한 각 npm package.json에 명시한 의존 패키지 설치를 합니다.

따라서 외부 패키지 배포(npm publush)가 필요 없는 서비스나 애플리케이션을 개발할 때 활용할 수 있습니다.

yarn workspace로 프로젝트를 관리하다 보면, 시간이 지날 수록 프로젝트의 의존 패키지가 늘어남니다. 그러다 보면, 각 의존 패키지의 버전이 서로 다른 경우가 종종 발생합니다.

예를 들어, @my-workspace/package-a@my-workspace/package-b에서 graphql@10.0.0을 사용하고 있다가. @my-workspace/package-a의 기능 개선을 위해 graphql@11.0.0으로 버전을 업데이트 합니다. 이 경우 yarn workspace는 두 개의 버전을 모두 설치 합니다. 동작에는 이상이 없지만, 이렇게 버전관리를 신경쓰지 않게 되면 yarn install하는 비용이 점점 커지게 됩니다. 따라서 특정 패키지들이 동일한 패키지에 의존하는 경우, 버전 관리에 신경을 써 주어야 합니다.

oao는 yarn workflow 환경에서 의존 패키지를 업데이트 하는 수고를 덜어 줍니다.

마찬가지로 yarn install 비용을 조금이라도 줄이려면 주기적으로 사용하지 않는 패키지들은 의존관계를 삭제해 주어야 합니다. 이 경우 depcheck를 사용할 수 있습니다.

yarn workspace와 submmodule

하나의 git 레포지터리에서 서비스를 개발하는 것에 비해 장점은 무었일까요?

저는 대략 다음과 같이 정리하고 싶습니다.

좀 깔끔한 관리가 되고 향후 요구사항 변화에 유연해 진다.

브라우저와 node.js라는 두 가지 런타임에 모두 수행가능한 만큼 적절히 코드를 관리해 주어야 합니다.

  • 브라우저와 node.js에 모두 동작하는 라이브러리, 편의상 lib-universal이라 합니다.
  • 브라우저에서만 동작하는 라이브러리, 편의상 lib-browser라 합니다.
  • node.js에서만 동작하는 라이브러리, 편의상 lib-node라 합니다.

이렇게 하지 않을 경우, 하나의 프로젝트에 서버에서 필요한 의존성과 client에서 필요한 의존성이 뒤엉키게 됩니다. 또한 어떤 라이브러리와 모듈이 어느 코드에서 import 할수 있는지 파악하기 힘들어 집니다. 때로는 lib-universallib-browser 코드가 뒤엉켜 대규모 리펙토링이 필요해 지기도 합니다.

또 한가지 monorepo 기반으로 패키지를 나누어야 하는 중요한 유즈 케이스는 공통 컴포넌트 입니다.

서비스를 개발하면 애플리케이션이 하나 이상이 될 수 있습니다. 다른 애플리케이션이라도 동일한 look & feel과 UX를 가져가야 합니다. 따라서 공통 컴포넌트를 별도의 패키지로 두고 애플리케이션을 각각 다른 패키지로 만들어 관리해야 합니다.

이렇게 관리할 경우 배포할 때 문제가 발생합니다.

애플리케이션 A, B를 배포하고 단 뒤, 애플리케이션 A만 일단 급하게 컴포넌트 수정과 함께 배포해야 하는 경우가 있습니다. 동시에 애플리케이션 B에도 컴포넌트 수정과 함께 배포를 해야 하는 경우 문제가 생깁니다.

물론 언젠가는 양쪽의 컴포넌트 수정을 통합해야 하지만 급박하거나 우선순위에 따라서 여러 시나리오를 대응할 운영의 유연성이 필요합니다.

이 경우 component 패키지를 별도의 git 레포지터리로 개발하고 이를 git submodule로 각 서비스 레포지터리에 설정합니다. 또한 git submodule 적용에 따라 브렌치 관리 전략과 코드 리뷰 전략을 잘 수립해야 합니다.

Loading script...