
매번 명령어를 치고 계신가요? 수동 빌드의 늪에서 탈출하기
퇴근 직전, 마지막 배포를 위해 터미널을 엽니다. docker-compose build 뒤에 붙는 길고 복잡한 인자들을 기억해내려 애를 씁니다. --build-arg VERSION=1.2.3, --build-arg ENV=production 같은 옵션들을 하나라도 빼먹거나 오타를 내면 배포는 그대로 실패로 이어져요. 다시 로그를 확인하고, 명령어를 수정하고, 처음부터 다시 빌드를 시작하는 과정은 개발자의 소중한 에너지를 갉아먹습니다.
이런 경험은 단순히 귀찮은 문제를 넘어 서비스 안정성에도 큰 위협이 돼요. 사람이 직접 입력하는 명령은 반드시 실수를 동반하기 마련이니까요. 운영 환경에 테스트용 빌드 인자를 넣거나, 잘못된 빌드 컨텍스트를 지정해 엉뚱한 파일이 이미지에 포함되는 사고는 대형 장애로 번질 수 있습니다. 이제는 이런 반복적이고 위험한 수동 작업에서 벗어나야 할 때예요.
컴포즈 build 옵션 자동화는 단순히 타이핑을 줄이는 기술이 아니에요. 빌드 과정을 정형화하고, 환경별 설정을 규격화하며, 최종적으로는 사람이 개입하지 않는 CI/CD 파이프라인을 구축하는 첫걸음입니다. 이 과정을 통해 우리는 배포의 불확실성을 제거하고 오직 코드의 품질에만 집중할 수 있는 환경을 만들 수 있어요.
이 글에서는 다음과 같은 내용을 상세히 다뤄요.
- 컴포즈 빌드 옵션과 컨텍스트의 핵심 개념 이해
- 빌드 인자를 효율적으로 관리하는 환경 변수 활용법
- 쉘 스크립트와 CI/CD를 이용한 빌드 프로세스 자동화
- 배포 실패를 방지하는 검증 및 롤백 전략
자동화 설계 전 반드시 챙겨야 할 핵심 요소
무작정 스크립트를 짜기 전에 우리가 무엇을 자동화할 것인지 명확히 정의해야 해요. 도커 컴포즈 환경에서 빌드를 자동화한다는 것은 단순히 명령어를 저장하는 것이 아니라, 빌드에 필요한 모든 데이터와 설정값을 코드로서 관리한다는 뜻입니다.
빌드 컨텍스트와 인자의 관계
가장 먼저 이해해야 할 개념은 빌드 컨텍스트(Build Context)예요. 도커 엔진이 빌드를 위해 로컬 파일 시스템에서 읽어올 수 있는 범위가 어디까지인지를 결정하는 아주 중요한 설정입니다. 컨텍스트를 너무 넓게 잡으면 불필요한 파일까지 도커 데몬으로 전송되어 빌드 속도가 느려지고, 너무 좁게 잡으면 빌드에 필요한 설정 파일을 찾지 못해 오류가 발생해요. 또한, 빌드 인자(Build Args)는 이미지 생성 시점에 주입되는 변수로, 이 값들을 어떻게 관리하느냐에 따라 환경별(dev, staging, prod) 이미지를 효율적으로 생성할 수 있습니다.
빌드 인자(Build Args)는 이미지 생성 시에만 유효하며, 컨테이너가 실행되는 시점의 환경 변수(Environment Variables)와는 구분해야 해요. 빌드 시점에 라이브러리 버전을 결정하거나 컴파일 옵션을 넣을 때 주로 사용합니다.
자동화 수준 결정하기
모든 프로젝트에 거창한 CI/CD 시스템이 필요한 것은 아니에요. 현재 팀의 규모와 배포 빈도에 따라 적절한 자동화 수준을 선택하는 것이 효율적입니다. 아래 표를 통해 여러분의 상황에 맞는 방식을 고민해 보세요.
| 자동화 단계 | 주요 방식 | 장점 | 단점 |
|---|---|---|---|
| 기초 단계 | 쉘 스크립트 (.sh) | 구현이 매우 빠르고 간단함 | 여러 개발자의 환경이 다를 수 있음 |
| 중급 단계 | Makefile 활용 | 명령어 규격화 및 의존성 관리 용이 | Makefile 문법 학습 필요 |
| 고급 단계 | CI/CD (GitHub Actions 등) | 완전 자동화 및 통합 테스트 가능 | 초기 설정 및 파이프라인 관리 비용 발생 |
규모가 작은 개인 프로젝트라면 쉘 스크립트만으로도 충분한 효과를 볼 수 있지만, 여러 명이 협업하는 팀 단위 프로젝트라면 CI/CD 파이프라인 구축을 강력히 추천해요. 환경의 일관성을 보장할 수 있기 때문입니다.
빌드 자동화 구축을 위한 5단계 실전 가이드
이제 이론을 넘어 실제 코드를 통해 자동화 시스템을 구축해 볼게요. 단순히 명령어를 실행하는 것을 넘어, 유지보수가 쉽고 확장 가능한 구조를 만드는 것이 핵심입니다.
STEP 1. docker-compose.yml 구조 최적화
자동화의 기초는 컴포즈 파일 자체를 유연하게 만드는 거예요. 하드코딩된 값들을 모두 변수로 치환해야 합니다. 특히 build 섹션에서 context와 args를 명확히 분리하는 것이 중요해요.
services:
web-app:
build:
context: . # 빌드 시작 지점
dockerfile: Dockerfile
args:
- APP_VERSION=${APP_VERSION}
- BUILD_ENV=${BUILD_ENV}
image: my-app:${APP_VERSION}
이렇게 설정하면 컴포즈 파일은 건드리지 않고도, 외부에서 주입하는 값에 따라 서로 다른 이미지를 만들어낼 수 있습니다. 여기서 context를 현재 디렉토리(.)로 잡았다면, .dockerignore 파일을 반드시 작성하여 node_modules나 .git 같은 무거운 폴더가 빌드 컨텍스트에 포함되지 않도록 막아야 해요. 그래야 빌드 속도가 빨라지고 보안도 지킬 수 있습니다.
STEP 2. .env 파일을 활용한 환경 변수 관리
빌드 인자를 매번 명령어로 입력하는 대신, 환경 변수 파일(.env)을 사용하세요. 이 방식은 개발 환경과 운영 환경의 설정을 분리하는 가장 쉬운 방법입니다.
예를 들어, .env.dev와 .env.prod 파일을 각각 만들어 관리할 수 있어요. .env.dev 파일 내용:APP_VERSION=1.0.0-dev
BUILD_ENV=development
이렇게 만들어두면, 명령어를 실행할 때 docker-compose --env-file .env.dev build라고만 입력하면 됩니다. 명령어가 훨씬 짧아지고 실수의 여지도 줄어들죠.
.env 파일에는 데이터베이스 비밀번호나 API 키 같은 민감한 정보가 포함될 수 있어요. 이런 파일이 절대 Git 저장소에 올라가지 않도록
.gitignore에 반드시 등록해야 합니다. 민감 정보는 CI/CD 도구의 Secret 기능을 활용하세요.STEP 3. 쉘 스크립트로 빌드 프로세스 캡슐화
환경 변수 파일까지 만들었다면, 이제 이 과정을 하나의 실행 파일로 묶어줄 차례입니다. deploy.sh 같은 스크립트를 만들어 빌드, 태깅, 푸시 과정을 한 번에 처리하게 만드세요.
#!/bin/bash
# 1. 변수 설정
ENV_FILE=$1
if [ -z "$ENV_FILE" ]; then
echo "사용법: ./deploy.sh [.env.dev | .env.prod]"
exit 1
fi
# 2. 빌드 실행
echo "🚀 빌드를 시작합니다... (환경: $ENV_FILE)"
docker-compose --env-file $ENV_FILE build
# 3. 이미지 태깅 및 푸시 (예시)
VERSION=$(grep APP_VERSION $ENV_FILE | cut -d '=' -f2)
TAG="my-registry.com/my-app:$VERSION"
echo "📦 이미지를 푸시합니다: $TAG"
docker tag my-app:$VERSION $TAG
docker push $TAG
echo "✅ 모든 작업이 완료되었습니다!"
이제 개발자는 복잡한 옵션을 외울 필요 없이 ./deploy.sh .env.dev 한 줄만 치면 됩니다. 이것이 자동화의 첫 번째 단계인 명령어의 추상화예요.
STEP 4. CI/CD 파이프라인(GitHub Actions) 연동
이제 사람이 직접 스크립트를 실행하는 단계를 넘어, 코드가 Push되면 자동으로 빌드가 돌아가게 만들어 봅시다. 가장 널리 쓰이는 GitHub Actions를 예로 들어볼게요. .github/workflows/deploy.yml 파일을 생성하고 다음과 같이 구성합니다.
name: Build and Push Image
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v3
- name: 도커 로그인
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 빌드 및 푸시
run: |
echo "APP_VERSION=1.0.${{ github.run_number }}" >> .env
echo "BUILD_ENV=production" >> .env
docker-compose --env-file .env build
# 이후 푸시 로직 추가
이 파이프라인은 코드가 메인 브랜치에 합쳐질 때마다 자동으로 버전 번호를 생성하고, 빌드를 수행하며, 이미지 저장소에 업로드합니다. 사람이 개입할 틈이 없는 완전 자동화 상태가 된 것이죠.
STEP 5. 빌드 결과 검증 및 배포 전략
빌드가 성공했다고 해서 모든 것이 끝난 것은 아니에요. 생성된 이미지가 정상적으로 구동되는지 확인하는 단계가 반드시 필요합니다. 빌드 직후 테스트 컨테이너를 띄워보는 단계를 파이프라인에 추가하세요. 간단한 healthcheck 명령어를 통해 애플리케이션이 200 OK를 응답하는지 확인하는 것만으로도 배포 사고의 90% 이상을 막을 수 있습니다.
만약 테스트가 실패한다면? 파이프라인은 즉시 중단되어야 하며, 이미 운영 중인 서버에는 어떠한 영향도 주지 않아야 합니다. 이것이 바로 안전한 배포의 핵심입니다.
자주 하는 실수와 해결법
자동화를 구축하다 보면 예상치 못한 벽에 부딪히곤 해요. 실무에서 가장 빈번하게 발생하는 문제들을 정리했습니다.
- ❌ 빌드 컨텍스트에 불필요한 파일이 너무 많아 빌드가 느려져요
왜 발생하는가:.dockerignore를 설정하지 않아node_modules나 거대한 데이터 파일이 도커 데몬으로 전송되기 때문입니다.
✅ 해결법: 프로젝트 루트에.dockerignore파일을 만들고, 빌드에 불필요한 폴더와 파일을 명시하세요. - ❌ 환경 변수가 빌드 시점에 반영되지 않아요
왜 발생하는가:.env에 정의한 변수는 컨테이너 실행 시점에 주입될 뿐, 이미지 빌드 시점(Dockerfile 내부)에는 자동으로 전달되지 않습니다.
✅ 해결법:docker-compose.yml의build: args:섹션에 해당 변수를 명시적으로 등록해야 합니다. - ❌ CI 서버에서 빌드할 때 권한 오류가 발생해요
왜 발생하는가: CI 환경의 유저가 도커 소켓(docker.sock)에 접근할 권한이 없기 때문입니다.
✅ 해결법: CI 설정에서 도커 데몬에 접근할 수 있는 권한을 부여하거나, Docker-in-Docker(DinD) 방식을 사용하세요. - ❌ 이미지 태그를 항상
latest로만 사용해요
왜 발생하는가: 관리가 편하다는 이유로 태그를 구분하지 않기 때문입니다.
✅ 해결법:latest태그는 지양하고, Git 커밋 해시나 버전 번호를 태그로 사용하여 어떤 코드가 배포되었는지 명확히 추적 가능하게 하세요. - ❌ 빌드 자동화 이후 롤백이 너무 힘들어요
왜 발생하는가: 이전 버전의 이미지를 보관하지 않고 계속 덮어쓰기 때문입니다.
✅ 해결법: 이미지 저장소(Registry)에 모든 버전의 이미지를 보관하고, 문제 발생 시 이전 태그를 가진 이미지로 즉시 다시 배포하는 스크립트를 준비하세요.
자주 묻는 질문
Q. build 옵션과 build_context의 차이가 뭔가요?
build는 빌드에 필요한 설정(Dockerfile 경로, 인자 등)을 모아놓은 섹션이고, context는 그 빌드 설정이 참조할 수 있는 파일들의 범위(디렉토리)를 뜻합니다. 즉, build라는 큰 주머니 안에 context라는 위치 정보가 들어있는 셈이에요.
Q. CI에서 빌드할 때 캐시를 어떻게 활용하면 좋을까요?
GitHub Actions를 사용한다면 docker/build-push-action을 활용해 보세요. 이전 빌드 결과를 캐시로 저장해 두었다가 다음 빌드 때 재사용하면 빌드 시간을 획기적으로 단축할 수 있습니다.
Q. .env 파일은 어떻게 안전하게 관리하나요?
로컬 개발 시에는 .env를 사용하되, Git에는 절대 올리지 마세요. 운영 환경(CI/CD)에서는 GitHub Secrets나 AWS Secrets Manager 같은 전문적인 비밀 관리 도구를 통해 변수를 주입하는 것이 가장 안전합니다.
Q. 빌드 속도가 너무 느린데 무엇부터 개선해야 할까요?
가장 먼저 .dockerignore를 확인하세요. 그리고 Dockerfile 내에서 COPY 명령어를 사용할 때, 자주 변하는 소스 코드 파일보다 자주 변하지 않는 라이브러리 설치 파일(예: package.json)을 먼저 복사하여 레이어 캐시를 극대화하는 것이 정석입니다.
Q. 여러 개의 서비스를 한 번에 자동화할 수 있나요?
네, 가능합니다. docker-compose build 명령어를 실행하면 파일에 정의된 모든 서비스의 빌드 프로세스가 차례대로 진행됩니다. 다만, 서비스 간 의존성이 있다면 depends_on 설정을 통해 순서를 제어해야 합니다.
이제 손을 떼고, 시스템이 일하게 하세요
컴포즈 build 옵션 자동화는 단순히 명령어를 줄이는 기술이 아니라, 배포의 안정성을 확보하고 개발자의 시간을 확보하는 전략입니다. 수동으로 명령어를 입력하며 느끼던 불안함을 이제는 견고한 스크립트와 파이프라인에 맡겨보세요. 처음 설정할 때는 조금 번거로울 수 있지만, 한 번 구축해두면 배포할 때마다 느끼는 스트레스가 사라질 거예요.
.dockerignore로 빌드 컨텍스트를 최적화하여 속도를 높이세요.- 환경 변수는
.env파일로 관리하여 환경별 차이를 명확히 하세요. - 복잡한 인자는 쉘 스크립트로 캡슐화하여 실행을 단순화하세요.
- CI/CD 파이프라인을 통해 사람이 개입하지 않는 흐름을 만드세요.
- 이미지 태그에 버전을 명시하여 추적성과 롤백 가능성을 확보하세요.
자동화는 한 번에 완성되지 않아요. 처음에는 간단한 쉘 스크립트부터 시작해서, 점진적으로 CI/CD를 도입하고 검증 단계를 추가하는 방식으로 확장해 나가는 것이 좋습니다. 완벽한 시스템을 만들려다 시작조차 못 하는 것보다, 지금 당장 가장 귀찮은 명령 하나를 스크립트로 옮기는 것이 훨씬 가치 있습니다.
🚀 오늘 바로 실천할 일:
- 오늘 가장 많이 입력한
docker-compose build옵션을 메모장에 적어보세요. - 그 옵션들을 담은 간단한
deploy.sh파일을 만드세요. - 이번 주 안으로 GitHub Actions에 빌드 단계를 등록해 보세요.
가장 자주 반복하는 명령 하나부터 스크립트로 옮겨 보세요. 그 작은 변화가 여러분의 개발 라이프를 바꿀 것입니다.
관련해서 더 깊이 있는 내용이 궁금하다면 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드를 참고해 보세요.