DevOps
Docker 빌드 129초 → 21초, 모노레포 CI/CD 최적화 실전기
· 5분 읽기
PR 하나당 2분 30초 기다리던 CI를 최대 84% 단축하고, 일반적인 코드 변경 PR은 42초까지 줄인 개선 경험
1. 들어가며
인턴으로 참여한 서비스 프로젝트에서 NestJS(백엔드)와 Next.js(프론트엔드) 기반의 pnpm 모노레포 구조를 사용하고 있었습니다.
- PR 검증: GitHub Actions
- 실제 배포: GCP Cloud Build → Cloud Run
개발을 진행하면서 가장 크게 느낀 문제는 느린 CI 속도였습니다.
코드 한 줄 수정에도 2분 30초 이상 대기해야 했고, 이 시간이 반복되면서 개발 흐름이 끊기고 생산성이 저하되는 문제가 있었습니다.
2. 문제의 본질
기존 CI 구조는 변경 범위를 고려하지 않고 모든 작업을 수행하고 있었습니다.
- 코드 1줄 변경에도 Docker 전체 재빌드
- 의존성 변경이 없어도 pnpm install 매번 실행
즉, 불필요한 작업을 반복하는 구조였습니다.
3. 최적화 전 상황
3.1 전체 흐름
모든 PR
→ 의존성 설치
→ 백엔드 빌드
→ 프론트엔드 빌드
→ Docker 이미지 빌드 (백엔드 + 프론트)3.2 소요 시간
워크플로시간
<table style="border-collapse: collapse; width: 100%;" border="1" data-ke-align="alignLeft"><tbody><tr><td>PR 검증</td><td>약 2분 30초</td></tr><tr><td>배포</td><td>약 10분</td></tr></tbody></table>
4. 핵심 최적화
4.1 조건부 Docker 빌드
대부분의 PR은 소스 코드만 변경되는데, 기존에는 매번 Docker 이미지를 빌드하고 있었습니다.
이를 개선하기 위해 특정 파일이 변경된 경우에만 Docker 빌드를 실행하도록 변경했습니다.
# 변경된 파일 기준 실행 여부 판단 (예시)
if: <infra-or-dockerfile-changed>4.2 결과
- 일반적인 코드 수정 PR에서 Docker 빌드 완전 제거
- 약 2분 이상의 시간 절약
5. 적용한 최적화 항목
5.1 pnpm 의존성 캐싱
lockfile 기준으로 캐시 키를 생성하여 의존성이 변경되지 않으면 설치 과정을 생략하도록 개선했습니다.
- uses: actions/cache
with:
path: <pnpm-store-path>
key: <cache-key-based-on-lockfile>5.2 lint 단계 추가
빌드 이전에 코드 품질을 검증하도록 lint 단계를 추가했습니다.
pnpm install
pnpm run lint
pnpm run build5.3 Docker BuildKit 캐시 활용
Docker 빌드 시 레이어 캐시를 재사용하도록 설정했습니다.
cache-from: <cache-source>
cache-to: <cache-destination>5.4 배포 시 Docker 레이어 재사용
이전 이미지(latest)를 활용하여 변경된 부분만 다시 빌드하도록 개선했습니다.
docker build --cache-from <previous-image>5.5 Dockerfile 최적화
- 중복된 의존성 설치 제거
- 불필요한 캐시 무효화 제거
6. 실측 결과
6.1 최적화 전
<table style="border-collapse: collapse; width: 100%;" border="1" data-ke-align="alignLeft"><tbody><tr><td>항목</td><td>시간</td></tr><tr><td>Docker (frontend)</td><td>129초</td></tr><tr><td>전체 CI</td><td>약 129초</td></tr></tbody></table>
6.2 최적화 후
<table style="border-collapse: collapse; width: 100%;" border="1" data-ke-align="alignLeft"><tbody><tr><td>항목</td><td>시간</td></tr><tr><td>Docker (frontend)</td><td>21초</td></tr><tr><td>전체 CI</td><td>약 42초</td></tr></tbody></table>
7. 개선 효과
- Docker 빌드: 129초 → 21초 (약 84% 단축)
- 일반 PR: 약 129초 → 42초 (약 67% 단축)
8. 운영하면서 느낀 점
8.1 CI 최적화의 핵심
CI 최적화에서 가장 중요한 것은 속도를 높이는 것이 아니라 불필요한 작업을 제거하는 것입니다.
8.2 캐시는 누적 효과로 봐야 한다
초기 빌드는 캐시 저장 비용으로 인해 느릴 수 있지만, 반복 실행에서는 큰 성능 개선을 제공합니다.
8.3 작은 개선의 누적
캐시, 조건 분기, Dockerfile 정리와 같은 작은 개선들이 전체 CI 속도에 큰 영향을 미쳤습니다.
9. 마무리
CI/CD는 한 번 구축하고 끝나는 것이 아니라 지속적으로 개선해야 하는 영역이라고 느꼈습니다.
이번 경험을 통해 다음을 배울 수 있었습니다.
- 병목을 찾는 방법
- 캐시 전략의 중요성
- 불필요한 작업 제거의 효과
비슷한 환경에서 CI가 느리다면 "이 작업이 매번 필요한가?"를 먼저 점검해보는 것을 추천드립니다.
댓글
GitHub 계정으로 댓글을 남길 수 있어요.