[IT-방법] docker compose yml 설정 예제와 기본 문법 가이드 – 초보 개발자를 위한 옵션별 설정 및 실무 템플릿

docker-compose.yml 기본 문법를 설명하는 docker-compose.yml 예제 모음 대표 이미지

도커 컴포즈 설정 때문에 밤샘하는 분들을 위하여

분명히 명령어를 똑같이 입력했는데 Exit Code 1이라는 메시지와 함께 컨테이너가 멈춰버린 적이 있나요? 분명히 예제를 보고 그대로 따라 했는데, 들여쓰기 한 칸 차이로 전체 서비스가 먹통이 되는 경험은 개발자라면 누구나 한 번쯤 겪게 되는 고통이에요. 도커를 처음 접하면 개별 컨테이너를 하나씩 띄우는 것도 벅찬데, 여러 개의 서비스를 한꺼번에 관리하려고 하면 docker-compose.yml 파일의 복잡한 문법 앞에서 막막함을 느끼기 마련이에요.

단순히 명령어를 외우는 것만으로는 부족해요. 왜 이 옵션이 필요한지, 왜 이 위치에 이 값을 넣어야 하는지 근본적인 구조를 이해하지 못하면, 조금만 환경이 바뀌어도 설정 파일은 다시금 쓰레기통으로 향하게 돼요. 특히 실무에서는 데이터베이스와 웹 서버, 캐시 서버를 엮어야 하는 경우가 많아서 설정 파일의 작은 실수가 전체 인프라의 장애로 이어지기도 해요.

지금 이 글을 읽고 계신다면, 아마도 복잡한 설정 파일 사이에서 길을 잃었거나, 프로젝트를 처음부터 제대로 구축하고 싶은 분들일 거예요. 이 가이드는 단순한 문법 나열을 넘어, 실제 현업에서 바로 가져다 쓸 수 있는 수준의 docker compose yml 설정 예제를 중심으로 구성했어요. 이 글을 끝까지 따라오시면 더 이상 YAML 파일의 들여쓰기 때문에 머리를 싸매는 일은 없을 거예요.

이 글에서 여러분이 얻어갈 수 있는 정보는 다음과 같아요.

  • docker-compose.yml의 핵심 구성 요소와 계층 구조 이해
  • 실무에서 가장 많이 쓰이는 필수 옵션(Ports, Volumes, Networks 등) 완벽 정리
  • DB와 웹 서버를 연결하는 실제 멀티 컨테이너 구성 시나리오
  • 설정 오류를 즉시 찾아내는 검증 방법과 트러블슈팅 기술

작성 전 반드시 알아야 할 기본 개념과 준비물

설정 파일을 작성하기 전에 먼저 우리가 다루는 도구가 무엇인지 명확히 짚고 넘어가야 해요. 도커 컴포즈(Docker Compose)는 여러 개의 컨테이너를 하나의 서비스 단위로 묶어서 관리할 수 있게 도와주는 도구예요. 우리가 작성할 YAML 형식의 파일은 이 거대한 오케스트라의 지휘자 역할을 한다고 생각하면 쉬워요.

YAML 문법은 매우 직관적이지만, 그렇기에 더 위험해요. 탭(Tab) 문자를 사용하면 안 되고 반드시 공백(Space)을 사용해야 한다는 점을 머릿속에 꼭 새겨두세요. 많은 초보 개발자가 여기서 실수를 해서 수십 번씩 파일을 수정하곤 해요. 또한, 작성하기 전에 현재 시스템에 Docker와 Docker Compose가 제대로 설치되어 있는지 확인하는 과정이 선행되어야 해요.

💡 알아두기
YAML은 ‘Yet Another Markup Language’의 약자로, 사람이 읽기 매우 편하게 설계된 데이터 직렬화 언어예요. 들여쓰기가 곧 데이터의 계층 구조를 의미하기 때문에 눈으로 보는 것보다 훨씬 엄격한 규칙을 따져야 해요.

본격적인 작성을 위해 어떤 핵심 요소들을 다루게 될지 아래 표를 통해 미리 살펴보세요. 각 요소는 컨테이너 운영의 뼈대를 이루는 아주 중요한 부분들이에요.

구성 요소 주요 역할 핵심 키워드
Services 실행할 컨테이너의 정의 (앱, DB 등) image, build, ports
Networks 컨테이너 간의 통신 통로 설정 driver, networks
Volumes 데이터 영구 보관을 위한 저장소 연결 driver, driver_opts
Environment 컨테이너 내부로 전달할 설정 값 env_file, environment

이 표를 기준으로 하나씩 깊이 있게 파고들 거예요. 처음부터 모든 것을 완벽하게 쓰려고 하기보다는, 가장 작은 단위의 서비스부터 시작해서 점진적으로 네트워크와 볼륨을 추가해 나가는 방식이 훨씬 안전하고 효율적이에요.

단계별 실무 docker compose yml 작성 가이드

이제 이론은 접어두고 실제로 손을 움직여볼 시간이에요. 가장 단순한 형태부터 시작해서, 실제 서비스 운영이 가능한 수준의 복잡한 구성까지 단계별로 완성해 나갈 거예요. 각 단계를 따라오면서 직접 코드를 타이핑해보는 것이 가장 빨리 배우는 지름길이에요.

STEP 1. 가장 단순한 단일 서비스 구성하기

모든 것의 시작은 아주 작은 컨테이너 하나를 띄우는 것이에요. 예를 들어, 웹 서버인 Nginx를 띄운다고 가정해 볼게요. 가장 기본적인 구조는 다음과 같아요.

version: '3.8'
services:
  web-server:
    image: nginx:latest
    ports:
      - "8080:80"

여기서 version은 컴포즈 파일의 형식을 결정해요. 최근에는 버전을 명시하지 않아도 최신 표준을 따르지만, 명시해두는 것이 호환성 측면에서 안전해요. services 아래에 우리가 만들 서비스 이름인 web-server를 정의했어요. image는 도커 허브에서 가져올 이미지를 뜻하고, ports호스트_포트:컨테이너_포트 형식을 가져요. 즉, 내 컴퓨터의 8080번 포트로 접속하면 컨테이너 내부의 80번 포트로 연결된다는 의미예요.

STEP 2. 환경 변수로 설정값 관리하기

실무에서는 데이터베이스 비밀번호나 API 키를 파일에 직접 적지 않아요. 보안을 위해 환경 변수를 사용해야 해요. 두 가지 방법이 있어요. environment 키를 직접 사용하는 방법과, 별도의 .env 파일을 사용하는 방법이에요.

services:
  app:
    image: my-app:1.0
    environment:
      - DB_HOST=db-server
      - DB_PASSWORD=${MY_SECRET_PASSWORD}
    env_file:
      - .env

위 예시처럼 ${VARIABLE} 문법을 사용하면, 같은 폴더에 있는 .env 파일의 값을 동적으로 불러올 수 있어요. 이렇게 하면 소스 코드를 공유할 때 민감한 정보를 제외하고 공유할 수 있어 매우 안전해요.

STEP 3. 데이터 영구 보존을 위한 볼륨 설정

컨테이너는 기본적으로 휘발성이에요. 컨테이너를 삭제하면 그 안에 저장했던 데이터도 모두 사라져요. 이를 방지하기 위해 Volumes 설정이 필수적이에요. 크게 두 가지 유형이 있어요. 내 컴퓨터의 특정 폴더를 직접 연결하는 Bind Mount와, 도커가 관리하는 가상 저장소를 사용하는 Named Volume예요.

services:
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql
      - ./my-config:/etc/mysql/conf.d

volumes:
  db_data:

첫 번째 줄은 db_data라는 이름의 도커 관리 볼륨을 사용하여 데이터를 보호해요. 두 번째 줄은 현재 디렉토리의 my-config 폴더를 컨테이너와 직접 연결하여 설정을 동기화해요. 데이터베이스처럼 데이터가 절대 사라지면 안 되는 서비스에는 반드시 Named Volume을 사용하는 것을 추천해요.

STEP 4. 서비스 간 의존성 및 네트워크 구성

여러 컨테이너가 함께 동작할 때, 웹 서버가 데이터베이스보다 먼저 켜지면 오류가 발생할 수 있어요. 이때 사용하는 것이 depends_on이에요. 또한, 컨테이너들끼리 서로를 찾을 수 있도록 networks를 정의해줘야 해요.

services:
  web:
    image: nginx
    depends_on:
      - api
    networks:
      - backend-net

  api:
    image: my-api:latest
    networks:
      - backend-net

networks:
  backend-net:
    driver: bridge

이렇게 설정하면 web 컨테이너는 api 컨테이너가 실행된 후에 실행돼요. 또한, 두 컨테이너는 backend-net이라는 가상의 네트워크 안에서 서로의 서비스 이름(예: api)만으로 통신할 수 있게 돼요. IP 주소를 일일이 알 필요가 없어 매우 편리해요.

STEP 5. 실무형 통합 시나리오 (Web + API + DB)

지금까지 배운 내용을 모두 합쳐서, 실제 프로젝트에서 바로 쓸 수 있는 완성형 템플릿을 만들어 볼게요. 이 예제는 웹 프론트엔드, 백엔드 API, 그리고 PostgreSQL 데이터베이스가 유기적으로 연결된 구조예요.

version: '3.8'

services:
  frontend:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./frontend/dist:/usr/share/nginx/html
    depends_on:
      - backend
    networks:
      - app-network

  backend:
    build: ./backend
    environment:
      - DB_URL=postgres://user:password@database:5432/mydb
    depends_on:
      - database
    networks:
      - app-network

  database:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  pg_data:

이 파일 하나면 전체 스택을 한 번에 관리할 수 있어요. build 옵션을 사용하여 로컬에 있는 소스 코드로 직접 이미지를 생성하도록 설정했고, 데이터베이스는 안전하게 pg_data 볼륨에 저장되도록 설계했어요. 모든 서비스는 app-network를 통해 서로를 인식해요. 이 템플릿을 복사해서 여러분의 프로젝트 구조에 맞게 값만 바꿔 사용해보세요.

💡 알아두기
위 예제에서 build: ./backend는 현재 경로의 backend 폴더 안에 있는 Dockerfile을 사용하여 이미지를 만들라는 뜻이에요. 이미 만들어진 이미지를 쓸 때는 image를 사용하지만, 개발 중인 소스코드를 바로 반영할 때는 build가 훨씬 유용해요.

자주 하는 실수와 해결법 및 FAQ

설정 파일을 다 작성하고 docker compose up을 눌렀을 때 빨간 글씨가 뜨면 당황스럽기 마련이에요. 하지만 대부분의 문제는 정해진 패턴 안에서 발생해요. 자주 발생하는 실수 5가지를 정리해 드릴게요.

  • 들여쓰기 오류 (Indentation Error)
    왜 발생하는가: YAML은 공백(Space)으로 계층을 구분하는데, 탭(Tab)을 섞어 쓰거나 공백 개수가 맞지 않을 때 발생해요.
    ✅ 해결법: 에디터(VS Code 등)에서 탭 대신 공백을 사용하도록 설정하고, docker compose config 명령어로 문법을 먼저 검증하세요.
  • 포트 충돌 (Port Collision)
    왜 발생하는가: 이미 내 컴퓨터에서 다른 프로그램이 사용 중인 포트를 지정했을 때 발생해요 (예: 80번 포트).
    ✅ 해결법: ports: ["8081:80"] 처럼 호스트 쪽 포트 번호를 다른 번호로 변경하세요.
  • 볼륨 권한 문제 (Permission Denied)
    왜 발생하는가: 호스트의 폴더를 마운트했는데, 컨테이너 내부의 사용자가 해당 폴더에 쓸 권한이 없을 때 발생해요.
    ✅ 해결법: 호스트 폴더의 권한을 변경하거나, user: "" 설정을 통해 컨테이너 실행 권한을 조정하세요.
  • 서비스 이름 오타 (Network Resolution Failure)
    왜 발생하는가: 환경 변수에서 DB 호스트를 localhost로 적었는데, 컨테이너 간 통신 시에는 서비스 이름을 써야 하기 때문이에요.
    ✅ 해결법: 컨테이너 간에는 db-server처럼 services에 정의한 이름을 사용하세요.
  • 이미지 태그 누락
    왜 발생하는가: 특정 버전을 명시하지 않고 latest만 사용하다가, 업데이트된 이미지와 기존 설정이 충돌할 때 발생해요.
    ✅ 해결법: mysql:8.0처럼 가능한 한 구체적인 버전을 명시하세요.
⚠️ 주의
환경 변수에 비밀번호를 직접 적는 행위는 매우 위험해요. 반드시 .env 파일을 활용하거나 비밀 관리 시스템을 사용하세요.

자주 묻는 질문

Q. docker-compose.yml 파일을 수정했는데 반영이 안 돼요. 어떻게 하나요?

A. 수정 사항을 반영하려면 컨테이너를 완전히 멈추고 다시 올려야 해요. docker compose down으로 기존 자원을 제거한 뒤, docker compose up -d를 실행하는 것이 가장 깔끔해요.

Q. 현재 작성한 파일의 문법이 맞는지 어떻게 미리 알 수 있나요?

A. docker compose config 명령어를 사용해 보세요. 이 명령은 작성된 파일을 해석해서 최종적인 구조를 보여주며, 문법 오류가 있다면 즉시 알려줘요.

Q. 컨테이너 로그는 어떻게 확인하나요?

A. docker compose logs -f 명령어를 사용하면 실행 중인 모든 서비스의 로그를 실시간으로 확인할 수 있어요. 특정 서비스만 보고 싶다면 docker compose logs -f [서비스이름]이라고 입력하면 돼요.

Q. 백그라운드에서 실행하려면 어떻게 하나요?

A. docker compose up -d 명령어를 사용하세요. -d 옵션은 ‘Detached mode’의 약자로, 터미널을 점유하지 않고 백그라운드에서 서비스를 실행시켜 줘요.

Q. 볼륨에 저장된 데이터는 어떻게 삭제하나요?

A. docker compose down -v 명령어를 사용하면 컨테이너와 함께 정의된 볼륨까지 모두 삭제할 수 있어요. 다만, 데이터가 영구히 사라지니 주의가 필요해요!

성공적인 컨테이너 운영을 위한 마무리

지금까지 docker compose yml 설정 예제를 통해 기초부터 실무 응용까지 살펴보았어요. 처음에는 복잡해 보일 수 있지만, 결국 핵심은 서비스를 정의하고, 연결하고, 저장하는 것이라는 세 가지 원칙으로 요약할 수 있어요. 이 원칙만 기억한다면 어떤 복잡한 아키텍처라도 두렵지 않을 거예요.

✅ 핵심 요약

  • YAML 작성 시 탭(Tab) 대신 반드시 공백(Space)을 사용하세요.
  • 민감한 정보는 .env 파일을 통해 환경 변수로 관리하세요.
  • 데이터 보존을 위해 데이터베이스에는 Named Volume을 적용하세요.
  • 서비스 간 통신은 IP가 아닌 서비스 이름을 사용하세요.
  • 설정 변경 후에는 docker compose config로 문법을 검증하세요.

자, 이제 이론은 충분해요. 바로 실습으로 넘어가 볼까요? 오늘 바로 여러분의 프로젝트 폴더에 docker-compose.yml 파일을 하나 만들고, 위에서 배운 간단한 Nginx 예제부터 실행해 보세요.

오늘 할 일: docker compose up 명령어로 첫 번째 컨테이너 띄워보기
이번 주 할 일: 기존 프로젝트에 데이터베이스 볼륨 설정 추가하기
실행 직전 할 일: 환경 변수 관리를 위한 .env 파일 구조 설계하기

더 구체적인 개념이 궁금하다면 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드를 함께 읽어보시는 것을 추천해요. 예제 파일을 프로젝트 구조에 맞게 조금씩 변형해 사용하다 보면, 어느새 여러분도 도커 마스터가 되어 있을 거예요!

댓글 남기기