
터미널의 붉은 에러 메시지, YAML 문법 때문에 당황하셨나요?
어제까지만 해도 잘 돌아가던 컨테이너가 갑자기 멈춰버렸어요. 분명히 코드는 바꾼 게 없는데, 터미널에는 mapping values are not allowed here라는 정체 모를 메시지만 가득하죠. 원인을 찾아보니 범인은 바로 docker-compose.yml 파일의 들여쓰기 한 칸이었어요.
도커 컴포즈를 처음 접하는 분들이라면 누구나 이런 경험을 해요. 명령어를 하나씩 입력하는 대신 파일 하나로 여러 컨테이너를 관리하는 방식은 정말 편리하지만, 그만큼 문법 하나하나가 아주 예민하거든요. 탭(Tab)을 썼는지, 스페이스(Space)를 몇 번 눌렀는지에 따라 전체 서비스가 통째로 무너질 수 있어요.
특히 서버 운영을 시작하거나 팀 프로젝트에 참여하게 되면, 복잡한 네트워크 설정과 볼륨 연결 때문에 머리가 아파오기 시작해요. 왜 내 컨테이너는 DB를 못 찾는지, 왜 내가 수정한 파일이 컨테이너 안에 반영되지 않는지 답답할 때가 많죠. 이런 문제는 단순한 실수를 넘어 서비스 전체의 장애로 이어질 수 있어요.
그래서 오늘은 docker compose yml 질문 정리를 주제로, 입문자들이 가장 많이 겪는 시행착오를 모아봤어요. 이 글을 다 읽고 나면 더 이상 문법 에러 때문에 밤을 지새우지 않아도 돼요.
- 가장 자주 발생하는 YAML 문법 오류와 해결법
- 서비스, 네트워크, 볼륨 설정의 핵심 포인트
- 실무에서 바로 쓰는 환경 변수 활용 기술
- 컨테이너 간 의존성 관리와 실행 순서 제어
작성 전 반드시 체크해야 할 YAML 기초와 준비물
docker-compose.yml 파일을 작성하기 전에 먼저 머릿속에 넣어두어야 할 기본 원칙이 있어요. 바로 YAML(YAML Ain’t Markup Language) 형식을 이해하는 거예요. YAML은 데이터를 구조화하는 방식인데, 아주 강력하지만 동시에 아주 까다로운 녀석이에요.
가장 먼저 기억해야 할 점은 탭(Tab) 키를 절대 사용하지 말아야 한다는 거예요. YAML 표준에서는 들여쓰기를 할 때 반드시 공백(Space)을 사용해야 해요. 많은 에디터가 탭을 공백으로 변환해주지만, 가끔 설정이 꼬이면 보이지 않는 오류의 주범이 된답니다. 또한, 콜론(:) 뒤에는 반드시 한 칸의 공백이 있어야 한다는 점도 잊지 마세요.
준비물은 간단해요. 도커(Docker)와 도커 컴포즈(Docker Compose)가 설치된 환경만 있으면 돼요. 최근에는 도커 엔진에 컴포즈 기능이 통합되어 docker compose 명령어를 바로 사용할 수 있어요. 하지만 구형 시스템에서는 docker-compose처럼 하이픈을 붙여야 할 수도 있으니 본인의 환경을 먼저 확인해보세요.
작업을 시작하기 전, 내가 어떤 방식으로 컨테이너를 운영할지 기준을 세우는 것이 중요해요. 아래 표를 통해 도커 명령어를 직접 쓰는 방식과 컴포즈를 쓰는 방식의 차이를 확인해보세요.
| 비교 항목 | 도커 CLI (docker run) | 도커 컴포즈 (docker compose) |
|---|---|---|
| 설정 저장 | 명령어를 매번 입력해야 함 | YAML 파일에 저장하여 재사용 가능 |
| 다중 컨테이너 관리 | 각 컨테이너를 개별적으로 관리 | 파일 하나로 전체 스택 관리 |
| 네트워크 구성 | 수동으로 네트워크를 생성/연결 | 자동으로 서비스 간 네트워크 형성 |
| 운영 편의성 | 복잡한 설정 시 명령어가 매우 길어짐 | 구조화된 코드로 가독성이 높음 |
결국 컴포즈를 쓰는 이유는 인프라를 코드로 관리(IaC)하기 위해서예요. 파일 하나만 있으면 서버를 옮겨도 똑같은 환경을 1초 만에 복제할 수 있으니까요. 이제 기본 준비가 끝났다면, 실제 문법을 하나씩 파헤쳐 볼까요?
실수를 줄이는 단계별 docker-compose.yml 작성법
이제 본격적으로 파일을 작성해봐요. 단순히 코드를 따라 치는 게 아니라, 각 설정이 어떤 역할을 하는지 이해하는 게 핵심이에요. 실제 프로젝트를 구성한다고 생각하며 단계별로 따라와 주세요.
STEP 1. 서비스 정의와 이미지 설정하기
모든 파일의 시작은 어떤 컨테이너들을 띄울지 결정하는 services 섹션이에요. 여기서 각 서비스는 하나의 컨테이너 역할을 하죠. 가장 기본적인 설정은 image예요. 사용할 이미지를 지정하는 거죠. 만약 직접 만든 이미지를 사용하고 싶다면 build 옵션을 써야 해요.
image: nginx:latest라고 쓰면 최신 버전의 엔진엑스를 가져오지만, 실무에서는 버전을 명확히 지정하는 것이 훨씬 안전해요. 버전이 예고 없이 업데이트되면 기존 설정이 깨질 수 있거든요. container_name을 지정하면 나중에 컨테이너를 찾을 때 훨씬 편하지만, 스케일 아웃(Scale-out)을 염두에 둔다면 이름을 고정하지 않는 게 나을 수도 있어요.
STEP 2. 포트 포워딩과 볼륨 연결 마스터하기
컨테이너는 마치 격리된 방과 같아요. 외부에서 접속하려면 문(Port)을 열어줘야 하고, 데이터를 보관하려면 창고(Volume)를 연결해야 하죠. ports 설정은 호스트포트:컨테이너포트 순서예요. 예를 들어 8080:80이라고 적으면, 내 컴퓨터의 8080번 포트로 접속했을 때 컨테이너 안의 80번 포트로 연결돼요.
볼륨 설정은 두 가지 방식이 있어요. 하나는 bind mount 방식이고, 다른 하나는 named volume 방식이에요. bind mount는 내 컴퓨터의 특정 폴더(예: ./html)를 컨테이너와 직접 연결하는 거예요. 코드를 수정하면 컨테이너에 즉시 반영되니까 개발할 때 아주 유용하죠. 반면, named volume은 도커가 관리하는 별도의 저장 공간을 만드는 거예요. 데이터의 안전성이 훨씬 높아서 데이터베이스(DB) 데이터를 보관할 때 주로 사용해요.
STEP 3. 네트워크로 컨테이너끼리 대화하게 만들기
여러 개의 서비스를 띄웠다면, 그들이 서로 통신할 수 있어야 해요. 예를 들어 웹 서버가 DB 서버에 데이터를 저장해야 하니까요. 도커 컴포즈는 기본적으로 모든 서비스가 속한 동일한 네트워크를 자동으로 만들어줘요. 그래서 서비스 이름만 알면 서로를 찾을 수 있죠. web 서비스에서 db라는 이름으로 바로 접속이 가능한 식이에요.
하지만 보안이 중요한 프로젝트라면 networks 설정을 통해 네트워크를 분리해야 해요. 외부로 노출될 웹 서버용 네트워크와, 내부에서만 통신할 DB용 네트워크를 따로 나누는 거죠. 이렇게 하면 해커가 웹 서버를 뚫더라도 DB로 바로 접근하는 것을 어렵게 만들 수 있어요.
STEP 4. 환경 변수로 보안과 유연성 잡기
비밀번호나 API 키 같은 민감한 정보를 YAML 파일에 그대로 적는 건 아주 위험한 행동이에요. 누구나 코드를 볼 수 있기 때문이죠. 이때 사용하는 것이 environment와 env_file이에요. environment는 파일 안에 직접 변수를 적는 방식이고, env_file은 별도의 .env 파일을 만들어 관리하는 방식이에요.
.env 파일에는 반드시 DB 비밀번호, API 토큰 등 민감한 정보만 담으세요. 그리고 이 파일은 절대로 Git 같은 버전 관리 시스템에 올리면 안 됩니다! 반드시
.gitignore에 추가해서 보호하세요.STEP 5. 서비스 실행 순서 제어하기
DB가 아직 준비되지 않았는데 웹 서버가 먼저 실행되면 어떻게 될까요? 웹 서버는 DB를 찾지 못해 에러를 내며 종료될 거예요. 이를 방지하기 위해 depends_on 설정을 사용해요. depends_on: [db]라고 적으면, 도커는 DB 컨테이너가 생성된 후에 웹 서버를 띄워요.
하지만 여기서 주의할 점! depends_on은 컨테이너가 ‘실행’되는 것만 보장할 뿐, 서비스가 ‘완전히 준비(Ready)’된 상태인지는 확인하지 못해요. 그래서 실제 운영 환경에서는 healthcheck 설정을 함께 써서 DB가 실제로 쿼리를 받을 준비가 되었는지 확인하는 과정이 반드시 필요해요.
웹 애플리케이션 + MySQL 환경 구축 예시:
1.
db 서비스에 named volume을 설정해 데이터 보존2.
web 서비스에 depends_on을 걸어 DB 실행 대기3.
.env 파일을 통해 DB 비밀번호 관리4.
ports를 통해 외부에서 80번 포트로 접속 가능하게 설정자주 하는 실수와 해결법
문법은 다 맞춘 것 같은데 왜 안 될까요? 현장에서 가장 많이 발생하는 5가지 사례를 정리했어요.
- ❌ 인덴트(들여쓰기) 오류 → YAML은 공백의 개수에 매우 민감해요. 에디터의
YAML Validator확장을 설치해서 실시간으로 확인하는 습관을 들이세요. - ❌ 포트 충돌 →
Address already in use에러가 뜬다면 이미 다른 프로그램이 해당 포트를 쓰고 있는 거예요. 호스트 포트 번호를 바꿔보세요. - ❌ 볼륨 경로 오기입 → 상대 경로를 쓸 때
./를 빼먹지 않았는지 확인하세요. 호스트 경로와 컨테이너 내부 경로가 바뀌어 있는 경우도 흔해요. - ❌ 네트워크 이름 불일치 → 서비스 간 통신이 안 된다면, 두 서비스가 동일한 네트워크에 속해 있는지, 혹은 서비스 이름을 정확히 호출했는지 체크하세요.
- ❌ 이미지 태그 누락 →
latest태그를 쓰면 편리하지만, 어느 날 갑자기 설정이 바뀌어 작동하지 않을 수 있어요. 특정 버전을 명시하는 것을 권장해요.
자주 묻는 질문
Q. docker-compose.yml 파일 이름은 꼭 이렇게 지어야 하나요?
꼭 그럴 필요는 없지만, 관례적으로 그렇게 사용해요. 만약 다른 이름을 쓰고 싶다면 docker compose -f my-config.yml up처럼 -f 옵션을 붙여서 파일명을 직접 알려줘야 해요.
Q. 파일 최상단에 version을 꼭 써야 하나요?
최신 도커 컴포즈 버전에서는 version 필드가 더 이상 필수가 아니에요. 도커가 알아서 최신 사양으로 해석하거든요. 하지만 이전 방식과의 호환성을 위해 명시해두는 경우도 많아요.
Q. depends_on을 썼는데도 DB 접속 에러가 나요. 왜 그런가요?
앞서 말씀드렸듯이 depends_on은 컨테이너의 ‘기동’만 확인하기 때문이에요. DB 엔진이 완전히 부팅되어 쿼리를 받을 준비가 될 때까지의 시간을 고려해야 해요. 애플리케이션 코드 내에 재시도(Retry) 로직을 넣거나, 도커의 healthcheck 기능을 활용해보세요.
Q. 컨테이너를 삭제하면 볼륨에 담긴 데이터도 사라지나요?
사용 방식에 따라 달라요. docker compose down은 컨테이너와 네트워크는 지우지만, named volume에 담긴 데이터는 유지해요. 하지만 -v 옵션을 붙여 docker compose down -v라고 실행하면 볼륨까지 모두 삭제되니 주의하세요!
Q. .env 파일은 어디에 두어야 하나요?
보통 docker-compose.yml 파일과 같은 디렉토리에 두는 것이 기본이에요. 그러면 컴포즈가 실행될 때 자동으로 해당 파일을 읽어서 변수를 적용해준답니다.
완벽한 컨테이너 운영을 위한 마지막 점검
도커 컴포즈는 강력한 도구이지만, 기초가 흔들리면 오히려 운영을 어렵게 만들 수 있어요. 오늘 배운 내용을 바탕으로 여러분의 설정 파일을 다시 한번 검토해보세요. 작은 디테일이 안정적인 서비스를 만듭니다.
- 들여쓰기는 반드시 공백(Space)으로, 탭(Tab)은 금지예요.
- 포트 설정은
호스트:컨테이너순서라는 점을 명심하세요. - 민감한 정보는 반드시
.env파일을 통해 관리하세요. - 데이터 보존이 중요하다면
named volume을 활용하세요. - 서비스 간 실행 순서는
depends_on과healthcheck로 정교하게 조절하세요.
이제 여러분은 docker-compose.yml 파일을 작성할 준비가 되었어요. 처음에는 에러 메시지가 무섭게 느껴지겠지만, 하나씩 해결하다 보면 어느새 능숙하게 인프라를 코드로 다루는 자신을 발견하게 될 거예요.
오늘 배운 내용 중에서 아직 이해가 가지 않거나, 실무에서 마주친 새로운 에러가 있다면 주저 말고 댓글로 남겨 주세요. 여러분의 질문을 정리해서 다음 가이드에 꼭 반영하도록 할게요!
더 깊이 있는 공부를 원하신다면, 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드 글을 함께 읽어보시는 것을 강력히 추천드려요.