Lerna와 Yarn Workspaces를 Serverless와 함께 사용하기
Lerna와 Yarn Workspaces를 Serverless와 함께 사용하기
Serverless 프로젝트 구성 챕터에서 표준 모노레포 설정을 다뤘습니다. 여기에는 서비스 간 코드를 공유하는 방법과 상호 의존적인 서비스가 있는 Serverless 앱을 배포하는 방법이 포함되었습니다.
이 설정은 잘 작동하지만, 팀과 프로젝트가 성장함에 따라 새로운 문제가 발생합니다. 여러 서비스에서 공통으로 사용되는 코드 라이브러리가 있습니다. 이 라이브러리를 업데이트하면 모든 서비스가 다시 배포됩니다. 서비스가 팀 내 다른 사람이나 다른 팀에서 관리되는 경우, 공통 코드를 변경할 때마다 팀의 다른 사람들이 자신의 서비스를 테스트하거나 업데이트해야 하는 문제가 생깁니다.
이런 경우 공통 코드 라이브러리를 패키지로 관리하는 것이 합리적입니다. 이렇게 하면 서비스가 동일한 패키지의 다른 버전을 사용할 수 있습니다. 팀은 패키지의 새 버전을 자신에게 가장 적합한 시점에 업데이트할 수 있습니다. 공통 코드의 작은 변경으로 인해 해당 코드에 의존하는 모든 서비스가 깨지는 상황을 피할 수 있습니다.
그러나 동일한 리포지토리에서 이러한 패키지를 관리하는 것은 매우 어려울 수 있습니다. 이 문제를 해결하기 위해 다음을 사용할 것입니다:
-
이 도구는 모든 개별
node_modules/
를 루트 레벨로 끌어올려 리포지토리를 최적화합니다. 따라서 단일yarn install
명령으로 모든 서비스와 패키지의 NPM 모듈을 설치할 수 있습니다. -
이 도구는 패키지를 관리하고, 배포하며, 패키지 간의 의존성을 추적하는 데 도움을 줍니다.
Lerna와 Yarn Workspaces를 함께 사용하면 Serverless 프로젝트가 성장함에 따라 확장할 수 있는 모노레포 설정을 만들 수 있습니다.
스타터 템플릿
여러분이 쉽게 시작할 수 있도록 두 가지 스타터 프로젝트를 준비했습니다.
이 스타터 템플릿은 다음과 같은 특징을 가지고 있습니다.
- 대규모 프로젝트에 적합하도록 설계됨
- 내부 의존성을 패키지로 관리
- Lerna를 사용하여 업데이트된 서비스를 자동으로 파악
- 의존성을 프라이빗 npm 패키지로 배포 가능
- Yarn Workspaces를 사용하여 패키지를 루트
node_modules/
디렉토리로 호이스팅
이 설정을 통해 쉽게 시작할 수 있습니다. 하지만 Lerna나 Yarn Workspaces에 익숙하지 않다면, 해당 문서를 꼭 확인해 보세요.
이제 두 스타터를 살펴보겠습니다.
Serverless Framework 스타터
레포지토리는 여기에서 확인할 수 있습니다 — github.com/AnomalyInnovations/serverless-lerna-yarn-starter
설치
새로운 Serverless 프로젝트를 생성하려면
$ git clone https://github.com/AnomalyInnovations/serverless-lerna-yarn-starter my-project
새로운 디렉토리로 이동
$ cd my-project
전체 프로젝트의 NPM 패키지 설치
$ yarn
동작 방식
디렉터리 구조는 대략 다음과 같습니다:
package.json
/libs
/packages
/sample-package
index.js
package.json
/services
/service1
handler.js
package.json
serverless.yml
/service2
handler.js
package.json
serverless.yml
이 저장소는 세 개의 디렉터리로 나뉘며, 각각 다른 목적을 가지고 있습니다:
-
packages
서비스에서 사용되는 내부 패키지입니다. 각 패키지는
package.json
을 포함하며, 선택적으로 NPM에 배포할 수 있습니다. 패키지에 변경이 발생하면 해당 패키지에 의존하는 서비스만 배포해야 합니다. -
services
배포되는 Serverless Framework 서비스입니다.
package.json
과serverless.yml
파일을 포함합니다. 두 개의 샘플 서비스가 있습니다.service1
:sample-package
에 의존합니다. 즉, 이 패키지가 변경되면service1
을 배포해야 합니다.service2
: 내부 패키지에 의존하지 않습니다.
배포에 대한 자세한 내용은 아래에서 설명합니다.
-
libs
패키지로 유지하기 어려운 공통 코드를 포함합니다.
package.json
파일이 없습니다. 여기에 변경이 발생하면 모든 서비스를 다시 배포해야 합니다.
packages/
와 services/
디렉터리는 Yarn Workspaces로 구성됩니다.
서비스
Serverless Framework 서비스는 각각 독립적으로 관리됩니다. 각 서비스는 Serverless Node.js Starter를 기반으로 합니다. serverless-bundle 플러그인(Webpack 기반)을 사용하여 최적화된 Lambda 패키지를 생성합니다.
이 방법은 Lambda 패키지를 작게 유지하는 데 좋습니다. 또한 Yarn이 모든 NPM 패키지를 프로젝트 루트로 호이스팅할 수 있도록 보장합니다. Webpack 없이는 Serverless Framework가 서비스의 의존성을 제대로 패키징하지 못하기 때문에 호이스팅을 비활성화해야 합니다.
서비스 내부에 NPM 패키지를 설치합니다.
$ yarn add some-npm-package
로컬에서 함수를 실행합니다.
$ serverless invoke local -f get
서비스에서 테스트를 실행합니다.
$ yarn test
서비스를 배포합니다.
$ serverless deploy
단일 함수를 배포합니다.
$ serverless deploy function -f get
새로운 서비스를 추가합니다.
$ cd services/
$ serverless install --url https://github.com/AnomalyInnovations/serverless-nodejs-starter --name new-service
$ cd new-service
$ yarn
패키지
각 패키지는 자체 package.json
을 가지고 있기 때문에, 다른 NPM 패키지와 동일한 방식으로 관리할 수 있습니다.
새로운 패키지를 추가하려면 다음과 같이 합니다.
$ mkdir packages/new-package
$ yarn init
패키지는 선택적으로 NPM에 배포할 수도 있습니다.
라이브러리
여러분의 리포지토리에서 패키지로 유지되지 않는 공통 코드를 추가해야 한다면, libs/
디렉토리에 추가하세요. 이 디렉토리는 package.json
을 포함하지 않습니다. 즉, NPM 패키지를 루트에 종속성으로 설치해야 합니다.
루트에 NPM 패키지를 설치하려면 다음 명령어를 사용하세요.
$ yarn add -W some-npm-package
배포
업데이트된 서비스만 배포되도록 보장하고자 합니다. 이는 다음과 같은 변경 사항이 있을 때 적용됩니다:
-
서비스
변경된 서비스만 배포되어야 합니다. 예를 들어,
service1
의 코드를 변경했다면,service2
는 배포되지 않아야 합니다. -
패키지
패키지가 변경되면, 해당 패키지에 의존하는 서비스만 배포되어야 합니다. 예를 들어,
sample-package
가 변경되면,service1
이 배포되어야 합니다. -
라이브러리
라이브러리 중 하나라도 변경되면, 모든 서비스가 배포됩니다.
배포 알고리즘
위 내용을 구현하려면 CI에서 다음 알고리즘을 사용하세요:
lerna ls --since ${prevCommitSHA} -all
을 실행하여 마지막 성공적인 배포 이후 변경된 모든 패키지를 나열합니다. 이 목록에 서비스 중 하나가 포함되어 있다면 해당 서비스를 배포합니다.git diff --name-only ${prevCommitSHA} ${currentCommitSHA}
를 실행하여 업데이트된 모든 파일 목록을 가져옵니다. 이 파일들이 Lerna 패키지(lerna ls -all
)에 속하지 않는다면 모든 서비스를 배포합니다.- 그렇지 않으면 배포를 건너뜁니다.
Seed를 통한 배포
Seed는 Lerna와 Yarn Workspaces를 사용하는 SST 모노레포 프로젝트를 기본적으로 지원합니다.
이 스타터들은 여러분의 다음 모노레포 서버리스 프로젝트를 구축하기 위한 훌륭한 템플릿을 제공할 것입니다. 한번 시도해 보고 의견을 알려주세요!
SST 스타터
레포지토리는 여기에서 확인할 수 있습니다 — github.com/sst/lerna-yarn-starter
Installation_XQYWVibyGcdUuDsfMXmozh
먼저 이 저장소를 클론합니다.
$ git clone https://github.com/sst/lerna-yarn-starter my-project
새로운 디렉토리로 이동합니다.
$ cd my-project
전체 프로젝트의 npm 패키지를 설치합니다.
$ yarn
작동 방식_9hKEFoZkNVWCzL7PYBxF4N
디렉터리 구조는 대략 다음과 같습니다:
package.json
/lib
/frontend
package.json
/src
/services
/service1
handler.js
package.json
/service2
handler.js
package.json
/packages
/sample-package
index.js
package.json
/util
이 저장소는 몇 가지 부분으로 나뉩니다. 각각의 목적은 다음과 같습니다:
-
stacks/
여기에는 서버리스 앱의 인프라를 정의하는 CDK 코드가 있습니다.
-
src/
여기에는 Lambda 함수의 코드가 있습니다. 이 코드는 서비스별로 더 세분화되어 있으며, 각 서비스는 Lambda 함수의 모음입니다.
-
src/services/
이 디렉터리에는 Lambda 함수로 배포되는 서비스들이 있습니다. 각 서비스는
package.json
과 진입점을 가지고 있습니다. 두 개의 샘플 서비스가 있습니다.service1
:sample-package
에 의존합니다.service2
: 내부 패키지에 의존하지 않습니다.
-
src/packages/
이 디렉터리에는 서비스에서 사용되는 내부 패키지들이 있습니다. 각 패키지는
package.json
을 포함하며, npm에 선택적으로 게시할 수 있습니다. -
src/util/
패키지로 유지하고 싶지 않은 공통 코드가 있습니다.
package.json
이 없습니다. -
frontend/
서버리스 앱의 일부인 샘플 프론트엔드 React 앱입니다.
src/packages/
, src/services/
, 그리고 frontend/
디렉터리는 Yarn Workspaces입니다.
Services_n9rgg88gEr8rVnqkPsCZkD
각 서비스는 비슷한 목적을 가진 Lambda 함수들의 모음입니다. 이들은 독립적으로 관리되도록 설계되었습니다. 각 서비스는 자체 package.json
을 가지며, 의존성 버전은 다른 서비스와 분리되어 유지됩니다. SST는 내부적으로 esbuild를 사용하여 각 Lambda 함수를 최적화된 방식으로 패키징합니다.
이 방식은 Lambda 패키지 크기를 작게 유지하는 데 유용합니다. 또한 Yarn Workspaces는 모든 npm 패키지를 프로젝트 루트로 끌어올려 관리합니다.
Packages_jYkTN6qLTBBjRfMV2QMjGd
각 패키지는 자체 package.json
파일을 가지고 있기 때문에, 다른 npm 패키지와 동일하게 관리할 수 있습니다.
새로운 패키지를 추가하려면:
$ mkdir src/packages/new-package
$ yarn init
패키지는 npm에 게시할 수도 있습니다.
패키지를 사용하려면:
$ yarn add new-package@1.0.0
패키지를 추가할 때는 해당 패키지의 package.json
에 선언된 버전 번호를 명시해야 합니다. 그렇지 않으면 Yarn이 레지스트리에서 의존성을 찾으려고 시도합니다.
Util
리포지토리에서 패키지로 유지하지 않을 공통 코드를 추가해야 한다면 util 디렉터리에 넣으세요. 이 디렉터리에는 package.json
이 없습니다. 따라서 npm 패키지를 설치하려면 루트에서 의존성으로 설치해야 합니다.
루트에서 npm 패키지를 설치하려면:
$ yarn add -W some-npm-package
모든 공통 코드를 util에 추가하는 것은 편리하지만 단점도 있습니다. 팀이 util을 업데이트하면, 이를 의존하는 모든 서비스는 배포 전에 이 변경 사항을 테스트해야 합니다. 반면에 패키지는 특정 버전으로 고정할 수 있고, 팀이 선택한 시점에 업그레이드할 수 있습니다.
Deployment_6QrdDjqm6HXAXc4mxt3kcz
SST는 모든 의존성을 내부적으로 처리하고 모든 서비스(그리고 프론트엔드)를 순서대로 배포합니다.
For help and discussion
Comments on this chapter