Docker Compose를 활용한 다중 컨테이너 개발 환경 구축 및 관리 방법을 심층적으로 다룹니다. 설정부터 실전 적용까지, 효율적인 개발 워크플로우를 위한 핵심 가이드를 제시합니다.
복잡한 웹 서비스는 프론트엔드, 백엔드 API, 데이터베이스, 캐시 서버 등 여러 컴포넌트로 구성되는 경우가 많습니다. 이러한 다중 서비스 환경을 개별적으로 관리하고 개발하는 것은 비효율적이며, 때로는 버전 충돌이나 환경 불일치 문제를 야기할 수 있습니다. 개발팀 내에서 일관된 개발 환경을 유지하는 것 또한 만만치 않은 도전이죠. 이러한 문제에 직면했을 때, 어떻게 하면 효율적으로 다중 서비스 개발 환경을 구축하고 관리할 수 있을까요? 바로 Docker Compose가 그 해답을 제시합니다.
📑 목차
- 왜 다중 컨테이너 개발 환경이 필요한가?
- Docker Compose란 무엇인가?
- Docker Compose의 핵심 기능
- Docker CLI와의 비교
- Docker Compose 설치 및 기본 설정
- 설치 확인
- Docker Compose 파일(docker-compose.yml) 심층 분석
- docker-compose.yml 기본 구조
- 서비스 정의: 핵심 파라미터
- 실전 예제로 배우는 다중 컨테이너 환경 구축
- 웹 애플리케이션 + 데이터베이스 환경 구축 예시
- Docker Compose를 활용한 개발 워크플로우 최적화
- 1. 일관된 개발 환경 제공
- 2. 빠른 서비스 시작 및 종료
- 3. 코드 변경 사항의 실시간 반영 (핫 리로딩)
- 4. 특정 서비스만 관리
- 5. 스케일링 및 자원 관리
- 6. 다양한 명령어를 통한 관리 효율 증대
- 결론: Docker Compose, 개발 생산성을 높이는 핵심 도구
Image by 2427999 on Pixabay
왜 다중 컨테이너 개발 환경이 필요한가?
최신 소프트웨어 아키텍처는 대부분 마이크로서비스 또는 분산 시스템의 형태로 진화하고 있습니다. 이는 각 서비스가 독립적으로 개발, 배포, 확장될 수 있도록 하여 유연성과 확장성을 높이는 장점이 있습니다. 그러나 이러한 아키텍처는 개발 단계에서 다음과 같은 복잡성을 수반합니다.
- 환경 일관성 문제: 각 개발자의 로컬 환경이 다르면, "내 컴퓨터에서는 되는데..."라는 문제가 발생하기 쉽습니다.
- 의존성 관리의 어려움: 여러 서비스가 상호 의존하는 경우, 각 서비스의 버전 및 설정 관리가 복잡해집니다.
- 설정 시간 소요: 새로운 개발자가 프로젝트에 합류할 때, 모든 서비스의 개발 환경을 수동으로 설정하는 데 많은 시간이 소요됩니다.
- 리소스 격리: 각 서비스가 사용하는 라이브러리나 런타임 버전이 다를 경우 충돌이 발생할 수 있습니다.
Docker Compose는 이러한 문제들을 해결하고, 마치 하나의 애플리케이션처럼 여러 컨테이너를 한 번에 정의하고 실행할 수 있도록 돕는 강력한 도구입니다. 이를 통해 개발 환경의 일관성, 이식성, 그리고 효율성을 극대화할 수 있습니다.
Docker Compose란 무엇인가?
Docker Compose는 여러 개의 Docker 컨테이너를 정의하고 실행하기 위한 도구입니다. YAML 파일을 사용하여 애플리케이션의 서비스, 네트워크, 볼륨 등을 한 번에 구성하며, 단일 명령어로 전체 애플리케이션 스택을 관리할 수 있게 해줍니다.
Docker Compose의 핵심 기능
Docker Compose는 다음과 같은 핵심 기능을 통해 다중 컨테이너 애플리케이션 관리를 단순화합니다.
- 서비스 정의: `docker-compose.yml` 파일 내에서 애플리케이션을 구성하는 각 서비스를 정의합니다. 각 서비스는 어떤 Docker 이미지를 사용할지, 어떤 포트를 개방할지, 어떤 환경 변수를 가질지 등을 명시합니다.
- 네트워크 정의: 서비스 간의 통신을 위한 가상 네트워크를 자동으로 생성하거나 수동으로 정의할 수 있습니다. 이를 통해 서비스 이름만으로 다른 서비스에 접근할 수 있습니다.
- 볼륨 정의: 컨테이너의 데이터를 영구적으로 저장하거나 호스트 시스템과 공유하기 위한 볼륨을 설정할 수 있습니다. 개발 환경에서는 주로 소스 코드를 마운트하여 실시간으로 변경 사항을 반영하는 데 사용됩니다.
- 단일 명령어 관리: `docker-compose up` 명령 하나로 정의된 모든 서비스를 동시에 시작하고, `docker-compose down` 명령으로 한 번에 중지 및 삭제할 수 있습니다.
Docker CLI와의 비교
여러 컨테이너를 관리할 때 Docker CLI 명령어를 직접 사용하는 것과 Docker Compose를 사용하는 것에는 명확한 차이가 있습니다. 각각의 장단점을 살펴보면 Docker Compose의 필요성을 더욱 명확하게 이해할 수 있습니다.
| 특징 | Docker CLI (개별 명령어) | Docker Compose |
|---|---|---|
| 설정 방식 | 각 컨테이너를 `docker run` 명령어로 개별 설정 | `docker-compose.yml` 파일에 전체 스택을 정의 |
| 복잡성 | 다중 컨테이너 시 명령어가 길어지고 복잡해짐 (예: 포트 매핑, 네트워크 연결 등) | YAML 파일로 추상화되어 가독성 및 관리 용이성 우수 |
| 관리 용이성 | 각 컨테이너를 개별적으로 시작/중지/삭제해야 함 | 단일 명령어로 전체 서비스 스택을 일괄 관리 |
| 재현성 | 설정 오류 가능성 높음, 환경 재현 어려움 | `yml` 파일 공유로 모든 환경에서 동일한 설정 재현 가능 |
| 개발 워크플로우 | 수동 설정 및 관리로 개발 속도 저하 | 빠른 환경 구축 및 변경으로 개발 생산성 향상 |
위 표에서 볼 수 있듯이, 단일 컨테이너 환경에서는 Docker CLI가 충분할 수 있지만, 여러 서비스가 상호 작용하는 복잡한 애플리케이션 환경에서는 Docker Compose가 압도적으로 효율적이고 관리하기 쉽습니다.
Docker Compose 설치 및 기본 설정
Docker Compose는 Docker Desktop을 설치하면 대부분 자동으로 포함되어 있습니다. 별도로 설치해야 하는 경우도 있습니다. 다음은 일반적인 설치 및 확인 방법입니다.
설치 확인
터미널 또는 명령 프롬프트에서 다음 명령어를 실행하여 Docker Compose가 올바르게 설치되었는지 확인할 수 있습니다.
docker compose version
또는 구버전의 경우:
docker-compose --version
정상적으로 설치되었다면, 버전 정보가 출력됩니다 (예: `Docker Compose version v2.20.2`). 만약 설치되어 있지 않다면, Docker 공식 문서를 참조하여 운영체제에 맞는 설치 방법을 따라야 합니다. Docker Desktop을 설치하는 것이 가장 간편하며, Docker Engine과 Docker Compose를 한 번에 설치할 수 있습니다.
Image by Olga_Fil on Pixabay
Docker Compose 파일(docker-compose.yml) 심층 분석
Docker Compose의 핵심은 `docker-compose.yml` 파일입니다. 이 파일은 YAML 형식으로 작성되며, 애플리케이션을 구성하는 모든 서비스의 정보를 포함합니다. 이 파일을 이해하는 것이 Docker Compose를 마스터하는 첫걸음입니다.
docker-compose.yml 기본 구조
`docker-compose.yml` 파일은 크게 세 가지 최상위 키를 가집니다: `version`, `services`, `networks`, `volumes` (네트워크와 볼륨은 필수는 아니지만 자주 사용됩니다).
version: '3.8' # Docker Compose 파일 형식 버전
services: # 애플리케이션을 구성하는 서비스 정의
web:
# 서비스 설정
db:
# 서비스 설정
networks: # 서비스 간 통신을 위한 네트워크 정의 (선택 사항)
app_network:
driver: bridge
volumes: # 데이터 지속성을 위한 볼륨 정의 (선택 사항)
db_data:
- `version`: Docker Compose 파일의 문법 버전을 지정합니다. 최신 기능들을 사용하기 위해 가장 높은 안정적인 버전을 사용하는 것이 좋습니다 (예: '3.8').
- `services`: 애플리케이션을 구성하는 개별 컨테이너 서비스를 정의하는 섹션입니다. 각 서비스는 독립적인 컨테이너로 실행됩니다.
- `networks`: 서비스들이 통신할 수 있는 네트워크를 정의합니다. 명시적으로 정의하지 않으면 Docker Compose가 기본 네트워크를 생성합니다.
- `volumes`: 컨테이너의 데이터를 저장하거나 호스트와 공유하기 위한 볼륨을 정의합니다. 데이터의 영속성을 확보하는 데 필수적입니다.
서비스 정의: 핵심 파라미터
각 서비스는 다음과 같은 주요 파라미터들을 포함하여 정의됩니다.
- `image`: 서비스에 사용할 Docker 이미지를 지정합니다 (예: `nginx:latest`, `postgres:13`). 이미지가 로컬에 없으면 자동으로 다운로드합니다.
- `build`: Dockerfile이 있는 경로를 지정하여 이미지를 직접 빌드합니다. `context`와 `dockerfile` 옵션을 사용할 수 있습니다.
- `ports`: 호스트와 컨테이너 간의 포트 매핑을 설정합니다 (예: `"80:80"`은 호스트의 80번 포트를 컨테이너의 80번 포트에 연결).
- `environment`: 컨테이너 내에서 사용할 환경 변수를 설정합니다 (예: `DATABASE_URL: postgres://user:password@db:5432/myapp`).
- `volumes`: 호스트의 경로를 컨테이너 내의 경로에 마운트하거나, 명명된 볼륨을 연결합니다 (예: `./app:/app`, `db_data:/var/lib/postgresql/data`).
- `depends_on`: 서비스 간의 의존성을 지정합니다. 예를 들어, `web` 서비스가 `db` 서비스보다 먼저 시작되어야 할 때 사용합니다. (컨테이너 시작 순서만 보장하며, 서비스가 완전히 준비되었음을 보장하지는 않습니다.)
- `networks`: 서비스가 연결될 네트워크를 지정합니다.
- `command`: 컨테이너가 시작될 때 실행할 명령어를 재정의합니다.
- `restart`: 컨테이너 종료 시 재시작 정책을 정의합니다 (예: `always`, `on-failure`).
이러한 파라미터들을 조합하여 각 서비스의 특성과 요구사항에 맞는 컨테이너 환경을 구축할 수 있습니다.
실전 예제로 배우는 다중 컨테이너 환경 구축
이론만으로는 부족하죠. 실제 예제를 통해 Docker Compose로 간단한 웹 애플리케이션과 데이터베이스 환경을 구축하는 방법을 알아보겠습니다. 여기서는 Node.js 웹 애플리케이션과 PostgreSQL 데이터베이스를 사용하는 시나리오를 가정합니다.
웹 애플리케이션 + 데이터베이스 환경 구축 예시
다음과 같은 파일 구조를 가정합니다.
.
├── docker-compose.yml
├── web/
│ ├── Dockerfile
│ ├── app.js
│ ├── package.json
└── db/
└── init.sql # (선택 사항) 데이터베이스 초기화 스크립트
1. `web/Dockerfile` 생성:
# web/Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
2. `web/package.json` 및 `web/app.js` 생성:
// web/package.json
{
"name": "my-web-app",
"version": "1.0.0",
"description": "A simple Node.js web app",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3"
}
}
// web/app.js
const express = require('express');
const { Client } = require('pg');
const app = express();
const port = 3000;
const client = new Client({
user: process.env.DB_USER || 'user',
host: process.env.DB_HOST || 'db', // Docker Compose 서비스 이름으로 접근
database: process.env.DB_NAME || 'mydb',
password: process.env.DB_PASSWORD || 'password',
port: process.env.DB_PORT || 5432,
});
client.connect()
.then(() => console.log('Connected to PostgreSQL'))
.catch(err => console.error('Connection error', err.stack));
app.get('/', (req, res) => {
res.send('Hello from Node.js Web App!');
});
app.get('/data', async (req, res) => {
try {
const result = await client.query('SELECT NOW() as current_time');
res.json({ message: 'Data from DB', time: result.rows[0].current_time });
} catch (err) {
console.error('Error executing query', err.stack);
res.status(500).send('Error retrieving data');
}
});
app.listen(port, () => {
console.log(`Web app listening at http://localhost:${port}`);
});
3. `docker-compose.yml` 생성:
# docker-compose.yml
version: '3.8'
services:
web:
build: ./web # web 디렉토리의 Dockerfile을 사용해 이미지 빌드
ports:
- "3000:3000" # 호스트의 3000번 포트를 컨테이너의 3000번 포트에 연결
environment:
DB_HOST: db # db 서비스 이름으로 PostgreSQL에 접근
DB_USER: user
DB_PASSWORD: password
DB_NAME: mydb
volumes:
- ./web:/app # 호스트의 web 디렉토리를 컨테이너의 /app에 마운트 (코드 변경 실시간 반영)
- /app/node_modules # node_modules는 호스트에 마운트하지 않음 (컨테이너 내에서 관리)
depends_on:
- db # web 서비스는 db 서비스가 시작된 후에 시작
restart: always # 컨테이너 종료 시 항상 재시작
db:
image: postgres:13 # PostgreSQL 13 이미지 사용
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
ports:
- "5432:5432" # (선택 사항) 호스트에서 DB에 직접 접근하고 싶을 때
volumes:
- db_data:/var/lib/postgresql/data # db_data 볼륨을 사용하여 데이터 영속성 확보
# - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql # (선택 사항) 초기 데이터베이스 스크립트 실행
restart: always # 컨테이너 종료 시 항상 재시작
volumes:
db_data: # db_data라는 이름의 명명된 볼륨 정의
4. 다중 컨테이너 환경 실행:
프로젝트 루트 디렉토리에서 다음 명령어를 실행합니다.
docker compose up -d
- `-d` 옵션은 컨테이너를 백그라운드에서 실행하여 터미널을 다시 사용할 수 있게 합니다.
- 이 명령어는 `web` 서비스의 Dockerfile을 빌드하고, `postgres:13` 이미지를 다운로드하며, 두 서비스를 정의된 네트워크와 볼륨에 연결하여 시작합니다.
5. 실행 확인:
컨테이너가 정상적으로 실행 중인지 확인합니다.
docker compose ps
출력 예시:
NAME COMMAND SERVICE STATUS PORTS
my-web-app-db-1 "docker-entrypoint.s…" db running 0.0.0.0:5432->5432/tcp
my-web-app-web-1 "docker-entrypoint.s…" web running 0.0.0.0:3000->3000/tcp
이제 웹 브라우저에서 `http://localhost:3000`에 접속하면 "Hello from Node.js Web App!" 메시지를 볼 수 있습니다. `http://localhost:3000/data`에 접속하면 데이터베이스에서 가져온 현재 시간을 확인할 수 있습니다.
6. 로그 확인:
모든 서비스의 로그를 실시간으로 확인하려면:
docker compose logs -f
특정 서비스의 로그만 확인하려면:
docker compose logs -f web
7. 환경 중지 및 삭제:
모든 서비스 컨테이너와 네트워크를 중지하고 삭제하려면:
docker compose down
볼륨까지 삭제하려면 (`db_data`에 저장된 데이터가 모두 사라지므로 주의):
docker compose down -v
이 예제를 통해 Docker Compose가 얼마나 쉽게 복잡한 다중 서비스 환경을 구축하고 관리할 수 있는지 확인할 수 있습니다. 특히 `volumes` 설정을 통해 호스트의 소스 코드를 컨테이너에 마운트함으로써, 코드 변경 시 컨테이너를 재빌드할 필요 없이 실시간으로 변경 사항을 반영하는 개발 생산성 향상 효과를 얻을 수 있습니다.
Image by Boskampi on Pixabay
Docker Compose를 활용한 개발 워크플로우 최적화
Docker Compose는 단순히 환경을 구축하는 것을 넘어, 개발 워크플로우 전반에 걸쳐 효율성을 높이는 데 기여합니다.
1. 일관된 개발 환경 제공
모든 개발자가 동일한 `docker-compose.yml` 파일을 공유함으로써, 운영체제나 로컬 설정에 관계없이 동일한 개발 환경을 가질 수 있습니다. 이는 "내 컴퓨터에서는 되는데..." 문제를 근본적으로 해결하고, 온보딩 시간을 크게 단축시킵니다.
2. 빠른 서비스 시작 및 종료
단 한 번의 `docker compose up -d` 명령으로 모든 서비스가 시작되고, `docker compose down` 명령으로 깔끔하게 종료됩니다. 이는 개발자가 핵심 비즈니스 로직 개발에 집중할 수 있도록 환경 설정 및 관리에 드는 오버헤드를 최소화합니다.
3. 코드 변경 사항의 실시간 반영 (핫 리로딩)
앞선 예제에서 `volumes: - ./web:/app`와 같이 호스트의 소스 코드를 컨테이너에 마운트했습니다. Node.js 애플리케이션의 경우 `nodemon`과 같은 도구를 사용하여 파일 변경을 감지하고 자동으로 서버를 재시작하도록 설정하면, 컨테이너를 재빌드하거나 재시작할 필요 없이 코드 변경 사항이 즉시 반영되어 빠른 개발 반복 주기를 가질 수 있습니다.
# web/package.json (nodemon 추가 예시)
{
"name": "my-web-app",
"version": "1.0.0",
"description": "A simple Node.js web app",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js" # 개발용 스크립트 추가
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3"
},
"devDependencies": {
"nodemon": "^3.0.1" # 개발 의존성 추가
}
}
이후 `docker-compose.yml`의 `web` 서비스 `command`를 다음과 같이 변경할 수 있습니다.
web:
build: ./web
ports:
- "3000:3000"
environment:
DB_HOST: db
DB_USER: user
DB_PASSWORD: password
DB_NAME: mydb
volumes:
- ./web:/app
- /app/node_modules # node_modules는 컨테이너 내부에 유지
depends_on:
- db
restart: always
command: npm run dev # 개발 모드 실행
이제 `docker compose up -d`로 컨테이너를 시작하고 `web/app.js` 파일을 수정하면, `nodemon`이 변경을 감지하여 자동으로 웹 서버를 재시작하고 변경 사항을 반영합니다.
4. 특정 서비스만 관리
여러 서비스 중 특정 서비스만 시작하거나 중지해야 할 때도 Docker Compose는 유용합니다.
- 특정 서비스만 시작: `docker compose up -d web`
- 특정 서비스만 중지: `docker compose stop web`
- 특정 서비스만 재시작: `docker compose restart web`
5. 스케일링 및 자원 관리
필요에 따라 특정 서비스의 인스턴스를 늘려 스케일링할 수 있습니다. 예를 들어, 웹 서비스의 부하가 높아질 것으로 예상될 때:
docker compose up --scale web=3 -d
이 명령은 `web` 서비스의 컨테이너를 3개로 늘려 실행합니다. 이는 개발 환경에서 부하 테스트를 수행하거나, 여러 버전의 서비스를 동시에 테스트할 때 유용합니다.
6. 다양한 명령어를 통한 관리 효율 증대
Docker Compose는 개발 환경 관리에 필요한 다양한 명령어를 제공하여 효율성을 높입니다.
| 명령어 | 설명 | 예시 |
|---|---|---|
| `up` | 정의된 모든 서비스를 빌드하고 시작합니다. `-d`는 백그라운드 실행. | `docker compose up -d` |
| `down` | 실행 중인 모든 서비스를 중지하고 컨테이너, 네트워크를 제거합니다. `-v`는 볼륨까지 제거. | `docker compose down -v` |
| `ps` | 현재 실행 중인 서비스의 상태를 보여줍니다. | `docker compose ps` |
| `logs` | 서비스의 로그를 출력합니다. `-f`는 실시간 스트리밍. | `docker compose logs -f web` |
| `exec` | 실행 중인 컨테이너 내에서 명령어를 실행합니다. | `docker compose exec web bash` (web 컨테이너 쉘 접속) |
| `run` | 일회성 명령어를 실행합니다 (새 컨테이너 생성). | `docker compose run web npm test` (web 컨테이너에서 테스트 실행) |
| `build` | 서비스 이미지를 강제로 다시 빌드합니다. | `docker compose build web` |
이러한 명령어들을 통해 Docker Compose는 다중 컨테이너 개발 환경을 구축하고 관리하는 데 있어 개발자에게 강력한 제어력과 편의성을 제공합니다.
결론: Docker Compose, 개발 생산성을 높이는 핵심 도구
지금까지 Docker Compose를 활용하여 다중 컨테이너 개발 환경을 구축하고 관리하는 방법에 대해 심층적으로 살펴보았습니다. 복잡한 다중 서비스 아키텍처는 현대 소프트웨어 개발의 표준이 되었고, 이러한 환경을 효율적으로 다루는 것이 개발 생산성과 팀의 협업에 직결됩니다. Docker Compose는 이 과정에서 다음과 같은 핵심적인 가치를 제공합니다.
- 일관성: 모든 개발자가 동일한 환경에서 작업하여 "내 컴퓨터에서는 되는데" 문제를 없앱니다.
- 이식성: `docker-compose.yml` 파일 하나로 어떤 Docker 환경에서든 동일한 스택을 재현할 수 있습니다.
- 단순성: 여러 컨테이너를 단일 명령어로 손쉽게 시작, 중지, 관리할 수 있습니다.
- 효율성: 빠른 환경 구축, 코드 변경 실시간 반영, 서비스별 독립적인 관리 등을 통해 개발 워크플로우를 최적화합니다.
Docker Compose는 단순히 컨테이너를 묶는 도구를 넘어, 개발팀 전체의 개발 생산성을 향상시키고, 더 견고하고 예측 가능한 개발 환경을 조성하는 데 필수적인 도구입니다. 아직 Docker Compose를 사용하고 있지 않다면, 지금 바로 도입하여 여러분의 개발 경험을 한 단계 업그레이드해 보시길 강력히 권장합니다.
이 글이 Docker Compose를 활용한 효율적인 개발 환경 구축에 도움이 되셨기를 바랍니다. 여러분의 Docker Compose 활용 경험이나 궁금한 점을 댓글로 공유해 주세요!
📌 함께 읽으면 좋은 글
- [클라우드 인프라] AWS Lambda, API Gateway로 서버리스 백엔드 구축: 운영 전략부터 비용 최적화까지
- [튜토리얼] tRPC와 Next.js로 구현하는 타입 안전 풀스택 웹 애플리케이션 구축 가이드
- [생산성 자동화] API 문서화 자동화: 코드에서 시작하는 효율적인 개발 워크플로우
이 글이 도움이 되셨다면 공감(♥)과 댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.
'튜토리얼' 카테고리의 다른 글
| VS Code 원격 개발 환경 구축: WSL, SSH, Dev Containers 실전 활용 가이드 (0) | 2026.05.06 |
|---|---|
| Prometheus Grafana 활용 컨테이너 모니터링 시스템 구축 가이드: 안정적인 서비스 운영을 위한 필수 전략 (0) | 2026.05.04 |
| WebRTC 실시간 화상 통신 애플리케이션 개발 실전 가이드: 핵심 기술부터 구현 전략까지 (0) | 2026.05.03 |
| GitHub Actions CI/CD 파이프라인 구축 실전 가이드: 개발 생산성을 극대화하는 방법 (0) | 2026.05.02 |
| Minikube로 로컬 쿠버네티스 환경 구축: 애플리케이션 배포까지 완전 정복 (0) | 2026.05.01 |