목. 8월 14th, 2025

G: 개발자라면 한 번쯤은 “내 컴퓨터에서는 잘 되는데, 팀원 컴퓨터에서는 왜 안 되지?” 라는 고민을 해보셨을 겁니다. 🤯 또는, 복잡한 프로젝트를 시작할 때 수십 가지의 개발 환경 설정에 시간을 낭비하며 진이 빠진 경험도 있을 텐데요. 오늘 소개해 드릴 Docker Compose는 이러한 개발자들의 오랜 숙원을 해결해 줄 마법 지팡이 같은 도구입니다!

이 글에서는 Docker Compose가 무엇인지, 왜 현대 개발 환경에서 필수적인지, 그리고 어떻게 활용할 수 있는지 상세하고 친절하게 알려드리겠습니다. 함께 개발 생산성을 최대로 끌어올려 볼까요? 🚀


1. Docker Compose란 무엇인가요? 🐳

Docker Compose를 한 문장으로 정의하자면 다음과 같습니다:

“여러 개의 Docker 컨테이너로 이루어진 애플리케이션을 정의하고 실행하기 위한 도구”

좀 더 쉽게 설명해 볼까요? 우리가 웹 서비스를 만든다고 생각해 봅시다. 단순히 웹 서버만 필요한 것이 아닙니다.

  • 사용자 데이터를 저장할 데이터베이스 (DB) (예: PostgreSQL, MySQL)
  • 세션이나 캐시를 관리할 인메모리 데이터 저장소 (예: Redis)
  • 사용자의 요청을 처리할 백엔드 애플리케이션 (예: Node.js, Spring Boot, Python Django)
  • 정적인 웹 페이지를 제공하거나 백엔드로 요청을 전달할 웹 서버 (예: Nginx, Apache)

이 모든 구성 요소는 각각 독립적인 소프트웨어지만, 서로 유기적으로 연결되어 하나의 서비스를 이룹니다. Docker는 각각의 소프트웨어를 ‘컨테이너’라는 독립적인 환경에 격리시켜 실행할 수 있게 해주는 강력한 도구입니다.

하지만 문제는, 이 여러 컨테이너를 함께 관리하는 것입니다. 각 컨테이너를 일일이 docker run ... 명령어로 실행하고, 서로 통신할 수 있도록 네트워크를 설정하고, 데이터를 보존하기 위해 볼륨을 연결하는 과정은 매우 번거롭고 실수하기 쉽습니다. 😵‍💫

바로 이때! Docker Compose가 등장합니다. Docker Compose는 .yml (YAML) 파일 하나에 애플리케이션의 모든 서비스를 정의해 놓고, 단 하나의 명령어로 이 모든 서비스를 한 번에 빌드하고, 실행하고, 중지하고, 관리할 수 있게 해줍니다. 마치 오케스트라의 지휘자처럼 여러 컨테이너를 조율하여 하나의 완벽한 하모니를 만들어내는 것이죠! 🎼

🧩 docker-compose.yml 파일 맛보기

Docker Compose의 핵심은 바로 docker-compose.yml 파일입니다. 이 파일은 애플리케이션을 구성하는 서비스들(컨테이너들), 네트워크, 볼륨 등을 어떻게 설정할지 정의하는 청사진 역할을 합니다.

간단한 예시를 통해 docker-compose.yml 파일이 어떻게 생겼는지 살펴보겠습니다.

# docker-compose.yml 예시 (Node.js 웹앱 + Redis + PostgreSQL)

version: '3.8' # Docker Compose 파일 형식 버전 지정 (최신 버전을 사용 권장)

services: # 애플리케이션을 구성하는 각 서비스를 정의합니다.

  web: # Node.js 웹 애플리케이션 서비스
    build: . # 현재 디렉토리의 Dockerfile을 사용하여 이미지를 빌드합니다.
    ports: # 호스트와 컨테이너 포트 연결
      - "3000:3000" # 호스트의 3000번 포트를 컨테이너의 3000번 포트에 연결
    environment: # 환경 변수 설정
      NODE_ENV: development
      DATABASE_URL: postgres://user:password@db:5432/mydb # DB 연결 정보 (서비스 이름: db)
      REDIS_URL: redis://redis:6379 # Redis 연결 정보 (서비스 이름: redis)
    depends_on: # 이 서비스가 시작되기 전에 먼저 실행되어야 할 서비스
      - db
      - redis
    volumes: # 볼륨 마운트 (코드 변경 시 컨테이너 재빌드 없이 반영)
      - .:/app # 현재 디렉토리를 컨테이너의 /app 디렉토리에 마운트
    working_dir: /app # 컨테이너 내부에서 작업할 디렉토리 지정
    command: npm start # 컨테이너 시작 시 실행할 명령어

  db: # PostgreSQL 데이터베이스 서비스
    image: postgres:13 # Docker Hub에서 postgres:13 이미지를 사용
    environment: # 환경 변수 (DB 사용자, 비밀번호, DB 이름 설정)
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes: # 데이터 영속성을 위한 볼륨 마운트
      - pgdata:/var/lib/postgresql/data # pgdata라는 이름의 볼륨을 사용하여 DB 데이터 보존

  redis: # Redis 캐시 서비스
    image: redis:6 # Docker Hub에서 redis:6 이미지를 사용
    ports:
      - "6379:6379" # 호스트의 6379번 포트를 컨테이너의 6379번 포트에 연결 (선택 사항)
    command: redis-server --appendonly yes # Redis 시작 명령어 (데이터 영속성 활성화)
    volumes:
      - redisdata:/data # redisdata 볼륨을 사용하여 Redis 데이터 보존

volumes: # 위에서 정의한 볼륨들을 명시적으로 선언합니다.
  pgdata:
  redisdata:

docker-compose.yml 파일을 보면,

  • web 서비스는 Node.js 애플리케이션으로, build: .을 통해 현재 디렉토리의 Dockerfile을 빌드합니다. ports로 3000번 포트를 연결하고, depends_on으로 dbredis 서비스가 먼저 시작되도록 합니다. 💡
  • db 서비스는 postgres:13 이미지를 사용하며, environment로 DB 계정 정보를 설정하고, volumes를 통해 데이터를 영구적으로 저장합니다. 💾
  • redis 서비스는 redis:6 이미지를 사용하며, 마찬가지로 volumes를 통해 데이터를 보존합니다. 🧠

이 파일 하나만 있으면, docker-compose up -d 명령어 한 번으로 웹 애플리케이션, 데이터베이스, Redis 캐시가 모두 정상적으로 실행될 준비가 됩니다. 정말 간편하죠? 👍


2. 왜 Docker Compose가 필요한가요? 🤔

Docker Compose는 단순한 편의 도구를 넘어, 현대 개발 환경에서 필수적인 이유가 명확합니다. 주요 필요성을 5가지로 정리해 보았습니다.

2.1. 복잡한 docker run 명령어의 반복과 탈출 🏃‍♂️💨

  • 문제점: 여러 컨테이너를 개별적으로 실행하려면 각 컨테이너마다 docker run 명령어를 입력해야 합니다. 이때 포트 매핑, 볼륨 마운트, 환경 변수 설정, 네트워크 연결 등 수많은 옵션을 일일이 적어줘야 합니다. 이는 매우 지루하고, 실수할 가능성이 높으며, 나중에 어떤 옵션을 사용했는지 기억하기 어렵습니다. 🤯

    # 수동으로 컨테이너 실행하는 예시 (매우 번거로움!)
    docker run -d --name my-db -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=mydb -v pgdata:/var/lib/postgresql/data postgres:13
    
    docker run -d --name my-redis -p 6379:6379 redis:6 redis-server --appendonly yes
    
    docker run -d --name my-web -p 3000:3000 --link my-db:db --link my-redis:redis -e NODE_ENV=development -v $(pwd):/app -w /app node:16-alpine npm start
  • Docker Compose의 해결책: docker-compose.yml 파일에 모든 설정을 정의해두면, 단 하나의 명령어로 모든 컨테이너를 실행할 수 있습니다.
    docker-compose up -d # 모든 서비스를 백그라운드에서 실행

    이 얼마나 간편하고 멋진가요! ✨ 마치 개발 환경을 자동으로 설정해 주는 마법 버튼을 누르는 것과 같습니다.

2.2. 컨테이너 간의 손쉬운 상호 연결 🤝

  • 문제점: 여러 컨테이너가 서로 통신해야 할 때 (예: 웹 앱이 DB에 접속해야 할 때), IP 주소를 사용하거나 docker link (현재는 권장되지 않음) 같은 복잡한 방법을 사용해야 했습니다. 컨테이너가 재시작되면 IP 주소가 바뀔 수도 있어 관리하기가 매우 까다로웠습니다. 🙅‍♀️
  • Docker Compose의 해결책: Docker Compose는 기본적으로 서비스를 위한 자체 네트워크를 생성합니다. 이 네트워크 안에서는 각 서비스의 이름이 곧 호스트 이름이 됩니다. 예를 들어, web 서비스는 db 서비스에 db:5432로, redis 서비스에 redis:6379로 접근할 수 있습니다. IP 주소를 외우거나 하드코딩할 필요 없이, 서비스 이름만으로 쉽게 통신할 수 있어 코드를 훨씬 간결하고 유연하게 만들 수 있습니다. 🥳
    // Node.js 코드에서 DB 접속 예시 (환경 변수를 통해 서비스 이름 사용)
    const { Pool } = require('pg');
    const pool = new Pool({
      user: process.env.POSTGRES_USER,
      host: process.env.DATABASE_URL.split('//')[1].split(':')[0], // 'db'
      database: process.env.POSTGRES_DB,
      password: process.env.POSTGRES_PASSWORD,
      port: 5432,
    });

    위 예시처럼 DATABASE_URLpostgres://user:password@db:5432/mydb로 설정되어 있으면, host 부분은 db로 인식되어 Compose가 생성한 내부 네트워크에서 db 서비스(PostgreSQL 컨테이너)를 찾아 연결합니다.

2.3. 일관된 개발 환경 구축 🌍

  • 문제점: “제 컴퓨터에서는 잘 작동하는데요!” 이 말은 개발자에게 뼈아픈 문제입니다. 팀원마다 운영체제가 다르고, 설치된 라이브러리 버전이 다르고, 심지어 Node.js나 Python 같은 런타임의 버전까지 다르면, 같은 코드라도 다르게 동작하거나 오류가 발생할 수 있습니다. 이 때문에 환경 설정에 엄청난 시간과 노력을 낭비하게 됩니다. 😭
  • Docker Compose의 해결책: Docker Compose는 모든 개발자가 동일한 컨테이너 이미지와 환경에서 작업할 수 있도록 보장합니다. docker-compose.yml 파일만 공유하면, 누가 어떤 OS를 사용하든, 어떤 도구가 설치되어 있든 상관없이 항상 동일한 격리된 개발 환경을 구축할 수 있습니다. 이는 “환경이 달라서 생기는 문제”를 근본적으로 제거해 주어, 모든 팀원이 예측 가능한 환경에서 효율적으로 작업할 수 있게 합니다. 🤝✨

2.4. 볼륨 및 네트워크 관리의 용이성 💾🌐

  • 문제점: 데이터베이스처럼 영구적으로 데이터를 저장해야 하는 컨테이너의 경우, 컨테이너가 삭제되어도 데이터가 사라지지 않도록 볼륨을 연결해야 합니다. 또한, 특정 컨테이너 그룹만 접근 가능한 전용 네트워크를 구성하고 싶을 수도 있습니다. 이러한 고급 설정들을 docker run 명령어로 매번 관리하는 것은 매우 복잡하고 오류를 유발할 수 있습니다. 😩
  • Docker Compose의 해결책: docker-compose.yml 파일 내에서 volumesnetworks 섹션을 통해 볼륨과 네트워크를 명시적으로 정의하고 각 서비스에 쉽게 할당할 수 있습니다.
    • 볼륨: pgdata:/var/lib/postgresql/data와 같이 영구 데이터를 저장할 볼륨을 선언하면, Compose가 알아서 볼륨을 생성하고 관리해 줍니다.
    • 네트워크: 기본적으로 Compose는 하나의 네트워크를 생성하지만, 필요하다면 여러 개의 사용자 정의 네트워크를 생성하고 서비스별로 연결할 수 있습니다. 이는 애플리케이션의 데이터 영속성과 네트워크 아키텍처를 훨씬 쉽고 명확하게 관리할 수 있게 해줍니다.

2.5. 새로운 팀원 온보딩 및 협업의 극대화 🚀

  • 문제점: 새로운 팀원이 프로젝트에 합류했을 때, 개발 환경을 설정하는 데만 며칠이 걸리는 경우가 허다합니다. 복잡한 설치 가이드를 읽고, 수많은 의존성을 설치하고, 버전을 맞추는 과정은 신입 개발자에게 큰 부담이자 비효율입니다. ⏳
  • Docker Compose의 해결책: 이젠 단 3단계면 충분합니다!
    1. Git Repository Clone: git clone <프로젝트_URL>
    2. Docker Desktop (또는 Docker Engine) 설치 (최초 1회)
    3. Docker Compose 실행: docker-compose up -d 놀랍게도, 이 세 단계만으로 모든 개발 환경이 완벽하게 구축됩니다! ✨ 이는 새로운 팀원의 온보딩 시간을 극적으로 단축하고, 팀 전체의 생산성을 비약적으로 향상시킵니다. 또한, 개발 환경이 코드와 함께 버전 관리되므로, 모든 팀원이 항상 최신 개발 환경을 유지하며 협업할 수 있습니다.

3. Docker Compose 기본 명령어 🧑‍💻

docker-compose.yml 파일을 작성했다면, 이제 몇 가지 간단한 명령어로 모든 것을 제어할 수 있습니다.

  • docker-compose up: docker-compose.yml 파일에 정의된 모든 서비스를 빌드하고 시작합니다. -d 옵션을 붙이면 백그라운드에서 실행됩니다.

    docker-compose up           # 포그라운드에서 모든 서비스 시작 (로그 출력)
    docker-compose up -d        # 백그라운드에서 모든 서비스 시작
    docker-compose up --build   # 서비스 시작 전 이미지를 강제로 다시 빌드
  • docker-compose down: up 명령어로 시작된 모든 서비스 컨테이너를 중지하고, 네트워크와 기본 볼륨을 삭제합니다.

    docker-compose down         # 모든 서비스 중지 및 삭제 (네트워크, 익명 볼륨 포함)
    docker-compose down --volumes # 명명된 볼륨까지 모두 삭제 (주의: 데이터 유실 가능성!)
  • docker-compose build: build 지시어가 있는 서비스의 이미지를 다시 빌드합니다. 코드 변경 후 이미지에 반영해야 할 때 유용합니다.

    docker-compose build        # 모든 서비스 이미지 빌드
    docker-compose build web    # 특정 서비스 (web) 이미지 빌드
  • docker-compose ps: 현재 실행 중인 Compose 서비스 목록을 표시합니다.

    docker-compose ps           # 실행 중인 서비스 상태 확인
  • docker-compose logs: 서비스의 로그를 실시간으로 출력합니다.

    docker-compose logs         # 모든 서비스 로그 출력
    docker-compose logs web     # 특정 서비스 (web) 로그 출력
    docker-compose logs -f web  # 특정 서비스 (web) 로그 실시간으로 따라가기
  • docker-compose exec: 실행 중인 서비스 컨테이너 내에서 명령어를 실행합니다.

    docker-compose exec web bash       # web 서비스 컨테이너에 bash 쉘로 접속
    docker-compose exec db psql -U user mydb # db 서비스 컨테이너에서 psql 명령어 실행
  • docker-compose stop: 실행 중인 서비스를 중지하지만, 컨테이너는 삭제하지 않습니다.

    docker-compose stop         # 모든 서비스 중지
    docker-compose stop web     # 특정 서비스 (web) 중지
  • docker-compose start: 중지된 서비스를 다시 시작합니다.

    docker-compose start        # 모든 중지된 서비스 시작
    docker-compose start web    # 특정 중지된 서비스 (web) 시작

4. Docker Compose 활용 팁! 💡

더욱 효율적인 개발을 위한 Docker Compose 활용 팁을 소개합니다.

4.1. .env 파일로 환경 변수 관리 🤫

docker-compose.yml 파일 내에 민감한 정보(비밀번호, API 키 등)를 직접 넣는 것은 보안상 좋지 않습니다. 또한, 개발 환경과 배포 환경에서 다른 값을 사용해야 하는 경우도 많죠. 이럴 때 .env 파일을 활용합니다.

.env 파일 생성:

# .env 파일
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
POSTGRES_DB=mydb
APP_PORT=3000

docker-compose.yml에서 .env 변수 사용:

version: '3.8'
services:
  web:
    build: .
    ports:
      - "${APP_PORT}:${APP_PORT}" # .env 파일의 APP_PORT 변수 사용
    environment:
      POSTGRES_USER: ${POSTGRES_USER} # .env 파일의 POSTGRES_USER 변수 사용
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      NODE_ENV: development
    depends_on:
      - db
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

docker-compose up 명령어를 실행하면 Docker Compose가 자동으로 .env 파일을 읽어들여 변수를 대체해 줍니다. 민감 정보는 .gitignore.env 파일을 추가하여 버전 관리에 포함되지 않도록 하는 것이 중요합니다! 🔒

4.2. 여러 개의 Compose 파일 사용 (-f) 📄📄

개발 환경, 테스트 환경, 배포 환경 등 각기 다른 설정을 사용해야 할 때 유용합니다. 예를 들어, 개발 환경에서는 로컬 볼륨 마운트를 사용하고, 배포 환경에서는 더 강력한 이미지와 네트워크 설정을 사용할 수 있습니다.

  • docker-compose.yml (기본 설정)
  • docker-compose.dev.yml (개발 환경 추가 설정)
  • docker-compose.prod.yml (배포 환경 추가 설정)
# 기본 파일 + 개발 환경 설정으로 실행
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# 기본 파일 + 배포 환경 설정으로 실행
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

-f 옵션을 여러 번 사용하여 여러 파일을 병합할 수 있으며, 뒤에 오는 파일의 설정이 앞의 설정을 덮어씁니다.

4.3. 서비스 헬스 체크 (Health Check) ❤️‍🩹

컨테이너가 단순히 “실행 중”인 것과 “정상적으로 작동 중”인 것은 다릅니다. 예를 들어, DB 컨테이너는 실행 중이지만 아직 접속 요청을 받을 준비가 안 되었을 수 있습니다. healthcheck를 사용하여 서비스가 실제로 준비될 때까지 기다리게 할 수 있습니다.

version: '3.8'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy # db 서비스가 healthy 상태가 될 때까지 기다림
  db:
    image: postgres:13
    environment:
      # ...
    healthcheck: # 헬스 체크 설정
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"] # DB 연결 테스트 명령어
      interval: 5s # 5초마다 체크
      timeout: 5s # 5초 안에 응답 없으면 실패
      retries: 5 # 5번 실패하면 unhealthy
      start_period: 30s # 컨테이너 시작 후 30초 동안은 헬스 체크 실패해도 오류로 간주하지 않음

이렇게 설정하면 web 서비스가 시작하기 전에 db 서비스가 완전히 준비될 때까지 기다려주므로, 애플리케이션 시작 시 발생할 수 있는 연결 오류를 방지할 수 있습니다.


맺음말 ✨

지금까지 Docker Compose가 무엇인지, 왜 현대 개발에서 필수적인 도구인지, 그리고 기본적인 사용법과 유용한 팁까지 자세히 알아보았습니다. Docker Compose는 여러 컨테이너로 이루어진 복잡한 애플리케이션의 개발 및 배포 과정을 놀라울 정도로 단순화시켜 줍니다.

개발 환경 설정에 드는 시간을 줄이고 싶으신가요? 팀원들과의 협업을 더욱 매끄럽게 만들고 싶으신가요? “내 컴퓨터에서는 되는데…”라는 말을 더 이상 듣고 싶지 않으신가요? 그렇다면 지금 바로 Docker Compose를 여러분의 개발 워크플로우에 도입해 보세요!

단순히 편리함을 넘어, 안정적이고 일관된 개발 환경을 제공하여 여러분의 개발 생산성을 한 단계 더 끌어올려 줄 것입니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 행복한 개발 되세요! 😊

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다