튜토리얼

Docker Compose로 풀스택 로컬 개발 환경 구축: 백엔드, 프론트엔드, 데이터베이스 연동 완벽 가이드

강코의 코딩 일기 2026. 6. 3. 21:20
반응형

Docker Compose를 활용해 백엔드, 프론트엔드, 데이터베이스를 완벽하게 연동하는 로컬 개발 환경 구축 방법을 친절하게 안내합니다. 복잡한 설정 없이 효율적인 개발 워크플로우를 시작해 보세요!

안녕하세요, 개발자 여러분! 새로운 프로젝트를 시작할 때마다 개발 환경 설정 때문에 머리 싸매고 계시진 않나요? 😩 팀원마다 각기 다른 OS와 라이브러리 버전 때문에 "제 컴퓨터에서는 잘 되는데요?"라는 말을 들어본 경험, 한 번쯤 있으실 거예요. 아니면 백엔드, 프론트엔드, 데이터베이스까지 여러 서비스를 동시에 띄우느라 노트북 팬이 비명을 지르던 순간도 있었을 거고요.

이런 문제들, 이제 Docker Compose 하나로 깔끔하게 해결할 수 있습니다! Docker Compose는 여러 개의 Docker 컨테이너를 하나의 서비스처럼 정의하고 실행할 수 있도록 도와주는 도구인데요. 오늘 이 글을 통해 백엔드, 프론트엔드, 데이터베이스까지 완벽하게 연동되는 나만의 로컬 개발 환경을 구축하는 방법을 친절하게 알려드릴게요. 복잡한 설정은 이제 그만! 효율적인 개발 워크플로우를 함께 만들어가 보시죠!


📑 목차

Docker Compose를 활용한 로컬 개발 환경 구축: 백엔드, 프론트엔드, 데이터베이스 연동 가이드 - belgium, antwerp, shipping, container, freight, cargo, transport, harbor, container, container, container, freight, cargo, cargo, cargo, cargo, cargo

Image by 2427999 on Pixabay

개발 환경 설정, 아직도 고통받으시나요? Docker Compose가 해답!

개발 환경 설정은 프로젝트 시작의 필수 관문이지만, 많은 개발자에게 큰 부담으로 다가오곤 하죠. 특히 풀스택 프로젝트처럼 여러 기술 스택이 얽혀 있을 때는 더욱 그렇습니다. 데이터베이스 설치부터 버전 관리, 백엔드 런타임 설정, 프론트엔드 의존성 관리까지, 할 일이 산더미거든요. 게다가 팀원 간의 환경 일관성까지 신경 써야 하니 여간 번거로운 일이 아닐 수 없습니다.

이런 상황에서 Docker Compose는 마치 구원투수처럼 등장합니다. Docker Compose는 여러 개의 도커 컨테이너를 하나의 애플리케이션으로 묶어 정의하고 관리할 수 있게 해주는 도구예요. 예를 들어, 백엔드 서버는 Node.js 컨테이너로, 데이터베이스는 PostgreSQL 컨테이너로, 프론트엔드 서버는 Nginx 컨테이너로 각각 띄우고, 이 세 컨테이너가 서로 유기적으로 통신하며 하나의 서비스처럼 동작하게 만들 수 있는 거죠.

Docker Compose를 사용하면 어떤 이점들이 있을까요? 다음 표를 통해 기존 수동 환경 설정 방식과 비교해 보면 그 차이를 확연히 느끼실 수 있을 거예요.

항목 수동 환경 설정 Docker Compose 활용
설정 시간 길고 복잡하며, 각 서비스별 수동 설치 필요 docker-compose.yml 파일 정의 후, 단 한 번의 명령어로 모든 서비스 실행
환경 일관성 OS, 로컬 환경에 따라 다를 수 있으며, "내 컴퓨터에선 되는데..." 문제 발생 모든 팀원이 동일한 컨테이너 이미지를 사용하므로 일관된 개발 환경 보장
의존성 관리 각 서비스의 의존성(라이브러리, 런타임 등)을 로컬에 직접 설치하고 관리해야 함 각 서비스가 격리된 컨테이너에서 실행되므로 의존성 충돌 걱정 없음
협업 용이성 새로운 팀원 합류 시 환경 설정에 많은 시간 소요, 문제 발생 시 해결 어려움 docker-compose.yml 파일만 공유하면 누구나 쉽게 개발 환경 구축 가능
리소스 격리 로컬 시스템 리소스를 공유하며, 여러 버전의 서비스 설치 어려움 각 서비스가 독립적인 컨테이너에서 실행되어 리소스 격리 및 효율적 관리 가능

어때요? 이제 Docker Compose를 사용해야 할 강력한 이유를 아시겠죠? 개발의 시작부터 끝까지, 우리의 생산성을 비약적으로 끌어올려 줄 아주 유용한 도구랍니다. 다음 섹션에서는 Docker Compose의 핵심 개념들을 좀 더 자세히 파헤쳐 볼게요.


Docker Compose, 핵심 개념부터 제대로 알아봐요

Docker Compose를 효과적으로 활용하려면 몇 가지 핵심 개념을 이해하는 것이 중요해요. 너무 어렵게 생각하지 마시고, 마치 레고 블록을 조립하듯이 이해해 보면 쉽습니다!

Dockerfile과 Docker Compose 파일의 역할 차이

먼저, DockerfileDocker Compose 파일 (`docker-compose.yml`)의 역할을 명확히 구분하는 것이 중요해요.

  • Dockerfile: 이건 마치 하나의 레고 블록을 만드는 설계도라고 생각하시면 돼요. 특정 애플리케이션(예: Node.js 백엔드, React 프론트엔드)을 실행하기 위한 환경, 종속성, 실행 명령어 등을 정의해서 도커 이미지(Docker Image)를 만들어내는 파일이죠. 이 이미지를 기반으로 컨테이너(Container)가 생성됩니다. 즉, '어떻게 하나의 독립적인 실행 환경을 만들 것인가'를 정의하는 겁니다.
  • Docker Compose 파일 (`docker-compose.yml`): 이건 여러 개의 레고 블록(컨테이너)들을 서로 연결하고 조립해서 하나의 멋진 작품(애플리케이션)을 만드는 전체 설계도예요. 이 파일에서는 여러 개의 서비스를 정의하고, 각 서비스에 어떤 이미지를 사용할지, 어떤 포트를 열지, 어떤 볼륨을 연결할지, 어떤 네트워크를 사용할지 등을 설정합니다. 한마디로 '어떻게 여러 컨테이너들을 묶어서 하나의 시스템으로 동작하게 할 것인가'를 정의하는 거죠.

그러니까 Dockerfile개별 서비스의 컨테이너를 만드는 데 집중하고, Docker Compose 파일여러 컨테이너들을 묶어 하나의 시스템으로 실행하고 관리하는 데 집중한다고 이해하시면 됩니다.

`docker-compose.yml` 파일 구조 해부하기

docker-compose.yml 파일은 YAML 형식으로 작성되며, 다음과 같은 주요 섹션들로 구성됩니다. 예시와 함께 살펴볼게요.


version: '3.8' # Docker Compose 파일 형식 버전

services: # 이 애플리케이션을 구성하는 서비스들을 정의하는 섹션
  web: # 'web'이라는 서비스 이름
    build: . # 현재 디렉토리의 Dockerfile을 사용하여 이미지 빌드
    ports: # 컨테이너의 포트를 호스트 머신의 포트에 연결
      - "80:80" # 호스트의 80번 포트를 컨테이너의 80번 포트에 연결
    volumes: # 호스트와 컨테이너 간의 볼륨(데이터) 공유 설정
      - .:/app # 현재 디렉토리를 컨테이너의 /app 디렉토리에 마운트
    environment: # 컨테이너 내부에서 사용할 환경 변수 설정
      NODE_ENV: development
    depends_on: # 이 서비스가 시작되기 전에 다른 서비스가 시작되어야 함을 지정
      - db # 'db' 서비스가 먼저 시작되어야 함

  db: # 'db'라는 서비스 이름
    image: postgres:13 # Docker Hub에서 postgres:13 이미지를 사용
    environment: # 데이터베이스 관련 환경 변수 설정
      POSTGRES_DB: mydatabase
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes: # 데이터베이스 데이터 영속성을 위한 볼륨 설정
      - db_data:/var/lib/postgresql/data # 'db_data'라는 이름의 볼륨 사용

volumes: # 명시적 볼륨 정의 섹션
  db_data: # 'db_data'라는 이름의 볼륨을 정의 (컨테이너가 삭제되어도 데이터 유지)

networks: # 사용자 정의 네트워크 정의 섹션 (선택 사항, 기본적으로 default 네트워크 사용)
  app_network:
    driver: bridge # 브릿지 네트워크 드라이버 사용

주요 옵션들을 하나씩 살펴볼까요?

  • version: Docker Compose 파일 형식의 버전을 지정합니다. 최신 버전은 보통 '3.x'대를 사용하죠.
  • services: 이 섹션에 애플리케이션을 구성하는 각 컨테이너 서비스들을 정의합니다. 위 예시에서는 `web`과 `db`라는 두 개의 서비스가 있네요.
    • build: Dockerfile이 있는 경로를 지정하여 이미지를 빌드합니다.
    • image: 미리 빌드된 도커 이미지를 사용할 때 이미지 이름을 지정합니다 (예: postgres:13).
    • ports: "호스트포트:컨테이너포트" 형식으로 포트 매핑을 설정합니다.
    • volumes: "호스트경로:컨테이너경로" 형식으로 호스트 머신의 파일이나 디렉토리를 컨테이너 내부에 마운트하여 데이터를 공유하거나 영속성(persistence)을 확보합니다. 개발 중 코드 변경 시 컨테이너를 다시 빌드할 필요 없이 바로 반영되도록 할 때 유용하죠.
    • environment: 컨테이너 내부에서 사용할 환경 변수를 설정합니다.
    • depends_on: 특정 서비스가 다른 서비스보다 먼저 시작되어야 함을 명시합니다. 예를 들어, 백엔드 서비스는 데이터베이스 서비스가 시작된 후에 실행되어야겠죠. 하지만 depends_on단순히 시작 순서만 보장할 뿐, 서비스가 완전히 준비(ready)될 때까지 기다리는 것은 아님을 주의해야 합니다.
    • networks: 컨테이너가 참여할 네트워크를 지정합니다. 별도로 지정하지 않으면 Docker Compose가 기본적으로 생성하는 default 네트워크에 모든 서비스가 연결됩니다.
  • volumes: services 섹션에서 사용될 명시적 볼륨들을 정의합니다. 이렇게 정의된 볼륨은 컨테이너가 삭제되어도 데이터가 유지되어 데이터 영속성을 보장합니다.
  • networks: 사용자 정의 네트워크를 정의합니다. 컨테이너 간의 통신을 더욱 세밀하게 제어할 수 있게 해줍니다.

이 정도 개념만 잘 이해하고 있어도 Docker Compose로 대부분의 개발 환경을 구축하는 데 전혀 문제가 없을 거예요. 이제 실제 백엔드, 데이터베이스, 프론트엔드 서비스를 하나씩 컨테이너화하는 방법을 살펴볼까요?


백엔드 서비스 (Node.js/Spring Boot) 컨테이너화하기

가장 먼저 백엔드 서비스를 컨테이너화하는 방법을 알아보겠습니다. 여기서는 Node.js를 예시로 들고, Spring Boot도 간략히 언급할게요. 여러분의 프로젝트 스택에 맞춰 적용하시면 됩니다.

Node.js 백엔드 컨테이너 설정 예시

Node.js 백엔드 프로젝트가 backend 폴더 안에 있다고 가정해 봅시다. 이 폴더 안에 다음과 같은 Dockerfile을 작성해 주세요.


# backend/Dockerfile

# 1. Node.js 공식 이미지를 기반으로 사용 (Node.js 18 버전)
FROM node:18-alpine

# 2. 컨테이너 내부의 작업 디렉토리 설정
WORKDIR /app

# 3. package.json과 package-lock.json 파일을 복사하여 의존성 캐싱 활용
#    먼저 복사하는 이유는 이 파일들이 변경되지 않는 한 npm install을 다시 하지 않도록 캐싱하기 위함
COPY package*.json ./

# 4. 프로젝트 의존성 설치
RUN npm install

# 5. 모든 소스 코드를 컨테이너의 /app 디렉토리로 복사
COPY . .

# 6. 애플리케이션이 사용할 포트 명시 (실제 노출은 docker-compose.yml에서 설정)
EXPOSE 3000

# 7. 애플리케이션 실행 명령어
CMD ["npm", "start"]

Dockerfile은 Node.js 애플리케이션을 위한 이미지를 생성하는 방법을 정의합니다. 이제 docker-compose.yml 파일에 이 백엔드 서비스를 추가해 봅시다. docker-compose.yml 파일은 프로젝트 루트 디렉토리에 있다고 가정합니다.


# docker-compose.yml

version: '3.8'

services:
  backend:
    build: ./backend # backend 폴더의 Dockerfile을 사용하여 이미지 빌드
    ports:
      - "3000:3000" # 호스트의 3000번 포트를 컨테이너의 3000번 포트에 연결
    volumes:
      - ./backend:/app # 호스트의 ./backend 폴더를 컨테이너의 /app에 마운트 (코드 변경 실시간 반영)
      - /app/node_modules # node_modules 폴더는 볼륨에서 제외 (컨테이너 내부의 node_modules 사용)
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://user:password@db:5432/mydatabase # DB 연결 정보 (db는 서비스 이름)
    depends_on:
      - db # db 서비스가 먼저 시작되어야 함을 명시
    restart: always # 컨테이너 종료 시 항상 다시 시작

여기서 중요한 부분은 volumes 설정이에요. ./backend:/app을 통해 호스트 머신의 소스 코드를 컨테이너와 동기화함으로써, 코드를 수정하면 컨테이너를 다시 빌드할 필요 없이 바로 변경 사항이 반영됩니다. /app/node_modules를 별도로 제외하는 이유는 호스트의 node_modules가 컨테이너 내부의 것과 충돌하거나 비효율적으로 작동하는 것을 방지하기 위함이에요. 그리고 DATABASE_URL처럼 민감한 정보는 environment로 설정하거나, 더 나아가 .env 파일을 사용하는 것이 좋습니다.

Spring Boot 백엔드 컨테이너 설정 예시 (간략)

Spring Boot 프로젝트의 경우, 빌드된 JAR 파일을 컨테이너화하는 것이 일반적입니다. backend 폴더 안에 다음과 같은 Dockerfile을 작성할 수 있습니다.


# backend/Dockerfile (Spring Boot 예시)

# 1. OpenJDK 이미지를 기반으로 사용
FROM openjdk:17-jdk-slim

# 2. 컨테이너 내부의 작업 디렉토리 설정
WORKDIR /app

# 3. 빌드된 JAR 파일을 컨테이너로 복사 (예: build/libs/my-app.jar)
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar

# 4. 애플리케이션 실행 명령어
ENTRYPOINT ["java","-jar","/app.jar"]

그리고 docker-compose.yml에서는 build 대신 image를 사용하거나, 멀티스테이지 빌드를 통해 빌드 과정을 컨테이너 내부에서 처리할 수도 있습니다. 여기서는 빌드된 JAR 파일을 복사하는 간단한 예시를 들었습니다.


# docker-compose.yml (Spring Boot 백엔드 서비스 부분)

services:
  backend:
    build: ./backend # Dockerfile을 사용하여 빌드
    ports:
      - "8080:8080" # Spring Boot 기본 포트
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydatabase
      SPRING_DATASOURCE_USERNAME: user
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db
    restart: always

어떤 백엔드 스택을 사용하든, Dockerfile로 이미지를 정의하고 docker-compose.yml로 서비스 설정을 통합하는 방식은 동일합니다. 이제 데이터베이스 컨테이너를 추가해 볼까요?


Docker Compose를 활용한 로컬 개발 환경 구축: 백엔드, 프론트엔드, 데이터베이스 연동 가이드 - statue, sculpture, iron, steel, docker, finland, hamina, docker, docker, docker, docker, docker, finland

Image by Olga_Fil on Pixabay

데이터베이스 (PostgreSQL/MySQL) 컨테이너로 띄우기

데이터베이스는 애플리케이션의 핵심 데이터가 저장되는 곳이죠. Docker Compose를 사용하면 데이터베이스 설치 및 설정도 아주 간단해집니다. 여기서는 PostgreSQL을 예시로 들고, MySQL도 간략히 언급할게요.

PostgreSQL 데이터베이스 컨테이너 설정 예시

docker-compose.yml 파일에 db 서비스를 추가해 봅시다. 여기서는 Docker Hub에 있는 공식 PostgreSQL 이미지를 활용할 거예요.


# docker-compose.yml (DB 서비스 부분)

version: '3.8'

services:
  # ... (이전 백엔드 서비스)

  db:
    image: postgres:13 # PostgreSQL 13 버전 이미지 사용
    environment: # 데이터베이스 설정에 필요한 환경 변수
      POSTGRES_DB: mydatabase # 데이터베이스 이름
      POSTGRES_USER: user # 사용자 이름
      POSTGRES_PASSWORD: password # 비밀번호
    volumes:
      - db_data:/var/lib/postgresql/data # 데이터 영속성을 위한 볼륨 마운트
    ports:
      - "5432:5432" # (선택 사항) 호스트에서 DB에 직접 접근하려면 포트 매핑
    restart: always # 컨테이너 종료 시 항상 다시 시작

volumes: # 명시적 볼륨 정의
  db_data: # db_data라는 이름의 볼륨을 정의

여기서 가장 중요한 것은 volumes 설정이에요. db_data:/var/lib/postgresql/data는 컨테이너 내부의 PostgreSQL 데이터 디렉토리를 db_data라는 명명된 볼륨(named volume)에 연결합니다. 이렇게 하면 컨테이너가 삭제되더라도 db_data 볼륨은 남아있기 때문에 데이터가 영구적으로 보존됩니다. 개발 중에 컨테이너를 올렸다 내렸다 해도 데이터가 날아갈 걱정은 없겠죠?

ports: - "5432:5432"는 호스트 머신에서도 PostgreSQL에 직접 접속하고 싶을 때 유용합니다. 예를 들어, DBeaver나 DataGrip 같은 GUI 툴로 데이터베이스를 관리하고 싶을 때 이 포트를 통해 접속할 수 있어요. 백엔드 서비스는 db라는 서비스 이름을 통해 내부적으로 연결되기 때문에 외부 포트 매핑은 필수는 아닙니다.

MySQL 데이터베이스 컨테이너 설정 예시 (간략)

MySQL을 사용하고 싶다면 image만 변경하고 환경 변수를 MySQL에 맞게 설정해 주면 됩니다. 구조는 PostgreSQL과 거의 동일해요.


# docker-compose.yml (MySQL DB 서비스 부분)

services:
  # ...

  db:
    image: mysql:8.0 # MySQL 8.0 버전 이미지 사용
    environment:
      MYSQL_ROOT_PASSWORD: root_password # root 계정 비밀번호
      MYSQL_DATABASE: mydatabase # 데이터베이스 이름
      MYSQL_USER: user # 사용자 이름
      MYSQL_PASSWORD: password # 비밀번호
    volumes:
      - db_data:/var/lib/mysql # MySQL 데이터 영속성을 위한 볼륨 마운트
    ports:
      - "3306:3306" # (선택 사항) 호스트에서 DB에 직접 접근하려면 포트 매핑
    restart: always

volumes:
  db_data:

보시는 것처럼 Docker Compose는 데이터베이스 종류에 상관없이 선언적인 방식으로 환경을 구성할 수 있게 해줍니다. 이제 백엔드와 데이터베이스가 준비되었으니, 마지막으로 프론트엔드 서비스를 추가해 볼까요?


프론트엔드 서비스 (React/Vue) 컨테이너화 및 백엔드 연동

이제 백엔드 API와 통신할 프론트엔드 서비스를 컨테이너화할 차례입니다. 여기서는 React 개발 서버를 예시로 들고, 백엔드와의 연동 방법을 자세히 설명할게요.

React 프론트엔드 컨테이너 설정 예시

React 프로젝트가 frontend 폴더 안에 있다고 가정해 봅시다. 개발 환경에서는 보통 npm startyarn start로 개발 서버를 띄우는데요. 이를 컨테이너로 감싸는 Dockerfile은 다음과 같이 작성할 수 있습니다.


# frontend/Dockerfile

# 1. Node.js 공식 이미지를 기반으로 사용 (Node.js 18 버전)
FROM node:18-alpine

# 2. 컨테이너 내부의 작업 디렉토리 설정
WORKDIR /app

# 3. package.json과 package-lock.json 파일을 복사하여 의존성 캐싱 활용
COPY package*.json ./

# 4. 프로젝트 의존성 설치
RUN npm install

# 5. 모든 소스 코드를 컨테이너의 /app 디렉토리로 복사
COPY . .

# 6. React 개발 서버의 기본 포트 명시
EXPOSE 3000

# 7. 애플리케이션 실행 명령어 (개발 서버 실행)
CMD ["npm", "start"]

Dockerfile은 Node.js 기반의 React 개발 서버를 실행하기 위한 이미지를 만듭니다. 이제 docker-compose.yml 파일에 frontend 서비스를 추가해 봅시다.


# docker-compose.yml (frontend 서비스 부분)

version: '3.8'

services:
  # ... (이전 백엔드, DB 서비스)

  frontend:
    build: ./frontend # frontend 폴더의 Dockerfile을 사용하여 이미지 빌드
    ports:
      - "3001:3000" # 호스트의 3001번 포트를 컨테이너의 3000번 포트에 연결
    volumes:
      - ./frontend:/app # 호스트의 ./frontend 폴더를 컨테이너의 /app에 마운트
      - /app/node_modules # node_modules 폴더는 볼륨에서 제외
    environment:
      # 백엔드 API 주소를 환경 변수로 전달 (backend는 docker-compose.yml에 정의된 서비스 이름)
      REACT_APP_API_URL: http://backend:3000
    depends_on:
      - backend # 백엔드 서비스가 먼저 시작되어야 함을 명시
    restart: always

여기서 주목할 점은 ports: - "3001:3000"environment: REACT_APP_API_URL: http://backend:3000 부분이에요.

  • 포트 매핑 (3001:3000): React 개발 서버는 기본적으로 3000번 포트를 사용하죠. 하지만 백엔드도 3000번 포트를 사용할 수 있으므로, 프론트엔드는 호스트의 3001번 포트에 매핑하여 충돌을 피했습니다. 이렇게 하면 웹 브라우저에서 http://localhost:3001로 프론트엔드에 접근할 수 있습니다.
  • 백엔드와의 통신 (REACT_APP_API_URL): 프론트엔드 애플리케이션이 백엔드 API를 호출할 때, http://backend:3000이라는 주소를 사용하도록 환경 변수를 설정했습니다. 여기서 backenddocker-compose.yml에 정의된 백엔드 서비스의 이름이에요. Docker Compose는 서비스 이름으로 내부 DNS를 제공하기 때문에, 같은 Compose 네트워크 내에서는 서비스 이름만으로 서로 통신할 수 있습니다. 즉, http://localhost:3000 대신 http://backend:3000을 사용해야 컨테이너 간의 통신이 원활하게 이루어집니다.

만약 Vue.js나 Angular를 사용하신다면, Dockerfile의 내용과 CMD 명령어, 그리고 환경 변수 설정 방식만 해당 프레임워크에 맞게 바꿔주시면 됩니다. 예를 들어, Vue.js는 VUE_APP_API_URL 같은 환경 변수를 사용하겠죠.

이제 백엔드, 데이터베이스, 프론트엔드 서비스가 모두 Docker Compose 파일에 정의되었습니다. 다음 섹션에서는 이 모든 서비스를 한 번에 관리하고 실행하는 방법을 알아볼게요!


Docker Compose를 활용한 로컬 개발 환경 구축: 백엔드, 프론트엔드, 데이터베이스 연동 가이드 - stones, waterfalls, balance, rock balancing, stack, stone balancing, rock stacking, stone stacking, stone stack, stack of stones, stones, stones, waterfalls, balance, balance, balance, balance, balance

Image by Cao135 on Pixabay

Docker Compose로 모든 서비스 한 번에 관리하기

자, 이제 백엔드, 프론트엔드, 데이터베이스 서비스를 모두 포함하는 docker-compose.yml 파일이 완성되었을 거예요. 하나의 파일로 모든 서비스를 정의했으니, 이젠 아주 간단한 명령어로 이 모든 서비스를 한 번에 띄우고 관리할 수 있습니다.

전체 `docker-compose.yml` 파일 합치기

지금까지 작성했던 각 서비스의 내용을 종합하여 하나의 docker-compose.yml 파일을 만들어 봅시다. 프로젝트의 루트 디렉토리에 이 파일을 저장하세요.


# docker-compose.yml (최종 통합본)

version: '3.8'

services:
  # 백엔드 서비스 (Node.js 예시)
  backend:
    build: ./backend
    ports:
      - "3000:3000"
    volumes:
      - ./backend:/app
      - /app/node_modules
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://user:password@db:5432/mydatabase
    depends_on:
      - db
    restart: always

  # 데이터베이스 서비스 (PostgreSQL 예시)
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: mydatabase
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432" # (선택 사항)
    restart: always

  # 프론트엔드 서비스 (React 예시)
  frontend:
    build: ./frontend
    ports:
      - "3001:3000" # 호스트 3001 -> 컨테이너 3000
    volumes:
      - ./frontend:/app
      - /app/node_modules
    environment:
      REACT_APP_API_URL: http://backend:3000
    depends_on:
      - backend
    restart: always

volumes: # 명시적 볼륨 정의
  db_data:

이렇게 하나의 파일로 모든 서비스를 정의하면, 어떤 운영체제에서든, 어떤 개발자든 이 파일 하나만 가지고 동일한 개발 환경을 즉시 구축할 수 있게 됩니다. 정말 편리하죠?

주요 Docker Compose 명령어

이제 이 docker-compose.yml 파일을 사용하여 서비스를 관리하는 주요 명령어들을 알아볼게요. 모든 명령어는 docker-compose.yml 파일이 있는 디렉토리에서 실행해야 합니다.

  • 모든 서비스 실행하기: docker compose up
    
    docker compose up
            
    이 명령어는 docker-compose.yml 파일에 정의된 모든 서비스를 백그라운드(detached mode)가 아닌 포그라운드에서 실행하고, 컨테이너의 로그를 터미널에 출력합니다. 처음 실행할 때는 이미지를 빌드하고 다운로드하는 시간이 좀 걸릴 수 있어요. depends_on 설정에 따라 데이터베이스부터 백엔드, 프론트엔드 순으로 실행될 겁니다.
  • 모든 서비스 백그라운드에서 실행하기: docker compose up -d
    
    docker compose up -d
            
    개발 시에는 주로 이 명령어를 사용하게 될 거예요. -d 옵션은 "detached mode"를 의미하며, 모든 서비스를 백그라운드에서 실행하고 터미널을 다시 사용할 수 있게 해줍니다. 이제 웹 브라우저에서 http://localhost:3001로 접속하면 프론트엔드 애플리케이션을 볼 수 있고, 백엔드는 http://localhost:3000으로 접근 가능할 겁니다.
  • 실행 중인 서비스 목록 확인: docker compose ps
    
    docker compose ps
            
    현재 Docker Compose 프로젝트에서 어떤 서비스들이 실행 중인지, 어떤 포트가 매핑되었는지 등을 확인할 수 있습니다.
  • 서비스 로그 확인: docker compose logs
    
    docker compose logs backend # backend 서비스 로그만 확인
    docker compose logs -f # 모든 서비스의 로그를 실시간으로 스트리밍
            
    특정 서비스의 로그를 확인하거나, -f (follow) 옵션을 사용하여 실시간으로 모든 서비스의 로그를 확인할 수 있습니다. 개발 중에 문제가 발생했을 때 디버깅에 아주 유용하죠.
  • 서비스 중지 및 컨테이너 제거: docker compose down
    
    docker compose down
            
    이 명령어는 docker-compose.yml 파일에 정의된 모든 서비스의 컨테이너를 중지하고 제거합니다. 이때, 명명된 볼륨(예: db_data)은 기본적으로 제거되지 않으므로 데이터는 보존됩니다. 만약 볼륨까지 완전히 제거하고 싶다면 docker compose down -v 명령어를 사용하세요.
  • 컨테이너 내부로 접속하기: docker compose exec
    
    docker compose exec backend sh # backend 컨테이너 내부로 쉘 접속
    docker compose exec db psql -U user mydatabase # db 컨테이너에서 psql 명령 실행
            
    실행 중인 컨테이너 내부에서 명령을 실행하거나 쉘에 접속하여 디버깅이나 수동 작업을 할 때 유용합니다.

추가 팁: `.env` 파일을 활용한 환경 변수 관리

docker-compose.yml 파일에 직접 민감한 정보(비밀번호 등)를 입력하는 것은 보안상 좋지 않습니다. 이때 .env 파일을 활용하면 훨씬 안전하고 유연하게 환경 변수를 관리할 수 있어요.

프로젝트 루트 디렉토리에 .env 파일을 만들고 다음과 같이 작성해 보세요.


# .env

POSTGRES_DB=mydatabase
POSTGRES_USER=user
POSTGRES_PASSWORD=password
REACT_APP_API_URL=http://backend:3000

그리고 docker-compose.yml 파일에서는 ${변수명} 형태로 이 변수들을 참조할 수 있습니다.


# docker-compose.yml (환경 변수 .env 파일 참조 예시)

version: '3.8'

services:
  backend:
    # ...
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    # ...

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    # ...

  frontend:
    # ...
    environment:
      REACT_APP_API_URL: ${REACT_APP_API_URL}
    # ...

이렇게 하면 .env 파일은 Git에 커밋하지 않고 각 개발자가 로컬에서 관리할 수 있게 되어 보안성도 높이고, 환경에 따라 유연하게 값을 변경할 수 있게 됩니다. docker compose up 명령어를 실행하면 Docker Compose가 자동으로 .env 파일을 읽어 변수를 적용해 줍니다.

이제 여러분은 Docker Compose를 활용하여 백엔드, 프론트엔드, 데이터베이스로 구성된 풀스택 로컬 개발 환경을 완벽하게 구축하고 관리할 수 있게 되었습니다! 🎉


마무리: 안정적인 개발 환경, 생산성의 시작!

오늘 우리는 Docker Compose를 활용해 백엔드, 프론트엔드, 데이터베이스까지 아우르는 풀스택 로컬 개발 환경을 구축하는 방법을 상세히 살펴보았습니다. 처음에 복잡하게 느껴질 수도 있지만, 한 번 제대로 설정해두면 그 어떤 개발 환경보다 일관되고, 이식성이 높으며, 효율적인 개발 워크플로우를 경험하게 되실 거예요.

Docker Compose는 더 이상 "제 컴퓨터에서는 되는데요?"라는 말로 고통받지 않게 해주고, 새로운 팀원이 합류했을 때 몇 줄의 명령어로 바로 개발을 시작할 수 있게 해줍니다. 이는 개발 팀 전체의 생산성을 크게 향상시키는 강력한 밑거름이 될 겁니다.

이 글에서 다룬 내용을 바탕으로 여러분의 프로젝트에 맞는 docker-compose.yml 파일을 직접 작성하고 실행해 보세요. 분명 놀라운 변화를 느끼실 겁니다. 안정적인 개발 환경은 즐거운 개발 경험과 높은 생산성의 시작이니까요!

이 글이 여러분의 개발 여정에 조금이나마 도움이 되었기를 바라며, Docker Compose와 함께 더욱 멋진 애플리케이션을 만들어나가시길 응원합니다!

혹시 Docker Compose를 사용하면서 궁금한 점이나 팁이 있다면 언제든지 댓글로 남겨주세요. 함께 고민하고 배우는 시간이 되었으면 좋겠습니다. 감사합니다! 😊

📌 함께 읽으면 좋은 글

  • [튜토리얼] OpenAPI와 Swagger를 활용한 REST API 문서 자동화 및 효율적인 테스트 전략
  • [튜토리얼] Playwright를 활용한 웹 E2E 테스트 자동화 환경 완벽 구축 가이드
  • [개발 도구] 강력한 개발 터미널 환경 구축: Zsh, Oh My Zsh, Powerlevel10k 설정 및 활용 가이드

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.

반응형