AWS Amplify Next.js 모노레포 빌드 최적화: 40% 시간 단축 (8분 → 5분)
TL;DR
AWS Amplify에서 Next.js 모노레포 빌드 시간이 평균 8분 이상 소요되고 있었습니다.
빌드 로그 분석 결과 주요 병목은 다음 세 가지였습니다.
- Submodule 인증 과정
- 전체 의존성 설치
- node_modules 캐시
이를 개선해 빌드 시간을 8분 → 5분 초반(약 40% 이상)까지 단축했습니다.
문제 상황
회사 프론트엔드 코드는 모노레포 환경으로 관리되고 있습니다.
서비스는 다음과 같은 구조입니다.
- Next.js 기반 서비스
- Vite 기반 서비스
AWS Amplify 배포 환경에서 빌드 시간이 예상보다 길어지는 문제가 있었습니다.
| 서비스 | 평균 빌드 시간 |
|---|---|
| Next.js | 약 8분 |
| Vite | 약 5분 |
파이프라인은 초기 개선 이후 큰 변경 없이 유지되고 있었습니다.
이후 모노레포 구조의 도입과 패키지 매니저의 변경 (npm → pnpm)을 진행했습니다. 이 과정에서 비효율적인 설정이 많이 포함된 상태였습니다.
빌드 병목 분석 및 개선
로그 분석 결과 컴파일이 아닌, 준비 단계에서 낭비되는 시간이 길었습니다.
1. Submodule 인증 구조 개선
기존 구조
기존 Amplify 빌드 파이프라인에서는 Submodule 동기화에 이런 작업들을 진행했습니다.
→ jq 설치
→ Secrets Manager API
→ token fetch
→ https rewrite
→ git submodule sync
왜 이런 불필요하게 보이는 작업들이 필요했던 건지 궁금해서 사내 위키를 찾아봤습니다. 당시에는 다음과 같은 인식이 있었습니다.
"Amplify CI 환경에서는 SSH 인증을 사용할 수 없다."
이 말은 사실이 아닙니다.
Amplify는 최초에 연결된 GitHub 레포지토리에 대해서만 접근 권한을 가지며, 기본적으로는 Submodule로 연결된 다른 GitHub 레포지토리에 접근할 수 없습니다. 여기서 오해가 생겨서 SSH를 https로 치환하는 작업을 했던 것으로 추정됩니다.
Amplify에서 SSH 사용이 불가능한 것이 아니라 접근 권한 범위의 문제였을 것으로 판단해서 이 과정을 개선하려고 했습니다.
해결 방법
이 문제는 Deploy Key 기반 SSH 인증으로 해결할 수 있습니다.
-
Submodule Repository → Deploy Key 등록 (SSH Public Key)
-
Amplify → Private Key 환경 변수 설정 → SSH 기반 Submodule 접근
-
시크릿 매니저로 관리하던 https 접근 토큰을 삭제
이 방식으로 다음 문제를 모두 해결했습니다.
- Secrets Manager 호출 제거
- jq 설치 제거
.gitmodulesrewrite 제거- 로컬 / CI 인증 방식 일치
결과
| 제거한 작업 | 시간 |
|---|---|
| yum + jq 설치 | 24초 |
| Secrets Manager API | 13초 |
| https rewrite | 1초 |
- 약 40~50초 시간 개선
2. 모노레포 의존성 설치 최적화
기존 방식
pnpm install --frozen-lockfile이 명령은 모노레포 전체 패키지를 설치합니다.
개선
빌드 대상 서비스만 설치하도록 변경했습니다.
pnpm install --filter "@inf/web..." --frozen-lockfile결과
빌드 시간은 약 8~9초 단축되었습니다.
예상보다 체감 성능 개선은 크지 않았습니다.
성능 개선이 크지 않았던 이유
- 서비스 간 공통 의존성 공유율이 높음
- pnpm의 하드 링크 구조가 이미 매우 빠름
아래와 같은 장점도 있어서 적절한 의존성 설치는 필요한 작업이라고 생각합니다.
- Phantom Dependency 방지
- 빌드 파이프라인 정합성 확보
- 불필요한 패키지 설치 차단
3. 캐시 전략 개선
기존 방식
기존 캐시 전략으로 node_modules 전체에 캐시를 사용 중이었습니다.
캐시를 위해서는 압축 → 다운로드 → 압축 해제 → 업로드 과정이 필요합니다.
pnpm의 node_modules 구조는 symlink + hard link 기반입니다. node_modules 전체를 캐시하면 매우 많은 파일 I/O가 발생하고 캐시 과정 자체가 병목이 될 수 있습니다.
실제로 캐시 관련 작업에 약 2분 이상이 소요되고 있었습니다.
개선
node_modules캐시를 제거하고 pnpm store만 캐시하도록 변경했습니다.- Amplify 네트워크 속도가 빠르기 때문에 캐시 미사용 전략도 고려했습니다. 서비스별로 세 번 정도 테스트한 결과, 캐시를 사용할 때 미약한 성능 향상이 있었기에 pnpm-store 캐시는 유지했습니다.
결과
빌드 성능은 다음과 같이 개선되었습니다. (배포 오버헤드 제외)
| 서비스 | AS-IS | TO-BE | 개선율 |
|---|---|---|---|
| BIZ-WEB | 8분 | 5분 15초 | 34% |
| BO-WEB | 5분 | 2분 55초 | 42% |
| INF-WEB | 8분 | 5분 10초 | 35% |
Phase별 성능 변화
| Phase (빌드 단계) | AS-IS | TO-BE | 단축 시간 | 주요 원인 |
|---|---|---|---|---|
| 사전 준비 & 캐시 추출 | 1분 11초 | 15초 | -56초 | 무거운 node_modules 캐시 제거 |
| 인증 & 의존성 설치 | 1분 40초 | 43초 | -57초 | jq 설치 및 API 호출 제거 |
| Build (컴파일) | 2분 19초 | 2분 18초 | -1초 | Next.js 빌드 자체는 동일 |
| 캐시 생성 & 업로드 | 1분 47초 | 36초 | -1분 11초 | node_modules 압축 제거 |
| 순수 스크립트 실행 시간 | 6분 57초 | 4분 8초 | -2분 49초 |
참고: 위 표의 시간은 특정 서비스(@inf/web) 한 개의 Amplify 빌드 파이프라인 동작 시간 기준입니다. 컨테이너 프로비저닝, 내부 환경 캐싱, 배포 등은 제외했습니다.
정리
이번 최적화를 통해 다음과 같은 개선이 이루어졌습니다.
Submodule 인증 구조
Secrets Manager 기반 토큰 → Deploy Key 기반 SSH
모노레포 의존성 설치
pnpm install → pnpm install --filter
캐시 전략
node_modules 캐시 → pnpm store 캐시
Appendix: Amplify 설정 예시
- 실제 Amplify 설정의 구조를 단순화한 예시입니다.
frontend:
phases:
preBuild:
commands:
# Deploy Key 기반 SSH 인증
- mkdir -p ~/.ssh
- printf '%s' "$DEPLOY_KEY_BASE64" > ~/.ssh/key.base64
- base64 --decode ~/.ssh/key.base64 > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan github.com >> ~/.ssh/known_hosts
# Submodule 동기화
- git submodule sync
- git submodule update --init --recursive
# pnpm 환경 준비
- corepack enable pnpm
# 모노레포 의존성 설치 (target service only)
- pnpm install --filter "@inf/web..." --frozen-lockfile
build:
commands:
- pnpm run build
cache:
paths:
- ~/.pnpm-store/**/*
- .next/cache/**인사이트
이번 작업을 통해 몇 가지 흥미로운 점을 확인할 수 있었습니다.
- CI/CD 파이프라인은 한 번 구축되면 장기간 방치되기 쉽다
node_modules캐시는 항상 성능 향상을 보장하지 않는다- pnpm은 이미 상당히 최적화된 구조를 가지고 있다
- AWS Amplify에서도 SSH 인증을 충분히 활용할 수 있다
빌드 로그를 기반으로 병목을 분석하고 개선하는 과정에서 많은 인사이트를 얻을 수 있었습니다.
비슷한 환경에서 Amplify CI/CD를 운영하고 있다면 빌드 로그를 한 번 확인해보는 것도 좋은 출발점이 될 것 같습니다.
긴 글 읽어주셔서 감사합니다.