infra
Platform

모듈 맵

[Docker] 팀 전체가 클릭 한 번으로 똑같은 환경을 구성하는 Docker 워크플로우

0 / 27 완료

펼치기
0 / 27 완료0%

도커 & 컨테이너 · 12 / 27

[Docker] 팀 전체가 클릭 한 번으로 똑같은 환경을 구성하는 Docker 워크플로우

Makefile, .env.example, docker compose watch로 팀원 누구나 make dev 한 줄로 시작하는 개발 환경을 구축합니다

🚨INCIDENT ALERT
HIGH

입사 첫날, 신입은 Slack에서 “레포 클론하고 실행해 보세요”라는 메시지 하나를 받았습니다. README에는 Node.js 설치, Python 가상환경, PostgreSQL 로컬 설치, 환경변수 작성까지 열두 단계가 적혀 있었습니다.

docker compose up을 실행하자 .env 파일이 없어 앱이 죽었고, 필요한 값은 팀장만 알고 있었습니다. 개발 환경은 문서만으로 표준화되지 않습니다. 실행 진입점과 예시 환경변수까지 코드로 고정해야 팀원이 혼자 시작할 수 있습니다.

Docker로 팀 개발 환경 표준화하기

입사 첫날, 김신입은 Slack에서 "레포 클론하고 실행해 보세요"라는 메시지 하나를 받았습니다. README를 펼쳤더니 Node.js 18 설치, Python 가상환경 설정, PostgreSQL 로컬 설치, 환경변수 파일 작성... 단계가 열두 개였습니다. docker compose up 을 실행하자 Error: Cannot find module 'dotenv'가 떴고, .env 파일이 없다는 걸 뒤늦게 알았습니다. 환경변수 이름을 물어봤더니 팀장이 "아, 그거 저만 알고 있었네요"라고 답했습니다. 오후가 다 가도록 개발 환경이 뜨지 않았습니다.

이 시나리오는 낯설지 않습니다. 팀마다 개발 환경 설정 방식이 다르고, 신입이 올 때마다 누군가 옆에 앉아 두 시간을 쓰는 일이 반복됩니다. 이번 모듈에서는 Makefile, .env.example, docker compose watch, 멀티 타겟 Dockerfile을 조합해 git clone && make dev 한 줄로 환경이 뜨는 팀 표준을 만드는 방법을 다룹니다.


이번 챕터에서 배울 것

Docker Compose 기초(이전 모듈)를 알고 있다는 전제에서 출발합니다. 이번 목표는 '내 컴퓨터에서는 되는데요' 문제를 구조적으로 없애고, 팀원 누구나 동일한 환경에서 즉시 작업을 시작할 수 있게 하는 것입니다.

  • 1.env.example 패턴으로 비밀값 없이 필요한 변수 목록을 팀과 공유할 수 있다
  • 2Makefile로 make dev / make down / make logs 진입점을 표준화할 수 있다
  • 3개발용 Dockerfile 타겟(hot reload)과 프로덕션 타겟(final)을 하나로 관리할 수 있다
  • 4docker compose watch로 소스 변경 시 자동 sync/rebuild하여 볼륨 마운트의 한계를 극복할 수 있다
  • 5git clone && make dev 한 줄 온보딩 구조를 설계할 수 있다
실습 환경 준비

이 모듈의 실습은 실제 팀 프로젝트 디렉토리 구조를 직접 만들며 진행합니다. 완성된 구조는 실무 프로젝트에 그대로 복사해 사용할 수 있습니다.

Docker Compose V2 버전 확인 (2.22 이상 권장 — watch 명령 지원)
docker compose version
실습 디렉토리 생성
mkdir -p ~/dev-workflow-lab && cd ~/dev-workflow-lab
make 명령 설치 여부 확인
make --version || sudo apt-get install -y make
실습용 Node.js 이미지 사전 pull
docker pull node:20-alpine

💡개념

.env.example → .env 복사 패턴과 팀 컨벤션

Docker Compose는 프로젝트 루트의 .env 파일을 자동으로 읽어 docker-compose.yml 안의 변수(${DB_PASSWORD} 등)를 치환합니다. 문제는 이 파일에 DB 비밀번호, API 키, JWT 시크릿 같은 민감한 값이 들어있다는 점입니다. 실수로 Git에 커밋하면 퍼블릭 레포라면 즉시 유출, 프라이빗 레포도 협력사 초대 등으로 언제든 위험해집니다.

.env.example → .env 복사 패턴 — 민감한 값이 든 .env는 .gitignore로 커밋에서 제외하고, 키 이름과 더미 값만 담은 .env.example만 Git에 올림. 새 팀원은 .env.example을 .env로 복사해 실제 값을 채워 넣음. 실수로 비밀번호·API 키가 저장소에 유출되는 것을 방지하는 팀 컨벤션확대

두 파일로 역할을 분리한다

파일Git 커밋역할
.env.example커밋 O필요한 변수 이름과 예시값/빈 값
.env.gitignore실제 비밀값 (팀원 각자 관리)

.env.example 예시:

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part3/exam_12 && cd /tmp/docker/part3/exam_12

# 앱 설정
APP_PORT=3000
NODE_ENV=development

# 데이터베이스
DB_HOST=db
DB_PORT=5432
DB_NAME=myapp
DB_USER=myuser
DB_PASSWORD=change_me_locally   # ← 예시값, 실제값은 .env에서 변경

# 외부 API
STRIPE_SECRET_KEY=sk_test_REPLACE_WITH_YOUR_KEY

.gitignore 설정

로컬 터미널
# .gitignore
.env
.env.local
.env.*.local

팀 온보딩 컨벤션

새 팀원이 레포를 클론하면 README에 딱 한 줄이 있습니다.

로컬 터미널
cp .env.example .env
# .env를 열어 DB_PASSWORD, STRIPE_SECRET_KEY 등을 실제 값으로 채우세요
# (값은 팀 1Password / Notion 비밀 문서에 있습니다)

이 패턴의 장점은 .env.example필요한 변수 목록 문서 역할을 한다는 것입니다. 새 변수를 추가하면 코드 변경과 함께 .env.example도 업데이트해 PR에 포함시키는 것이 팀 컨벤션이 됩니다. 코드를 보면 어떤 환경변수가 필요한지 바로 알 수 있어, "저만 알고 있었네요" 상황이 반복되지 않습니다.


1Makefile로 dev/down/logs 진입점을 표준화하고 make dev 실행

docker compose up -d --build처럼 옵션이 길어지면 팀원마다 다르게 실행합니다. 어떤 사람은 --build를 빠뜨리고, 어떤 사람은 -d를 빠뜨려 터미널이 점령됩니다. Makefile은 이 진입점을 하나로 고정합니다. 디렉터리를 만들고 make dev로 동일하게 띄워봅니다.

실습 디렉토리 구조 만들기

로컬 터미널
mkdir -p ~/dev-workflow-lab
cd ~/dev-workflow-lab

# 필요한 파일들을 생성
touch Makefile docker-compose.yml .env.example .gitignore

Makefile 작성

MAKEFILE
.PHONY: dev down logs restart ps clean

dev:
	docker compose up -d

down:
	docker compose down

logs:
	docker compose logs -f

restart:
	docker compose restart

ps:
	docker compose ps

clean:
	docker compose down -v --remove-orphans
위험 명령어Compose 볼륨과 고아 컨테이너가 삭제되어 로컬 DB 데이터가 초기화됩니다.

개발 환경 데이터까지 완전 삭제

안전한 실행 조건: 로컬 개발 환경을 의도적으로 초기화하려는 경우에만 실행합니다.

실행 전 반드시 확인

  • 대상 Compose 프로젝트 디렉토리가 맞는지 확인했다
  • 로컬 DB 데이터가 삭제되어도 괜찮다
  • .env와 소스코드는 삭제 대상이 아님을 확인했다
docker compose down -v --remove-orphans

위 항목을 모두 확인한 후 복사할 수 있습니다

주의: Makefile의 들여쓰기는 반드시 탭(Tab) 문자를 사용해야 합니다. 스페이스로 작성하면 Makefile:3: *** missing separator. Stop. 오류가 납니다.

.PHONY는 같은 이름의 파일이 있어도 항상 명령을 실행하도록 선언합니다. dev라는 파일이 프로젝트에 생겨도 make dev가 그 파일이 아닌 명령을 실행합니다.

함께 쓸 docker-compose.yml 예시

YAML
services:
  app:
    build:
      context: .
      target: dev              # Dockerfile의 dev 스테이지만 빌드
    ports:
      - "${APP_PORT:-3000}:3000"
    env_file:
      - .env
    volumes:
      - .:/app
      - /app/node_modules      # node_modules는 컨테이너 것 유지
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ${DB_NAME:-myapp}
      POSTGRES_USER: ${DB_USER:-myuser}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-localpassword}
    ports:
      - "${DB_PORT:-5432}:5432"
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

동작 확인

로컬 터미널
cp .env.example .env
make dev     # docker compose up -d 와 동일
make ps      # 컨테이너 상태 확인
make logs    # 전체 로그 스트리밍
make down    # 중지 및 컨테이너 삭제
make dev
🔍실행 후 확인할 것
  • docker compose ps 먼저: State 컬럼에서 Up이 아닌 컨테이너를 즉시 식별 — Exit(1)이면 앱 크래시, Exit(137)이면 OOM, Restarting이면 헬스체크 실패 반복 중
  • 포트 바인딩 보안 판단: 포트가 0.0.0.0:5432로 바인딩되면 외부 접근 가능, 127.0.0.1:5432면 로컬만 접근 가능. 팀 개발 환경에서 DB 포트가 0.0.0.0으로 열려 있으면 보안 위험
  • Volume mount 경로 오류 조합 해석: 컨테이너가 Up이지만 앱이 빈 디렉토리로 시작하면 → volume mount 경로 오타나 절대/상대 경로 혼동. docker exec로 컨테이너 내부 확인 후 docker-compose.yml volumes 경로 점검

Makefile의 핵심 가치는 명령어 표준화입니다. 신입이 "어떻게 실행해요?"라고 물으면 "make dev요"라는 한 마디로 끝납니다. CI에서도 동일한 명령으로 환경을 올릴 수 있습니다.


💡개념

개발용 이미지(hot reload) vs 프로덕션 이미지(final) — Dockerfile 멀티 타겟

Dockerfile 하나로 개발 환경과 프로덕션 환경을 모두 커버하는 것이 팀 표준의 핵심입니다. 멀티 스테이지 빌드(이전 모듈에서 학습)를 활용하면 FROM ... AS devFROM ... AS final 두 타겟을 한 파일에 선언하고, 빌드 시 어떤 타겟을 쓸지 선택합니다.

개발용 이미지 vs 프로덕션 이미지 — Dockerfile 멀티 타겟확대

개발 vs 프로덕션 이미지의 차이

항목개발(dev)프로덕션(final)
소스 마운트볼륨/watch로 실시간 반영COPY로 이미지에 포함
실행 방식nodemon, uvicorn --reload 등node server.js, gunicorn 등
devDependencies포함 (nodemon 등)제외 (npm ci --only=production)
이미지 크기상대적으로 큼최소화
디버그 도구포함 가능제외

Node.js 예시 — 단일 Dockerfile, 두 타겟

Dockerfile
# -- 공통 베이스 --
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./

# -- 개발 스테이지 --
FROM base AS dev
# devDependencies 포함 (nodemon, ts-node 등)
RUN npm install
COPY . .
# 소스는 볼륨/watch로 오버라이드되므로 COPY는 fallback
CMD ["npm", "run", "dev"]    # nodemon 등 hot reload 실행

# -- 프로덕션 스테이지 --
FROM base AS final
RUN npm ci --only=production
COPY . .
RUN npm run build            # 빌드 아티팩트 생성
CMD ["node", "dist/index.js"]

타겟 지정 빌드

Docker
# 개발 이미지 빌드 (Compose가 target: dev를 보고 자동 처리)
docker build --target dev -t myapp:dev .

# 프로덕션 이미지 빌드
docker build --target final -t myapp:prod .

docker-compose.yml에서 target: dev로 선언하면 make dev 실행 시 항상 개발 스테이지로 빌드됩니다. CI/CD 파이프라인은 target: final을 사용하거나 docker build --target final을 직접 호출합니다. 파일은 하나, 목적에 따라 다른 결과물을 얻는 구조입니다.


실습 2: docker compose watch로 소스 변경 자동 동기화

볼륨 마운트는 소스 변경을 컨테이너에 즉시 반영하지만, package.json이 바뀌면 어떻게 될까요? 새 패키지가 설치되지 않아 앱이 충돌합니다. 개발자는 컨테이너를 수동으로 재빌드해야 합니다. docker compose watch는 이 문제를 해결합니다.

docker compose watch란?

Compose 2.22+에서 정식 도입된 기능으로, 파일 변경을 감지해 sync(파일 복사), rebuild(이미지 재빌드), restart(컨테이너 재시작) 세 가지 액션 중 하나를 수행합니다.

docker-compose.yml에 watch 블록 추가

YAML
services:
  app:
    build:
      context: .
      target: dev
    ports:
      - "${APP_PORT:-3000}:3000"
    env_file:
      - .env
    develop:                     # watch 설정 블록
      watch:
        - action: sync           # 소스 변경 → 컨테이너로 즉시 복사
          path: ./src
          target: /app/src
        - action: rebuild        # package.json 변경 → 이미지 재빌드
          path: package.json
        - action: rebuild
          path: package-lock.json

watch 실행

Docker
# watch 모드로 실행 (포그라운드 — 로그 스트리밍됨)
docker compose watch

# 또는 Makefile에 추가
# watch:
# 	docker compose watch

세 가지 액션 비교

action언제 사용동작
sync소스 코드(.js, .py, .go) 변경파일을 컨테이너 내부로 복사 (재시작 없음)
rebuildpackage.json, requirements.txt, go.mod 변경이미지를 다시 빌드하고 컨테이너 교체
restart설정 파일(.env, nginx.conf) 변경재빌드 없이 컨테이너만 재시작

sync vs 볼륨 마운트 선택 기준

YAML
# 볼륨 마운트 방식 (기존)
volumes:
  - ./src:/app/src
# 단순하고 즉각적이나, package.json 변경은 수동 대응 필요

# watch 방식
develop:
  watch:
    - action: sync
      path: ./src
      target: /app/src
    - action: rebuild
      path: package.json
# 파일 종류별 대응 규칙을 명시적으로 선언

볼륨 마운트가 더 단순한 경우도 많습니다. 프로젝트에 종속성 변경이 잦거나, 팀원들이 수동 재빌드를 자주 잊는다면 watch가 유리합니다.


💡개념

git clone && make dev — 한 줄로 시작되는 팀 환경 구조

앞서 배운 요소들을 조합하면 온보딩이 단 두 단계로 압축됩니다.

git clone && make dev — 한 줄로 시작되는 팀 환경 구조확대

로컬 터미널
git clone https://github.com/myorg/myapp.git
cd myapp
cp .env.example .env        # 비밀값 채우기 (팀 문서 참고)
make dev                    # 끝

전체 파일 구조

팀 표준 개발 환경을 위한 최소 파일 세트입니다.

myapp/의 최소 파일 세트입니다.

  • Makefile — 진입점 표준화
  • docker-compose.yml — 서비스 정의 (develop.watch 포함)
  • Dockerfile — dev / final 멀티 타겟
  • .env.example — 필요한 변수 목록 (커밋)
  • .env — 실제 비밀값 (.gitignore)
  • .gitignore
  • src/

README에 넣는 딱 세 줄

MARKDOWN
## 개발 환경 시작하기

git clone ... && cd myapp
cp .env.example .env  # .env 파일에 팀 문서의 값을 채워주세요
make dev

이 구조가 해결하는 문제

문제해결
"어떤 환경변수가 필요해요?".env.example을 보면 바로 알 수 있음
"docker compose 옵션을 어떻게 써요?"make dev 한 줄로 통일
"소스 수정했는데 반영이 안 돼요"docker compose watch가 자동 sync
"package.json 바꿨더니 오류 나요"watch의 rebuild 액션이 자동 처리
"개발용/프로덕션용 이미지 따로 관리해요?"Dockerfile 멀티 타겟으로 하나로 통합

신입이 환경을 올리는 데 두 시간 걸리던 것이 15분으로 줄어듭니다. 더 중요한 것은 시니어가 옆에 앉아줄 필요가 없다는 점입니다.


볼륨 마운트를 사용할 때 로컬의 node_modules가 컨테이너 내부 node_modules를 덮어쓰는 문제입니다.

Docker
# 증상
docker compose up
# Error: Cannot find module 'express'
YAML
# 원인: 볼륨 마운트가 컨테이너의 /app 전체를 덮어씀
# 컨테이너 내 npm install로 설치된 node_modules가 사라짐
volumes:
  - .:/app          # 이 줄이 컨테이너의 node_modules를 가림
YAML
# 해결: node_modules만 별도 볼륨으로 분리
volumes:
  - .:/app
  - /app/node_modules   # 익명 볼륨으로 컨테이너 것 유지

익명 볼륨(/app/node_modules)은 Docker가 관리하는 별도 공간에 저장됩니다. 호스트의 node_modules가 없거나 비어 있어도 컨테이너에서 npm install한 결과가 유지됩니다.

로컬에 PostgreSQL이 이미 실행 중이거나, 이전에 실행한 컨테이너가 아직 포트를 점유하고 있습니다.

로컬 터미널
# 1. 점유 프로세스 확인 (Mac/Linux)
lsof -i :5432
# 또는
ss -tlnp | grep 5432

# 2. 컨테이너가 이미 실행 중인지 확인
docker ps | grep 5432

# 3-A. 로컬 PostgreSQL 서비스 중지
sudo systemctl stop postgresql    # Linux
brew services stop postgresql@16  # Mac

# 3-B. 또는 .env에서 호스트 포트만 변경 (팀 파일은 그대로)
# .env 파일 수정
DB_PORT=5433    # 5432 → 5433으로 변경

docker-compose.yml을 직접 수정하면 Git에 올라가 팀 전체에 영향을 줍니다. .envDB_PORT만 바꾸면 내 로컬에서만 포트가 달라집니다. 이것이 포트도 환경변수로 관리하는 이유입니다.


심화 — 호스트와 컨테이너가 파일을 공유할 때 갈라지는 것들

💡개념

심화: docker compose watch가 실제로 하는 일 — 파일 이벤트가 VM 경계를 넘지 못하는 문제

리눅스 노트북에선 볼륨 마운트만으로 hot reload가 잘 되다가, 유독 맥·윈도우 팀원에게서만 "저장했는데 nodemon이 안 뜬다"는 제보가 옵니다. 같은 코드인데 OS에 따라 갈리는 이유는 파일 변경을 누가 감지하느냐에 있습니다.

  • 컨테이너 안 도구는 inotify로 변경을 감지합니다. nodemon·webpack 같은 도구는 커널의 inotify 이벤트로 "파일이 바뀌었다"를 통지받습니다. 그런데 Docker Desktop에서 소스는 호스트(맥/윈도우) 파일시스템에 있고 컨테이너는 그 위에 얹힌 리눅스 VM 안에서 돕니다. 파일 내용은 virtiofs/gRPC-FUSE로 공유되지만, inotify 이벤트는 그 경계를 넘어오지 않습니다. 그래서 파일 내용은 바뀌어 있는데 컨테이너 안 nodemon은 "바뀐 줄 모릅니다".
  • 리눅스 네이티브에선 이 문제가 없습니다. 바인드 마운트가 커널 레벨이라 inotify가 그대로 전달됩니다. 즉 이건 Docker 버그가 아니라 VM 경계의 구조적 한계이고, 그래서 "내 리눅스에선 되는데" 재현이 안 되는 것입니다.
  • watch가 우회하는 방식이 핵심입니다. docker compose watch는 감시를 컨테이너 안이 아니라 호스트 쪽에서 수행하고, 변경을 감지하면 파일을 컨테이너로 push(sync)합니다. inotify 통지에 의존하지 않으니 OS와 무관하게 동작합니다.
  • 한계도 알아야 씁니다. sync는 즉시가 아니라 짧은 지연이 있고, 시작 시 1회 전체 복사가 일어나며, ignore 규칙에 걸린 경로는 아예 감시되지 않습니다. rebuild 액션을 자주 트리거하는 경로를 잘못 걸면 매 저장마다 이미지가 다시 빌드돼 오히려 볼륨보다 느려집니다 — 어떤 파일에 sync를, 어떤 파일에 rebuild를 걸지는 설계의 문제입니다.

상황: 리눅스에서 make dev로 개발하다, 컨테이너가 만들어낸 파일 — DB 마이그레이션 생성물, __pycache__, 빌드 산출물, npm이 쓴 파일 — 을 호스트 에디터로 열어 고치려니 저장이 안 됩니다. rmgit checkoutPermission denied. 방금 내가 실행한 프로젝트인데 왜 내 파일이 내 것이 아닌지 어리둥절합니다.

원인: 컨테이너 프로세스가 기본적으로 root(uid 0) 로 돕니다. 바인드 마운트는 소유권을 이름이 아니라 uid 숫자 그대로 보존하므로, 컨테이너 안 root(0)가 만든 파일은 같은 커널·같은 파일시스템을 공유하는 호스트에서도 uid 0 = root 소유로 보입니다. 호스트의 나는 uid 1000이라 root 파일을 못 고칩니다. 유저 네임스페이스 매핑을 쓰지 않았을 때의 전형적인 함정입니다.

진단: ls -l로 문제 파일의 소유자가 root인지 확인하고, docker compose exec app id로 컨테이너 프로세스의 uid가 0인지 봅니다. 둘이 맞물리면 확정입니다. Docker Desktop(맥/윈도우)은 파일 공유 계층이 uid를 자동 매핑해줘 이 증상이 거의 안 나타나므로, "리눅스·CI 러너에서만" 재현되는 특징도 단서입니다.

해결: 이미 잠긴 파일은 sudo chown -R $USER:$USER . 로 되찾습니다. 재발 방지는 컨테이너를 호스트와 같은 uid로 돌리는 것 — compose에 user: "${UID}:${GID}" 를 주고 셸에서 UID/GID 를 export 하거나, Dockerfile에서 호스트와 같은 uid의 비-root USER 를 만들어 둡니다. 더 근본적으로는 rootless Docker나 userns-remap(유저 네임스페이스 재매핑)으로 컨테이너 root를 호스트의 비특권 uid로 매핑해 둘 수 있습니다.


💼
실무 맥락
현업 패턴

많은 팀에서 새 서비스나 기능 브랜치를 만들 때 PR 본문 또는 README에 "이 변경을 로컬에서 어떻게 실행하는가" 를 명시하는 관행이 있습니다.

실무에서 흔히 보는 PR 체크리스트

MARKDOWN
## 개발 환경 변경사항

- [ ] .env.example에 신규 환경변수 추가 여부 확인
- [ ] docker-compose.yml 변경 시 팀원 슬랙 공지
- [ ] 새 서비스 추가 시 README 시작 가이드 업데이트
- [ ] Makefile 신규 target 추가 시 `make help` 출력 업데이트

Makefile에 help target 추가하기

규모가 커지면 make 명령이 많아집니다. make help로 목록을 출력하는 관행이 있습니다.

MAKEFILE
.PHONY: dev down logs restart ps clean watch help

dev:       ## 개발 서버 시작 (백그라운드)
	docker compose up -d

down:      ## 컨테이너 중지 및 삭제
	docker compose down

logs:      ## 전체 로그 스트리밍
	docker compose logs -f

watch:     ## watch 모드로 실행 (소스 변경 자동 반영)
	docker compose watch

clean:     ## 컨테이너 + 볼륨 전체 삭제 (데이터 초기화)
	docker compose down -v --remove-orphans

help:      ## 사용 가능한 명령 목록 출력
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
	  awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
로컬 터미널
make help
# dev             개발 서버 시작 (백그라운드)
# down            컨테이너 중지 및 삭제
# logs            전체 로그 스트리밍
# watch           watch 모드로 실행 (소스 변경 자동 반영)
# clean           컨테이너 + 볼륨 전체 삭제 (데이터 초기화)

시니어가 코드 리뷰에서 체크하는 것

팀 환경 표준화가 잘 된 조직에서는 PR 리뷰 시 코드 외에 이런 항목도 확인합니다.

  • 신규 환경변수를 .env.example에 추가했는가?
  • DB 스키마 변경이 있다면 마이그레이션 스크립트가 포함됐는가?
  • make dev로 로컬 실행이 가능한가? (리뷰어가 직접 확인)

이 관행이 쌓이면 팀 전체의 개발 속도가 높아집니다. 누가 온보딩을 해줄 여유가 없어도, 문서와 도구 자체가 온보딩을 대신합니다.


명령어·단축키 빠른 참조

이 모듈에서 다룬 팀 개발 워크플로우 명령을 실전 옵션과 함께 모았습니다. make dev/make down 뒤에서 실제로 실행되는 명령들입니다.

명령어/단축키용도자주 쓰는 예
docker compose up서비스 스택 기동docker compose up -d
docker compose down스택 중지·컨테이너/네트워크 삭제docker compose down -v --remove-orphans (볼륨까지)
docker compose logs서비스 로그 확인docker compose logs -f
docker compose ps실행 중 서비스 상태docker compose ps
docker compose restart특정 서비스만 재시작docker compose restart web
docker compose exec실행 중 서비스에 명령 실행docker compose exec web sh
docker compose watch소스 변경을 컨테이너에 자동 반영docker compose watch (sync/rebuild 자동)
docker compose versionCompose 버전 확인(watch는 v2.22+)docker compose version
docker build --target멀티스테이지에서 개발/운영 스테이지 선택docker build --target dev -t myapp:dev .
docker ps컨테이너·포트 매핑 확인docker ps | grep 5432
docker stats개발 컨테이너 자원 사용량 관찰docker stats

관련 모듈로 더 깊이:

다음 모듈에서는 컨테이너가 CPU와 메모리를 과점유하지 못하도록 자원 제한을 설정하는 방법을 다룹니다. docker stats와 cgroups 기반 제한을 직접 확인합니다.

지식 확인

퀴즈 — 8문제

Q1

.env.example을 Git에 커밋하고 .env를 .gitignore에 추가하는 이유는?

Q2

docker compose watch와 볼륨 마운트(volumes)의 가장 큰 차이점은?

Q3

팀원이 make dev 실행 시 'Bind for 0.0.0.0:5432 failed: port is already allocated' 오류가 발생했다면 가장 먼저 해야 할 일은?

Q4

단일 Dockerfile에 dev/prod 두 스테이지를 두고 개발 스테이지에만 devDependencies(nodemon, ts-node)를 포함시키는 이유로 가장 적절한 것은?

Q5

make down이 docker compose down -v --remove-orphans를 실행한다. 이걸 매일 습관처럼 쓰던 팀원이 어느 날 DB 데이터가 전부 사라졌다고 한다. 직접 원인은?

Q6

docker-compose.yml의 build에 target: dev로 지정하면 일어나는 일로 옳은 것은?

Q7

[심화] 맥·윈도우 팀원만 '소스를 저장해도 컨테이너 안 nodemon이 재시작을 안 한다'고 한다. 볼륨 마운트로 파일 내용은 분명 반영되는데 hot reload만 안 되는 이유는?

Q8

[심화] 리눅스에서 make dev로 개발 중, 컨테이너가 생성한 마이그레이션 파일을 호스트 에디터로 고치려니 저장도 rm도 Permission denied다. 소유자를 보니 root였다. 직접 원인은?

0 / 8 답변

🧪 실습으로 확인하기

Docker Compose 멀티 서비스 구성

초급

docker-compose.yml로 nginx + 앱 컨테이너를 함께 정의하고, 서비스 간 통신과 볼륨 마운트를 구성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요