Vercel로 Nx 모노레포 배포하기

Team return·2024년 4월 4일
9

monorepo-with-nx

목록 보기
2/2
post-thumbnail

안녕하세요 JOBIS에서 프론트엔드 개발하고 있는 정지관입니다.
저번 글에서는 저희 팀이 왜 모노레포와 Nx를 도입하였는지에 대하여 알아보았습니다.
이번 시간에는 어떻게 모노레포를 적용하고 배포까지 하였는지에 대하여 소개해드리겠습니다.

구축하기

그럼 본격적으로 JOBIS에서 어떻게 Nx를 사용하였고 모노레포를 구축하였는지 알아봅시다.

세팅

일단 nx의 가이드 문서를 따라 다음 명령어를 입력해 nx 프로젝트를 생성 및 설치 해줍니다
https://nx.dev/getting-started/installation

npx nx@latest init

프로젝트가 생성된 뒤 package.json에서 스크립트와 workspace 구조 설정을 해주었습니다.

{
    "name": "jobis",
    "scripts": {
        "build": "nx build",
    	"build:many": "nx run-many -t build",
        "build:affected": "npx nx affected -t build",
        "graph": "nx run-many --target=build --graph",
        ...
    },
    "workspaces": [
        "apps/*",
        "packages/*"
    ],
    ...
}

스크립트

  • build 명령어는 최상단에서 특정 프로젝트를 빌드할 수 있습니다.
    nx build <패키지명>
  • build:many 명령어는 프로젝트 전체의 빌드 명령어를 수행하는 명령어입니다
  • build:affected는 저번 글에서 말했던 nx의 affected 기능을 이용하여 변경사항과 관련된 부분만 빌드를 진행하는 명령어입니다.
  • graph: nx의 기능인 프로젝트 및 패키지 의존성 관계를 시각화 하는 그래프를 생성합니다.

workspaces

  • apps: jobis의 여러 웹 서비스들을 포함하는 workspace 입니다.
  • packages: jobis 개발에 필요한 공통된 모듈을 포함하는 workspace입니다.

구조

디렉토리의 구조는 다음과 같이 가져 갔습니다.

// 디렉토리 구조
┗━ apps
┃ ┗━ @jobis/company
┃ ┗━ @jobis/storybook
┃ ┗━ @jobis/policy
┗━ packages
┃ ┗━ @jobis/design-token
┃ ┗━ @jobis/esbuild-config
┃ ┗━ @jobis/icons
┃ ┗━ @jobis/ui
┗━ package.json

packages
설정이 끝난 뒤 jobis에서 필요한 공통 모듈을 생성해보았습니다.

추가할 모듈은 다음과 같습니다.

  • @jobis/esbuild-config: 모듈을 생성할 때마다 번거롭게 번들러 설정하지 않도록 재사용할 수 있는 esbuild-config입니다.
  • @jobis/design-token: jobis에 사용되는 color와 font를 관리하는 모듈입니다.
  • @jobis/icons: jobis에 사용되는 icon들을 관리합니다.
  • @jobis/ui: jobis에 사용되는 UI 컴포넌트를 재사용할 수 있도록 제작한 패키지입니다.

apps
모듈을 분리한 뒤 서비스를 마이그레이션 하였습니다.
생성한 apps 디렉토리에 프로젝트들을 추가해줍니다.

┗━ apps
┃ ┗━ company
┃	┗━ package.json
┃ ┗━ storybook
┃	┗━ package.json
┃ ┗━ policy
┃	┗━ package.json
┗━ package.json

추가한 프로젝트 목록

  • @jobis/company: 학교로 채용 의뢰를 할 수 있는 기업용 웹입니다.
  • @jobis/policy: 앱에서 개인정보 처리 방침 조회를 위한 웹뷰입니다.
  • @jobis/storybook: storybook을 이용하여 UI 컴포넌트와 아이콘을 시각적으로 정리하고 문서화하는 웹입니다.

이렇게 옮긴 company와 storybook 프로젝트에서는 위에서 만든 UI컴포넌트와 icon 패키지를 필요로 합니다. 이러한 경우 패키지 매니저를 이용해 해당 프로젝트에서 add 명령어를 통해 내부의 패키지를 추가할 수 있습니다.

// 해당 패키지 경로에 있는 경우
yarn add <패키지명>
// 루트 경로에 있는 경우
yarn workspace <패키지를 추가할 패키지명> add <패키지명>

설치가 되면 다음과 같은 형태를 지니게 되고 의존 관계를 지니게 됩니다.

// ex)
// company/package.json
{
  "name": "@jobis/company",
  "dependencies": {
    "@jobis/design-token": "workspace:^",
  }
}

의존성 그래프


위 과정들을 통해 구축된 현재 저희 레포지토리의 의존성 구조는 위 사진과 같습니다.

배포

이제 구축된 모노레포에 있는 각각의 서비스들을 배포해야합니다.

Cloudflare pages에서 vercel로

기존에 저희가 멀티레포 구조를 다룰 때에는 cloudflare pages를 이용하여 각각의 레포지토리를 배포하였습니다.
그래서 기존 방식 그대로 cloudflare pages를 이용하여 배포를 하려고 하였지만 한가지 문제점이 존재하였습니다.

위 내용 간단히 해석하자면 cloudflare pages에서는 하나의 레포에는 하나의 프로젝트만 배포할 수 있고 만약 여러 프로젝트를 배포하고 싶은 경우 멀티레포 방식을 사용하거나 수동으로 배포를 하라는 것이었습니다.
저희 팀은 일일히 수동으로 배포하는 방식은 배포가 꼬일 수 있고 매번 번거로운 방식이라고 생각하였습니다.
또 cloudflare pages에서 next13은 정적인 웹만 배포할 수 있었기에 동적인 부분을 필요로하는 저희 서비스와 적합하지 않은 부분이 존재하였습니다.

따라서 저희는 다른 플랫폼을 모색하였고 그렇게 하나의 레포에서 여러 개의 프로젝트를 배포할 수 있으며 nextjs 동적 배포를 모두 다 만족할 수 있는 vercel을 선택하게 되었습니다.

vercel로 배포하기


vercel 계정을 생성한 뒤 github 연결을 통해 새 프로젝트를 생성해주었습니다.


연결을 하고 나면 프로젝트 이름과 배포할 프로젝트의 프레임워크를 선택해줍니다.

다음은 빌드 명령어와 환경 변수 설정을 해주면 됩니다. 예시로 apps 디렉토리에 있는 company를 배포해보겠습니다.

다음과 같이 프로젝트 빌드 명령어와 output 디렉토리, 설치 명령어를 입력해주고 배포해주었습니다.

yarn version

위와 같은 명령어를 입력하였지만 바로 배포가 되지는 않았습니다. 저희 팀은 첫 배포에 yarn version으로 인하여 한동안 배포를 하는데에 어려움을 겪고 있었습니다.

배포를 할 때마다 다음과 같은 오류가 계속해서 떴는데요.

이 오류를 고치기 위해 3일동안 삽질만 하고 있던 저는 한가지 이상한 점을 찾아낼 수 있었습니다.

바로 제 로컬에서 yarn 버전에 대한 문제였는데요. 해당 프로젝트에서는 yarnPnP를 사용하고 있고 버전은 4.1.0입니다. 그러나 제 로컬에서의 yarn 버전을 확인해보니 4.0.2라는 것을 확인해볼 수 있었습니다. 그래서 .yarn폴더에 들어가 있던 파일들이 배포할 때 사용되는 환경과 다르게 설치되었기 때문에 계속해서 오류가 발생하는 것이었습니다.

하지만 yarn set version 4.1.0 명령어를 통해서 버전을 바꿨음에도 yarn 버전은 바뀌지 않았습니다. 그러다 제 pc에 있는 상위 폴더에서 yarn이 4.0.2 버전의 형태로 존재하는 것을 확인하였고 모든 yarn 관련 파일을 삭제해준 뒤 재설치를 하니 .yarn폴더 안의 파일들이 업데이트 되는 것을 볼 수 있었습니다.


이렇게 업데이트된 파일을 다시 커밋해주니 배포가 성공되는 것을 확인할 수 있었습니다.

배포 시간 50% 단축

vercel의 remote caching 기능을 이용하면 배포 시간을 단축할 수 있습니다.


기존의 배포에 소요되는 시간입니다. 커밋을 할 때마다 새로 빌드를 하기 때문에 변경사항이 많이 없는 경우에도 똑같이 배포시간이 걸리는 것을 확인할 수 있습니다.

하지만 remote caching 기능을 사용하면 원격으로 캐싱을 해주어 다음 동작에서 반복되는 부분의 동작을 수행하지 않고 배포할 수 있습니다.

적용하기

yarn add -D @vercel/remote-nx

위 명령어를 통해 @vercel/remote-nx를 설치해줍니다.

{
  "tasksRunnerOptions": {
    "default": {
      "runner": "@vercel/remote-nx",
      "options": {
        "cacheableOperations": ["build", "test", "lint", "e2e"],
        "token": "<token>",
        "teamId": "<teamId>"
      }
    }
  }
}

설치가 완료되었다면 nx.json에서 runner를 @vercel/remote-nx로 변경해주고 캐싱을 적용할 명령어를 cacheableOperations에 추가해줍니다. token과 teamId에는 vercel에서 제공하는 access token과 teamId를 넣어주면 적용이 완료됩니다.


위 과정을 통해 적용되었을 때 배포시간입니다. 캐시 히트를 통해 배포 시간이 이전보다 단축되는 것을 확인할 수 있습니다.

모노레포 도입으로 개선된 점

  1. 코드리뷰 활성화
    • 각 프로젝트의 작업 사항을 하나의 레포지토리에서 한눈에 확인할 수 있게되면서 다른 프로젝트의 코드를 들여다보며 코드 리뷰에 적극 참여할 수 있는 계기가 만들어졌습니다.
  2. 팀 컨벤션 통일
    • 레포지토리 별로 달랐던 eslint와 prettier의 설정들이 통합되면서 동일한 컨벤션을 가져가면서 새로운 프로젝트를 생성할 때마다 린트 환경설정 등과 같은 리소스 낭비를 줄일 수 있었습니다.
  3. 최신화 상태 유지
    • 기존에 웹뷰 서비스의 경우 첫 배포 이후 수정사항이 거의 없어 프로젝트 관리가 거의 관리되지 않고 있었습니다. 하지만 모노레포를 통해 모든 프로젝트가 하나의 레포지토리에 관리되면서 팀원 모두가 모든 프로젝트에 관심을 가지고 최신화 상태를 보존할 수 있게 되었습니다.

마무리

모노레포를 적용하면서 팀의 생산성이나 유지보수적인 측면에서 개선해 나갈 수 있다는 부분이 인상적이었습니다.
nx와 vercel에서 지원하는 기능들이 정말 많았고 아직 미흡하게 적용해본 부분도 있고 연구해볼 내용들이 많은 것 같습니다. 앞으로도 계속 공부하고 적용하면서 다음에는 더 개선된 모습의 글로 찾아뵙겠습니다. 감사합니다.

참고자료

profile
Team return 기술 블로그입니다.

4개의 댓글

comment-user-thumbnail
2024년 4월 8일

좋네요

1개의 답글
comment-user-thumbnail
2024년 4월 16일

저희 회사에서도 nx를 사용하고 있는데, 이렇게 자세히 알게된건 처음이네요!
좋은 포스팅 감사합니다 :)

1개의 답글