# yarn 에서 pnpm 으로, 그리고 CodeBuild 캐시

## 개요

현재 회사에서는 Next.js 프로젝트에 CodePipeline, CodeBuild 를 활용한 CI/CD 가 구축되어 있다.

CodeBuild 와 yarn classic 을 활용해 배포를 진행하게 되면 빌드 + 배포 시간이 평균 9분이 넘어간다.  
예전에 Vercel 에서 빌드 + 배포를 진행했을 때 3분대가 나왔던 거랑 비교하면 9분이라는 굉장히 큰 시간을 빌드 + 배포에 쓰고 있다.

이를 줄여보고자 CodeBuild 의 S3 캐시와 yarn classic 을 pnpm 로 마이그레이션 하는 등 시도를 해봤다.

결론부터 말하자면 pnpm + S3 캐시를 도입 후 빌드+배포 시간이 평균 554초에서 평균 392초대로 25%가 감소했다.

## yarn 에서 pnpm 으로

1. pnpm 설치
    
    우선 pnpm 을 설치해야 한다. [공식문서](https://pnpm.io/installation)에 여러 방법이 설명되어있지만 이 글에서는 corepack 을 활용한다.
    
    corepack 을 사용하기 위해서는 node 16.13 버전 이상이 필요하다.
    
    ```bash
    $ corepack enable
    $ pnpm -v
    ```
    
2. node\_modules 삭제
    
    `npkill` 을 활용하여 프로젝트의 모든 node\_modules 를 삭제한다.
    
    ```bash
    $ npx npkill
    ```
    
3. pnpm import
    
    `pnpm import` 를 활용해 기존 lockfile 로 부터 `pnpm-lock.yaml` 을 생성한다.
    
    ```bash
    $ pnpm import
    ```
    
    `pnpm import` 후 기존 lockfile
    
    만약 모노레포를 사용중이라면 `pnpm-workspace.yaml` 을 미리 생성해야 한다.
    
    ```yaml
    # pnpm-workspace.yaml
    packages:
      # all packages in direct subdirs of packages/
      - 'apps/*'
      # all packages in subdirs of components/
      - 'components/**'
      # exclude packages that are inside test directories
      - '!**/test/**'
    ```
    
    참고: [https://pnpm.io/cli/import](https://pnpm.io/cli/import)
    
4. 의존성 설치
    
    ```bash
    $ pnpm install
    ```
    
    추가로 `package.json` 에 `packageManager` 필드를 추가 하면 다른 환경에서 같은 pnpm 버전을 사용할 수 있다.
    
    ```json
    // package.json
    {
      "packageManager": "pnpm@<version>"
    }
    ```
    
    자세한 내용은 [node 공식문서](https://nodejs.org/api/packages.html#packagemanager)에서 확인 할 수 있다.
    

이를 `buildspec.yaml` 에 적용하면 아래와 같은 코드가 된다.

```yaml
# buildspec.yaml
version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 18
    commands:
      # pnpm 사용
      - corepack enable
      - pnpm -v
      - pnpm install --frozen-lockfile
  build:
    commands:
      # 혹은 모노레포를 사용중이라면 `pnpm -F <package-name> run build`
      - pnpm run build
```

## CodeBuild 에 S3 캐시 적용하기

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1699173234988/9c88b137-e03c-4d65-99e3-b1a3d22ba717.png align="center")

우선 S3 캐시를 적용할 CodeBuild 프로젝트 상세 페이지에 접속한다.  
상세 콘솔에서 `Edit > Artifacts` 메뉴에 들어간다.

하단의 `Additional configuration` 을 확장해 보면 `Cache Type` 이 `No Cache` 로 설정되어 있는 걸 확인 할 수 있다.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1699173230343/12ddc45e-169b-413b-a676-fa9e19f29f7a.png align="center")

위 이미지와 같이 `Cache Type` 을 `Amazon S3` 로 변경한 뒤 `Cache bucket` 과 `Cache path prefix` 를 설정한다.

## CodeBuild, pnpm 캐시 트러블 슈팅

트러블 슈팅으로 고생했는데, cache paths 에 `node_modules` 를 입력하면 S3 캐시 업로드가 진행이 되지 않는 이슈가 있었다. 이는 pnpm 이 기본적으로 의존성을 심볼릭 링크 방식을 사용함에 있어서 생기는 이슈였고, S3 는 심볼릭 링크 업로드를 지원하지 않는다.

해결 방법은 [pnpm 의 공식 문서](https://pnpm.io/continuous-integration)를 보다 찾게 되었다.

공통으로 `pnpm config set store-dir <path>` 부분이 자주 보이는데, 이를 `buildspec.yaml` 에 적용해 보니 S3 캐시 업로드가 정상적으로 진행이 되었다.

pnpm store 와 캐시를 추가하면 아래와 같은 코드가 된다.

```yaml
# buildspec.yaml
version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 18
    commands:
      - corepack enable
      - pnpm -v
      # pnpm store 위치 설정
      - pnpm config set store-dir .pnpm-store
      - pnpm install --frozen-lockfile
  build:
    commands:
      - pnpm run build

# Cache 추가
cache:
  paths:
    - '.pnpm-store/**/*'
    - '.next/cache/**/*'
```

## 결론

| \- | 평균 초 | 빌드 + 배포 횟수 |
| --- | --- | --- |
| yarn + No Cache | 554 | 57 |
| yarn + No Cache (최근 10회) | 490 | 10 |
| pnpm + No Cache | 428 | 3 |
| pnpm + Cache(pnpm) | 408 | 3 |
| pnpm + Cache(pnpm, Next.js) | 392 | 2 |

아직 테스트 수가 적어서 더 지켜봐야 정확한 판단 가능하겠지만 당장 결과를 본다면,  
yarn → pnpm 으로 변경한 것만으로 Install 시간이 전체 평균 25%(162초), 최근 평균 12%(98초) 정도 감소했고, S3 캐시를 도입했을 때 평균 9%(30초) 정도 줄어들었다.

충분히 pnpm + S3 캐시를 도입할 가치는 있어 보인다.
