Docker Compose 로컬 개발환경¶
한 줄 요약: docker-compose는 "이 앱을 개발하려면 옆에 이것들이 떠 있어야 한다"는 준비물 목록이다. 배포 설정이 아니다. 앱 저장소에 들어 있는 이유도 그 앱에 딸린 개발 환경이기 때문이다.
📍 만난 상황¶
- 2026-11-09 16:45 — 로컬 준비(L0)를 마쳤다는 보고를 받은 직후였다. Next.js 프로젝트와 Docker PostgreSQL 17이 떴고, 화면은
http://localhost:3100에서 응답했다. → 그날 일지 - 그때 물었다: "next 컨테이너와 postgres 컨테이너는 다른 컨테이너인데, next 레포 안의 docker-compose에는 왜 postgres가 있지?"
- 답: 이 파일은 로컬 개발용 준비물이다. 게다가 로컬에서 Next는 컨테이너가 아니라
npm run dev로 직접 돌고 있었다.
🧩 쉽게 말하면¶
요리책과 준비물 목록에 빗대면 된다. 요리책(저장소)에는 레시피(앱 코드)와 "이 요리를 연습하려면 오븐이 필요하다"는 준비물 목록(docker-compose)이 같이 실려 있다.
오븐(PostgreSQL)과 요리사(Next.js)는 따로 일한다. 그래도 같은 책에 적혀 있다. 이 요리에 필요한 것이기 때문이다.
식당을 실제로 여는 일(배포)은 다른 문서로 한다. 주방 설계도(Helm·ArgoCD)가 따로 있다.
이게 없으면 새로 온 사람이 DB 버전, 계정, 포트를 하나하나 맞춰 설치해야 한다. 사람마다 환경이 달라 "내 PC에선 되는데"가 생긴다.
📖 제대로 알기¶
기본 용어¶
- 이미지(image: 프로그램과 실행에 필요한 것을 묶은 설치본). 예:
postgres:17. - 컨테이너(container: 이미지를 실제로 실행한 것). 이미지 하나로 컨테이너를 여러 개 띄울 수 있다.
- Docker: 이미지를 받고 컨테이너를 띄우는 프로그램.
- docker-compose.yml: 컨테이너 여러 개의 설정(이미지, 포트, 환경 변수, 저장 공간)을 한 파일에 적은 것.
docker compose up한 줄로 전부 띄운다. - 볼륨(volume: 컨테이너 밖에 두는 저장 공간). 컨테이너를 지워도 DB 데이터가 남게 한다.
- 포트 매핑(port mapping: PC의 포트를 컨테이너 포트에 잇는 것).
5432:5432면 PC의 5432번이 컨테이너의 5432번으로 간다. - 저장소(repository: 코드와 설정을 함께 관리하는 git 단위).
파일마다 쓰이는 곳¶
| 파일 | 언제 쓰나 | 무엇을 정하나 |
|---|---|---|
docker-compose.yml |
로컬 개발 | 개발에 필요한 DB 등을 이 PC에 띄움 |
Dockerfile |
배포 준비 | 앱을 이미지로 만드는 방법 |
| Helm 차트·ArgoCD 설정 | 클러스터 배포 | 앱과 DB를 클러스터 어디에 어떻게 띄울지 |
⚙️ 어떻게 동작하나¶
- 파일 읽기:
docker compose up -d를 치면 Docker가 같은 폴더의docker-compose.yml을 읽는다. - 이미지 받기:
postgres:17이미지가 없으면 내려받는다. 처음 한 번만 걸린다. - 저장 공간 만들기: 이름 붙은 볼륨이 없으면 만든다.
- 컨테이너 시작: 적힌 환경 변수(DB 이름, 계정)로 PostgreSQL을 켠다. 첫 시작 때 그 DB와 계정이 자동으로 생긴다.
- 포트 연결: PC의 포트를 컨테이너에 잇는다. 이제 PC에서
localhost로 접속된다. - 앱은 따로: Next.js는
npm run dev로 PC에서 직접 돈다. 코드를 고치면 바로 반영된다. 그리고localhost로 DB에 붙는다.
왜 앱은 컨테이너로 안 돌리나? 컨테이너로 돌리면 코드를 고칠 때마다 이미지를 다시 만들어야 한다. 그래서 개발 중에는 자주 바뀌는 앱은 직접, 잘 안 바뀌는 DB는 컨테이너로 나누는 게 흔하다.
flowchart LR
R["대시보드 저장소"] --> C["docker-compose.yml"]
R --> D["Dockerfile"]
R --> S["앱 코드"]
C -->|"로컬"| P[("PostgreSQL 컨테이너")]
S -->|"로컬 npm run dev"| A["Next.js (PC에서 직접)"]
A --> P
D -->|"배포"| I["앱 이미지"]
I -->|"Helm·ArgoCD"| K["클러스터"]
저장소 하나에서 로컬용(왼쪽 아래)과 배포용(오른쪽 아래)이 갈라진다. 사용자가 떠올린 그림은 "저장소 하나 = 컨테이너 하나"였다. 실제로는 저장소 하나가 여러 실행 단위를 설명할 수 있다.
🧪 예시¶
- 예시 1 · 일상에 빗대어: 새 팀원 3명이 같은 날 합류했다. compose가 없으면 각자 PostgreSQL을 설치한다. 한 명은 15, 한 명은 17, 한 명은 포트를 바꿔 설치한다. 같은 코드가 세 PC에서 다르게 돈다. compose가 있으면 셋 다
npm run db:up한 줄로 똑같은 DB 17을 갖는다. - 예시 2 · 실제로는: 로컬 개발용
docker-compose.yml의 전형적인 모양이다(값은 예시).
services:
db:
image: postgres:17
environment:
POSTGRES_DB: disaster_dashboard
POSTGRES_USER: dashboard
POSTGRES_PASSWORD: ${DB_PASSWORD}
ports:
- "5432:5432"
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
services.db: 띄울 컨테이너 하나. 여기엔 DB만 있고 Next.js는 없다.
- ② image: 운영과 같은 메이저 버전(17)을 고른다. 버전이 다르면 로컬에서만 되는 SQL이 생긴다.
- ③ environment: 첫 시작 때 만들 DB 이름과 계정. 비밀번호는 .env 파일에서 읽고, 그 파일은 커밋하지 않는다.
- ④ ports: 앱이 localhost:5432로 접속하게 한다.
- ⑤ volumes: 컨테이너를 내렸다 올려도 표와 데이터가 남는다.
package.json에 "db:up": "docker compose up -d"를 적어 두면 명령을 외울 필요가 없다.
⚖️ 비교 · 흔한 오해¶
컨테이너와 저장소는 나누는 기준이 다르다¶
| 구분 | 무엇을 나누나 | 예 |
|---|---|---|
| 컨테이너 | 실행 단위(무엇이 따로 도나) | Next, PostgreSQL이 따로 돎 |
| 저장소 | 관리 단위(무엇을 한 곳에서 고치나) | 앱 코드 + 개발용 DB 설정 |
오해 1: "컨테이너가 다르면 저장소도 달라야 한다"¶
- 실제: 둘은 다른 축이다. 같은 저장소에 여러 컨테이너 설정이 있어도 된다. 사내 선례도 그렇다. smartoffice 백엔드 저장소의 개발 환경에도 앱과 PostgreSQL이 같이 들어 있다.
- 왜 그렇게 생각하기 쉬운가: "next 레포"라는 이름 때문이다. 저장소 이름이 앱 이름이라 "그 안엔 앱만 있어야 한다"고 느끼기 쉽다.
오해 2: "docker-compose = 배포 설정"¶
- 실제: 여기서는 로컬 개발 전용이다. 쿠버네티스에 배포할 때는 compose를 쓰지 않는다. Dockerfile로 이미지를 만들고, Helm·ArgoCD가 클러스터에 띄운다.
- 덧붙임: 서버 한 대짜리 작은 서비스는 compose로 배포하기도 한다. 그래서 "compose는 배포용"이라는 말을 어디선가 들었을 수 있다. 우리 경우는 클러스터 배포라 해당하지 않는다.
오해 3: "운영 DB도 이 compose의 DB다"¶
- 실제: 운영 PostgreSQL은 따로 정한다. 클러스터 안에 띄울 수도, 사내 공용 DB를 쓸 수도 있다. 로컬 DB는 내 PC에만 있는 연습장이다.
🛠️ 실무에서는¶
- 쓸 때: 앱이 DB, 캐시(Redis), 가짜 로그인 서버처럼 "옆에 떠 있어야 하는 것"에 기대고 있을 때.
- 피할 때: 운영 환경 설정을 compose에 몰아넣을 때. 운영과 로컬 설정이 섞인다.
- 체크리스트
- DB 이미지 버전을 운영과 같은 메이저로 맞췄다.
- 비밀번호는
.env에 두고.gitignore에 넣었다. 예시는.env.example로 남겼다. - README에
db:up→ 표 만들기 → 시드 넣기 순서를 적었다. - 문제가 생기면 먼저 볼 것
- 접속 거부 →
docker compose ps로 컨테이너가 떠 있는지. - 포트 충돌 → PC에 이미 다른 PostgreSQL이 그 포트를 쓰는지.
- 계정·DB가 안 생김 → 볼륨이 이미 있으면 첫 시작 설정이 다시 돌지 않는다. 볼륨을 지우고 다시 띄운다.
- 데이터가 사라짐 →
docker compose down -v의-v는 볼륨까지 지운다.
❓ 자주 묻는 질문¶
- Q. Next.js도 compose에 넣을 수 있나? A. 넣을 수 있다. "명령 하나로 전부 띄우기"가 편해진다. 대신 코드 반영이 느려지기 쉬워서, 개발 중엔 앱을 직접 돌리는 경우가 많다.
- Q.
docker compose down하면 데이터가 날아가나? A. 그냥down은 컨테이너만 지우고 볼륨은 남긴다.down -v를 써야 데이터까지 지워진다. - Q. PC에 PostgreSQL을 직접 설치하면 안 되나? A. 그래도 된다. 다만 버전 관리, 지우고 다시 깔기, 여러 프로젝트 분리가 번거롭다. 컨테이너는 지우고 다시 띄우기가 쉽다.
- Q. pgAdmin은 compose와 무슨 관계인가?
A. pgAdmin은 DB를 들여다보는 도구일 뿐이다. compose가 띄운 DB에
localhost와 포트로 접속한다.
🔭 더 깊이 보기¶
- Dockerfile은 어떻게 쓰나(멀티 스테이지 빌드)? — 배포 단계(M6)에서 Next 앱 이미지를 직접 만들게 된다.
- 이미지와 컨테이너, 볼륨의 수명은 어떻게 다른가? — 무엇을 지우면 무엇이 사라지는지 알게 된다.
- Helm 차트는 무엇을 정하나? — compose가 로컬에서 하던 일을 클러스터에서는 무엇이 하는지 이어진다.
- 관련 주제: 쿠버네티스 클러스터 내부 통신 · 프론트엔드와 백엔드 서버 구조 · 시드 데이터와 초기 적재
📚 참고 자료¶
- 초보를 위한 도커 안내서 (subicura 블로그, 한국어) — 이미지·컨테이너·compose 개념을 그림으로 입문 · 블로그
- Docker Compose overview (Docker 공식 문서, 영문) — compose 파일 구조와 기본 명령 · 링크
- postgres 공식 이미지 (Docker Hub, 영문) —
POSTGRES_DB등 환경 변수와 첫 시작 동작 · 링크
🗓️ 이력¶
- 2026-11-09: next 저장소의 docker-compose에 왜 postgres가 있는지 물음. 컨테이너(실행 단위)와 저장소(관리 단위), 로컬 준비물과 배포 설정의 차이를 정리 → 일지
🕸️ 연결된 주제¶
바로 이어진 주제 4개전체 지도에서 보기 →