Blog
HomePostsTagsCategories
© 2026 Hyeonseok An. All rights reserved.
GitHubEmailInstagram
Home
DevOps
CI/CD 핵심 파일 완벽 정리: Dockerfile, Docker Compose, Git...

CI/CD 핵심 파일 완벽 정리: Dockerfile, Docker Compose, GitHub Actions

August 23, 2025
Loading...
TILdeploydockerCI/CDgithub actionsdocker composeGithubDevOpsDockerFileInfrastructureContainer개발 환경자동 배포
Cover image for CI/CD 핵심 파일 완벽 정리: Dockerfile, Docker Compose, GitHub Actions

개요

현대 웹 개발에서 CI/CD(Continuous Integration/Continuous Deployment)는 선택이 아닌 필수입니다. 개발자들이 코드를 푸시하면 자동으로 테스트되고, 빌드되고, 배포되는 환경을 구축하는 것이 당연한 시대가 되었죠. 이런 자동화된 개발 파이프라인의 핵심을 이루는 세 가지 파일이 있습니다: Dockerfile, docker-compose.yml, 그리고 GitHub Actions 워크플로우입니다.
이 포스트에서는 각 파일의 역할과 상호작용을 깊이 있게 탐구하고, 실제 프로덕션 환경에서 사용할 수 있는 베스트 프랙티스를 제시합니다. 단순한 문법 설명을 넘어서, 왜 이런 방식으로 구성해야 하는지, 어떤 문제들을 해결할 수 있는지까지 포괄적으로 다뤄보겠습니다.

들어가며: 현대 개발 환경의 필수 삼종 세트

개발자로서 "내 컴퓨터에서는 잘 돌아가는데?"라는 말을 한 번쯤은 해본 경험이 있을 겁니다. 이런 환경 차이로 인한 문제들은 프로젝트가 커질수록, 팀이 커질수록 더욱 심각해집니다. 로컬에서는 완벽하게 동작하던 애플리케이션이 서버에서는 의존성 충돌로 실행되지 않거나, 팀원마다 다른 Node.js 버전을 사용해서 예상치 못한 버그가 발생하는 상황들 말이죠.
이러한 문제들을 해결하기 위해 등장한 것이 컨테이너화와 CI/CD 자동화입니다. 컨테이너는 애플리케이션과 그 실행 환경을 하나의 패키지로 묶어서 "어디서든 동일하게 실행"되도록 보장합니다. 그리고 CI/CD는 개발자가 코드를 푸시하는 순간부터 프로덕션 배포까지의 모든 과정을 자동화합니다.
오늘 살펴볼 세 파일은 각각 다른 역할을 담당합니다. Dockerfile은 애플리케이션 실행 환경의 '설계도' 역할을, docker-compose.yml은 여러 서비스를 조율하는 '오케스트레이터' 역할을, GitHub Actions 워크플로우는 전체 과정을 자동화하는 '로봇 집사' 역할을 합니다. 이 삼종 세트가 어떻게 현대적인 개발 워크플로우를 완성시키는지 하나씩 깊이 있게 살펴보겠습니다.

Dockerfile: 애플리케이션 환경의 완벽한 레시피

Dockerfile을 이해하는 가장 좋은 방법은 요리 레시피와 비교하는 것입니다. 맛있는 요리를 만들기 위해서는 정확한 재료와 순서대로 진행되는 조리 과정이 필요하죠. Dockerfile도 마찬가지로 애플리케이션이 완벽하게 실행되는 환경을 만들기 위한 단계별 지시서입니다.

Dockerfile의 핵심 철학: 불변성과 재현성

Dockerfile의 가장 중요한 특징은 **불변성(Immutability)**입니다. 같은 Dockerfile로 만든 이미지는 언제, 어디서 빌드하더라도 동일한 결과를 보장합니다. 이는 개발 환경의 일관성을 유지하는 핵심 요소입니다.

주요 명령어와 그 뒤에 숨은 원리

각 Dockerfile 명령어는 새로운 레이어를 생성합니다. 이 레이어 시스템 덕분에 Docker는 변경된 부분만 다시 빌드할 수 있어 효율적입니다.
# 기반 이미지 선택 - 모든 것의 시작점 FROM node:20-alpine AS builder # 작업 디렉토리 설정 - 이후 모든 명령어의 기준점 WORKDIR /app # 의존성 파일부터 복사 - Docker 캐싱 최적화의 핵심 COPY package*.json ./ # 의존성 설치 - 프로덕션용으로만 설치해 이미지 크기 최소화 RUN npm ci --only=production # 소스 코드 복사 - 의존성 설치 후에 복사해 캐시 효율성 극대화 COPY . . # 빌드 과정 (필요한 경우) # RUN npm run build # 최종 실행 환경 구성 - 멀티스테이지 빌드로 이미지 크기 최소화 FROM node:20-alpine WORKDIR /app # 빌드 스테이지에서 결과물만 복사 COPY --from=builder /app . # 애플리케이션이 사용할 포트 명시 EXPOSE 3000 # 컨테이너 시작 시 실행될 명령어 - 배열 형태로 작성해 시그널 처리 최적화 CMD ["node", "src/index.js"]

베스트 프랙티스 심화 분석

멀티스테이지 빌드는 단순히 이미지 크기를 줄이는 것 이상의 의미를 가집니다. 개발용 도구와 빌드 도구들을 최종 이미지에서 제거함으로써 보안 공격 표면을 줄이고, 런타임 성능을 향상시킵니다.
레이어 캐싱 최적화도 중요한 개념입니다. 자주 변경되지 않는 의존성 파일을 먼저 복사하고 설치한 후, 자주 변경되는 소스 코드를 나중에 복사하는 순서는 Docker의 레이어 캐싱 메커니즘을 최대한 활용하는 전략입니다.

실무에서 마주치는 함정들과 해결책

Node.js 프로젝트에서 흔히 하는 실수는 .dockerignore 파일을 작성하지 않아서 node_modules 디렉토리까지 복사되는 것입니다. 이는 빌드 시간을 늘리고 이미지 크기를 불필요하게 키웁니다.
node_modules npm-debug.log .git .env.local README.md
보안 측면에서 주의할 점은 USER 명령어를 사용해 root 권한으로 애플리케이션을 실행하지 않는 것입니다. 대부분의 애플리케이션은 root 권한이 필요하지 않으며, 이는 잠재적인 보안 위험을 줄여줍니다.

참고 자료와 더 깊은 학습

Docker의 공식 문서는 항상 가장 신뢰할 수 있는 정보원입니다.
  • Dockerfile reference: 모든 Dockerfile 명령어에 대한 완벽한 레퍼런스
  • Best practices for writing Dockerfiles: Docker 팀이 직접 제공하는 베스트 프랙티스
  • Node.js Dockerfile Best Practices: Node.js 특화 가이드라인

Docker Compose: 멀티 컨테이너 오케스트레이션의 마법사

실제 웹 애플리케이션은 혼자 동작하지 않습니다. 데이터베이스, 캐시 서버, 메시지 큐 등 다양한 서비스들이 협력해서 하나의 완전한 시스템을 만들어냅니다. Docker Compose는 이런 복잡한 멀티 컨테이너 환경을 하나의 YAML 파일로 정의하고 관리할 수 있게 해주는 도구입니다.

오케스트레이션의 핵심 개념

Docker Compose가 해결하는 가장 큰 문제는 서비스 간 의존성 관리입니다. 예를 들어, 웹 애플리케이션은 데이터베이스가 완전히 준비된 후에 시작되어야 하고, 각 서비스는 서로를 찾을 수 있어야 합니다. Compose는 이런 복잡한 관계들을 선언적으로 정의할 수 있게 해줍니다.

실전 예시: 완전한 웹 애플리케이션 스택

version: '3.8' services: # 메인 애플리케이션 서비스 web: build: context: . dockerfile: Dockerfile container_name: my-blog-app ports: - "8080:3000" environment: - NODE_ENV=production - DATABASE_URL=mongodb://mongodb:27017/myblog - REDIS_URL=redis://redis:6379 depends_on: - mongodb - redis restart: unless-stopped volumes: - ./logs:/app/logs # 로그 파일 영속화 networks: - app-network # MongoDB 데이터베이스 mongodb: image: mongo:7-jammy container_name: mongodb ports: - "27017:27017" environment: - MONGO_INITDB_ROOT_USERNAME=admin - MONGO_INITDB_ROOT_PASSWORD=password123 volumes: - mongodb_data:/data/db # 데이터베이스 데이터 영속화 - ./mongo-init:/docker-entrypoint-initdb.d # 초기화 스크립트 restart: unless-stopped networks: - app-network # Redis 캐시 서버 redis: image: redis:7-alpine container_name: redis ports: - "6379:6379" volumes: - redis_data:/data restart: unless-stopped networks: - app-network # 데이터 영속성을 위한 볼륨 정의 volumes: mongodb_data: driver: local redis_data: driver: local # 네트워크 정의 (서비스 간 통신) networks: app-network: driver: bridge

Docker Compose의 고급 기능들

환경별 설정 관리는 실무에서 매우 중요합니다. docker-compose.override.yml 파일을 사용해서 개발 환경과 프로덕션 환경의 차이를 관리할 수 있습니다.
# docker-compose.override.yml (개발 환경용) version: '3.8' services: web: environment: - NODE_ENV=development - DEBUG=true volumes: - .:/app # 소스 코드 실시간 반영 - /app/node_modules # 컨테이너의 node_modules 보호 command: npm run dev # 개발용 명령어 오버라이드
healthcheck는 서비스의 실제 준비 상태를 확인하는 중요한 기능입니다.
services: web: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 5 start_period: 30s

실무에서의 함정과 해결책

포트 충돌 문제는 로컬 개발 환경에서 자주 발생합니다. 호스트의 포트를 동적으로 할당받거나, 개발자마다 다른 포트를 사용하도록 환경변수를 활용하는 것이 좋습니다.
데이터 손실 방지를 위해서는 반드시 volumes을 사용해 중요한 데이터를 영속화해야 합니다. 특히 데이터베이스 데이터는 컨테이너가 삭제되어도 보존되어야 합니다.

참고 자료

  • Compose file version 3 reference: YAML 파일의 모든 키와 값에 대한 완벽한 레퍼런스
  • Overview of Docker Compose: Docker Compose의 전반적인 개념과 사용법

GitHub Actions: 배포 자동화의 게임 체인저

GitHub Actions는 소프트웨어 개발의 패러다임을 완전히 바꾼 혁신적인 도구입니다. 과거에는 복잡한 CI/CD 서버를 직접 구축하고 관리해야 했지만, 이제는 YAML 파일 하나로 강력한 자동화 파이프라인을 구축할 수 있게 되었습니다.

CI/CD의 진화: 수동에서 완전 자동화까지

전통적인 배포 과정을 생각해보세요. 개발자가 코드를 완성하면, 수동으로 테스트를 실행하고, 빌드하고, 서버에 접속해서 배포하고, 서비스를 재시작하는 일련의 과정을 거쳐야 했습니다. 이 과정에서 사람의 실수가 발생할 가능성이 높고, 시간도 많이 소요됩니다.
GitHub Actions는 이 모든 과정을 코드로 정의해서 완전 자동화합니다. 더 나아가, Infrastructure as Code 개념을 통해 배포 과정 자체도 버전 관리되고, 팀원들과 공유되며, 지속적으로 개선될 수 있습니다.

워크플로우의 구조와 실행 모델

GitHub Actions는 이벤트 기반 시스템입니다. 특정 이벤트(코드 푸시, 풀 리퀘스트, 스케줄 등)가 발생하면 미리 정의된 워크플로우가 자동으로 실행됩니다.
name: Complete CI/CD Pipeline # 워크플로우 트리거 정의 - 언제 실행될지 결정 on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: - cron: '0 2 * * *' # 매일 오전 2시에 실행 (야간 빌드) # 환경 변수 전역 정의 env: NODE_VERSION: '20' DOCKER_IMAGE_NAME: 'my-blog-app' jobs: # 1단계: 코드 품질 검사 code-quality: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install Dependencies run: npm ci - name: Run ESLint run: npm run lint - name: Run Tests run: npm run test:coverage - name: Upload Coverage Reports uses: codecov/codecov-action@v3 with: file: ./coverage/lcov.info # 2단계: Docker 이미지 빌드 및 푸시 build-and-push: needs: code-quality # 코드 품질 검사 통과 후에만 실행 runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' # main 브랜치에만 실행 outputs: image-tag: ${{ steps.meta.outputs.tags }} image-digest: ${{ steps.build.outputs.digest }} steps: - name: Checkout Code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract Metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.DOCKER_IMAGE_NAME }} tags: | type=ref,event=branch type=sha,prefix={{branch}}- - name: Build and Push Docker Image id: build uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 # 멀티 아키텍처 지원 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha # GitHub Actions 캐시 활용 cache-to: type=gha,mode=max # 3단계: 보안 스캔 security-scan: needs: build-and-push runs-on: ubuntu-latest steps: - name: Run Trivy Vulnerability Scanner uses: aquasecurity/trivy-action@master with: image-ref: ${{ needs.build-and-push.outputs.image-tag }} format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy Scan Results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' # 4단계: 스테이징 환경 배포 deploy-staging: needs: [build-and-push, security-scan] runs-on: self-hosted environment: name: staging url: https://staging.myblog.com steps: - name: Deploy to Staging uses: appleboy/ssh-action@master with: host: ${{ secrets.STAGING_HOST }} username: ${{ secrets.STAGING_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /opt/staging/my-blog-app docker-compose pull docker-compose up -d --remove-orphans docker system prune -f - name: Health Check run: | curl -f https://staging.myblog.com/health || exit 1 # 5단계: 프로덕션 배포 (승인 필요) deploy-production: needs: deploy-staging runs-on: self-hosted environment: name: production url: https://myblog.com if: github.ref == 'refs/heads/main' steps: - name: Deploy to Production uses: appleboy/ssh-action@master with: host: ${{ secrets.PRODUCTION_HOST }} username: ${{ secrets.PRODUCTION_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /opt/production/my-blog-app docker-compose pull docker-compose up -d --remove-orphans docker system prune -f - name: Post-deployment Tests run: | curl -f https://myblog.com/health || exit 1 curl -f https://myblog.com/api/status || exit 1 - name: Notify Team uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} webhook_url: ${{ secrets.SLACK_WEBHOOK }} if: always()

고급 기능과 최적화 기법

매트릭스 빌드는 여러 환경에서 동시에 테스트할 때 유용합니다.
strategy: matrix: node-version: [18, 20, 22] os: [ubuntu-latest, windows-latest, macos-latest]
캐싱은 빌드 시간을 대폭 단축시킬 수 있는 핵심 기능입니다.
- name: Cache Dependencies uses: actions/cache@v3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-

보안 베스트 프랙티스

시크릿 관리는 매우 중요합니다. 민감한 정보는 절대 코드에 직접 포함시키면 안 되며, GitHub의 Secrets 기능을 활용해야 합니다.
최소 권한 원칙을 적용해서 GITHUB_TOKEN의 권한을 필요한 것만으로 제한하는 것도 좋은 보안 관행입니다.
permissions: contents: read packages: write security-events: write

참고 자료

  • Introduction to GitHub Actions: GitHub Actions의 기본 개념을 체계적으로 학습할 수 있는 최고의 시작점
  • Workflow syntax for GitHub Actions: 워크플로우 파일의 모든 문법에 대한 완벽한 레퍼런스
  • GitHub Marketplace: 재사용 가능한 수천 개의 액션들을 찾아볼 수 있는 보물창고

실전 적용: 세 파일이 만들어내는 완벽한 워크플로우

이제 Dockerfile, docker-compose.yml, GitHub Actions 워크플로우가 어떻게 하나의 완벽한 시스템으로 통합되는지 실제 시나리오를 통해 살펴보겠습니다. 이 세 파일의 협업은 현대 소프트웨어 개발의 핵심을 보여줍니다.

개발자의 하루: 코드 푸시부터 프로덕션 배포까지

아침에 출근한 개발자 김씨가 새로운 기능을 개발하고 git push origin main 명령어를 실행하는 순간부터 시작되는 자동화된 여정을 따라가보겠습니다.
1단계: 코드 푸시와 워크플로우 트리거 개발자가 코드를 푸시하면 GitHub Actions가 즉시 반응합니다. .github/workflows/deploy.yml 파일에 정의된 on: push: branches: [main] 조건이 충족되어 워크플로우가 시작됩니다.
2단계: 자동화된 품질 검사 첫 번째 job인 code-quality가 실행됩니다. ESLint를 통한
Tags:TILdeploydockerCI/CDgithub actionsdocker composeGithubDevOpsDockerFileInfrastructureContainer개발 환경자동 배포