화. 8월 12th, 2025

G: 안녕하세요, 열정적인 개발자 여러분! 👋 혹시 이런 경험 있으신가요? 🤔

  • 새로운 프로젝트에 합류했는데, 온갖 의존성 때문에 환경 설정에만 꼬박 하루가 걸린 경험… 🤯
  • “제 노트북에서는 잘 되는데요?” 하는 말에 속으로 피눈물을 흘렸던 경험… 😭
  • 팀원마다 개발 환경이 달라서 발생하는 알 수 없는 버그에 시달린 경험… 🐛

개발자라면 누구나 한 번쯤 겪어봤을, 바로 이 ‘개발 환경 설정의 지옥’에서 벗어나고 싶으시죠? 오늘 그 해답을 들고 왔습니다! 바로 Docker(도커) 입니다. 🐳

Docker를 활용하면 개발 환경을 표준화하고, 복잡한 설정 과정을 단 몇 줄의 코드로 자동화하며, 팀 생산성을 혁신적으로 끌어올릴 수 있습니다. 자, 그럼 지금부터 Docker와 함께 개발 생산성 200% UP!의 비결을 파헤쳐 볼까요? 🚀


1. 왜 Docker를 사용해야 할까요? (이유 없는 고통은 이제 그만! 🙅‍♀️)

Docker가 왜 개발 환경의 필수템이 되었는지, 그 이유를 몇 가지 핵심적인 문제점과 해결책으로 정리해 보았습니다.

1.1. “제 노트북에서는 되는데요?” (환경 일관성 확보!) 🛡️

  • 문제점: 개발자마다 운영체제(Windows, macOS, Linux), 설치된 라이브러리 버전, 심지어 시스템 설정까지 모두 다릅니다. 이로 인해 ‘내 환경에서는 잘 작동하는데 다른 팀원 컴퓨터에서는 안 되는’ 황당한 상황이 발생하곤 합니다.
  • Docker의 해결책: Docker는 애플리케이션과 그에 필요한 모든 의존성(라이브러리, 설정 파일 등)을 하나의 경량화된 독립적인 패키지, 즉 컨테이너(Container) 안에 담아 격리시킵니다. 이 컨테이너는 어떤 운영체제에서도 동일하게 작동하도록 보장합니다. “어디서든 동일하게 실행됩니다!” 🌍✨

1.2. 지긋지긋한 의존성 지옥 (Dependency Hell 탈출! 😈➡️😇)

  • 문제점: Node.js 앱을 개발하는데 특정 버전의 NPM이 필요하고, Python 앱을 돌리려면 또 다른 버전의 pip가 필요하며, 동시에 MySQL과 Redis도 설치해야 합니다. 이 모든 것을 로컬에 설치하고 버전 충돌까지 관리하는 것은 정말 고통스러운 일입니다.
  • Docker의 해결책: 각 프로젝트에 필요한 모든 의존성을 해당 프로젝트의 Docker 이미지 내에 포함시킬 수 있습니다. 시스템 전반에 걸쳐 패키지를 설치하거나 충돌을 걱정할 필요 없이, 프로젝트별로 완벽히 격리된 환경에서 개발할 수 있습니다. 예를 들어, 어떤 프로젝트는 Node.js 14를, 다른 프로젝트는 Node.js 18을 사용해도 충돌 없이 개발이 가능합니다! 🤩

1.3. 신규 팀원 온보딩 시간 단축 (새로운 개발자는 바로 코딩으로! ⏱️)

  • 문제점: 새로운 팀원이 합류하면, 프로젝트 개발 환경을 설정하는 데만 며칠이 걸리는 경우가 허다합니다. 복잡한 매뉴얼을 보며 수많은 패키지를 설치하고, 경로를 설정하고, 데이터베이스를 구성하는 것은 엄청난 시간 낭비입니다.
  • Docker의 해결책: Docker를 사용하면 docker-compose up 명령 한 줄로 모든 개발 환경이 즉시 구축됩니다. 마치 새로운 컴퓨터를 사면 앱스토어에서 앱을 설치하듯, 필요한 모든 것이 미리 준비된 ‘가상 개발 환경’을 제공받는 것과 같습니다. 신규 팀원은 바로 코드 작성에 돌입할 수 있습니다! 🏃‍♀️💨

1.4. 경량화된 가상 환경 (리소스는 아끼고, 효율은 높이고! 🌱)

  • 문제점: 가상 머신(VM)은 완벽히 격리된 환경을 제공하지만, OS 전체를 가상화하므로 무겁고 많은 시스템 자원을 소모합니다. 작은 서비스 하나를 테스트하려고 무거운 VM을 띄우는 것은 비효율적입니다.
  • Docker의 해결책: Docker 컨테이너는 가상 머신과 달리 호스트 OS의 커널을 공유하며, 필요한 라이브러리만 포함하므로 훨씬 가볍고 빠르게 시작됩니다. 적은 리소스로도 여러 개의 독립적인 개발 환경을 동시에 실행할 수 있습니다. 덕분에 노트북 팬이 굉음을 내는 일도 줄어들겠죠? 🌬️💻

1.5. 운영 환경과의 일치 (배포 실패는 이제 그만! ✅)

  • 문제점: 개발 환경에서 잘 작동하던 코드가 테스트 환경이나 실제 운영 환경에서는 예상치 못한 버그를 일으키는 경우가 있습니다. 이는 주로 개발 환경과 운영 환경의 설정 불일치에서 발생합니다.
  • Docker의 해결책: Docker는 개발, 테스트, 운영 환경 모두에서 동일한 컨테이너 이미지를 사용할 수 있도록 합니다. ‘내 환경에서 되는 것’이 ‘운영 환경에서도 되는 것’을 보장하여, 배포 안정성을 획기적으로 높여줍니다. 개발팀과 운영팀 간의 불필요한 마찰을 줄일 수 있습니다. 🤝

2. Docker, 이것부터 시작하자! (기본 개념 익히기 📚)

Docker를 본격적으로 사용하기 전에, 몇 가지 핵심 개념을 이해하고 가면 훨씬 수월합니다.

2.1. Docker란? (아주 쉽게 설명하면 📦)

Docker는 애플리케이션을 컨테이너라는 독립적인 환경에서 실행할 수 있도록 돕는 플랫폼입니다. 마치 배송 상자(컨테이너)에 제품(앱)과 필요한 모든 부품(의존성)을 담아, 어디로든 안전하고 효율적으로 운송할 수 있게 해주는 것과 같습니다.

2.2. 핵심 개념 (이것만 알아도 반은 성공! ✨)

  • Docker Image (도커 이미지):

    • 개념: 컨테이너를 생성하기 위한 ‘설계도’ 또는 ‘템플릿’입니다. 애플리케이션 코드, 런타임, 시스템 도구, 라이브러리, 설정 파일 등 컨테이너를 실행하는 데 필요한 모든 것을 포함하는 정적인 파일 묶음입니다.
    • 예시: “Node.js 16과 Nginx가 설치된 리눅스 운영체제 기반의 이미지” 🏗️
    • 특징: 한 번 빌드되면 불변(Immutable)하며, 여러 컨테이너를 생성할 수 있습니다.
  • Docker Container (도커 컨테이너):

    • 개념: Docker 이미지를 기반으로 실행되는 ‘실제 애플리케이션 인스턴스’입니다. 이미지를 통해 생성되며, 격리된 공간에서 애플리케이션을 실행합니다.
    • 예시: “현재 실행 중인 나의 Node.js 웹 서버 인스턴스” 🚀
    • 특징: 독립적이고 휴대 가능하며, 호스트 시스템에 영향을 주지 않습니다.
  • Dockerfile (도커파일):

    • 개념: Docker 이미지를 ‘어떻게 만들지’ 정의하는 텍스트 파일입니다. 이미지 빌드 명령어를 한 줄씩 기록합니다.
    • 예시:
      # Node.js 16 이미지를 기반으로 시작
      FROM node:16-alpine
      # 작업 디렉토리 설정
      WORKDIR /app
      # 현재 디렉토리의 파일을 컨테이너의 /app으로 복사
      COPY . .
      # 의존성 설치
      RUN npm install
      # 3000번 포트 노출
      EXPOSE 3000
      # 애플리케이션 실행 명령어
      CMD ["npm", "start"]
    • 특징: 사람이 읽을 수 있는 간단한 명령어로 이미지를 자동화하여 생성합니다. 📝
  • Docker Hub (도커 허브):

    • 개념: Docker 이미지들을 저장하고 공유하는 ‘공식 레지스트리’입니다. 마치 GitHub가 코드 저장소인 것처럼, Docker Hub는 이미지 저장소입니다.
    • 예시: node, ubuntu, mysql 등 공식 이미지들을 다운로드하거나, 내가 만든 이미지를 업로드하여 공유할 수 있습니다. ☁️
    • 특징: 전 세계 개발자들이 만든 수많은 이미지를 활용하여 개발을 시작할 수 있습니다.
  • Docker Compose (도커 컴포즈):

    • 개념: 여러 개의 Docker 컨테이너를 ‘하나의 서비스’처럼 정의하고 관리할 수 있게 해주는 도구입니다. 웹 애플리케이션, 데이터베이스, 캐시 서버 등 여러 서비스가 함께 필요한 복합 애플리케이션을 쉽게 관리할 수 있습니다.
    • 예시: 웹 서버, 데이터베이스, Redis 캐시를 한 번에 실행하고 관리. 👯
    • 특징: docker-compose.yml이라는 YAML 파일을 사용하여 서비스들을 정의하고, docker-compose up 명령 한 줄로 모든 서비스를 한 번에 시작할 수 있습니다.

2.3. Docker 설치하기 (준비 완료! 🔧)

Docker는 각 운영체제에 맞는 설치 파일인 Docker Desktop을 제공합니다. Docker 공식 홈페이지에서 다운로드하여 쉽게 설치할 수 있습니다.


3. Docker로 개발 환경 구축하기 (실전 돌입! 🧑‍💻)

이제 실제로 Docker를 사용하여 개발 환경을 구축하는 방법을 알아보겠습니다. 가장 기본적인 단일 앱 컨테이너부터, 여러 서비스가 묶인 복합 환경까지 다뤄봅니다.

3.1. Dockerfile 작성하기 (나만의 개발 환경 설계도 그리기 🎨)

먼저 간단한 Node.js 웹 애플리케이션을 Docker 컨테이너로 만들어 보겠습니다.

프로젝트 구조:

my-node-app/
├── Dockerfile
├── package.json
├── package-lock.json
└── app.js

app.js 내용:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello from Docker! 🐳✨');
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

package.json 내용:

{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "A simple Node.js app for Docker demo",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

Dockerfile 작성하기:

# 1. 어떤 기본 이미지를 사용할 것인가? (운영체제 + Node.js 런타임)
FROM node:16-alpine

# 2. 컨테이너 내부에서 작업할 디렉토리를 설정 (모든 명령은 이 디렉토리에서 실행)
WORKDIR /app

# 3. 프로젝트 의존성 파일을 컨테이너로 복사 (캐싱 활용을 위해 먼저 복사)
COPY package*.json ./

# 4. 의존성 설치 (npm install)
RUN npm install

# 5. 나머지 소스 코드 파일을 컨테이너로 복사
COPY . .

# 6. 컨테이너가 3000번 포트를 외부에 노출할 것임을 명시 (정보성)
EXPOSE 3000

# 7. 컨테이너가 시작될 때 실행할 명령어 (애플리케이션 실행)
CMD ["npm", "start"]

Dockerfile 설명:

  • FROM node:16-alpine: node:16-alpine 이미지를 기반으로 시작합니다. alpine은 경량 Linux 배포판으로, 이미지를 작게 만드는데 유용합니다.
  • WORKDIR /app: 컨테이너 내부의 /app 디렉토리를 작업 디렉토리로 설정합니다. 이후 모든 명령어는 이 디렉토리에서 실행됩니다.
  • COPY package*.json ./: 호스트(로컬)의 package.jsonpackage-lock.json 파일을 컨테이너의 현재 작업 디렉토리(/app)로 복사합니다. 이 파일들은 의존성 설치에 필요합니다.
  • RUN npm install: 컨테이너 내부에서 npm install 명령어를 실행하여 Node.js 의존성들을 설치합니다.
  • COPY . .: 호스트의 현재 디렉토리(루트)에 있는 모든 파일(소스 코드 포함)을 컨테이너의 /app 디렉토리로 복사합니다. COPY package*.json을 먼저 한 이유는, 의존성 파일이 변경되지 않았다면 npm install 레이어를 캐싱하여 빌드 속도를 높일 수 있기 때문입니다.
  • EXPOSE 3000: 이 컨테이너가 3000번 포트를 외부에 노출할 것이라고 선언합니다. 이는 문서화 목적이 크며, 실제로 외부에서 접근하려면 docker run 시 포트 매핑을 해야 합니다.
  • CMD ["npm", "start"]: 컨테이너가 시작될 때 실행될 명령어입니다. npm startapp.js를 실행합니다.

이미지 빌드 및 컨테이너 실행:

  1. 이미지 빌드: my-node-app 디렉토리로 이동하여 다음 명령어를 실행합니다.

    docker build -t my-node-app:1.0 .
    • -t my-node-app:1.0: 빌드된 이미지에 my-node-app이라는 이름과 1.0이라는 태그를 부여합니다.
    • .: Dockerfile이 현재 디렉토리에 있음을 의미합니다.
  2. 컨테이너 실행:

    docker run -p 80:3000 my-node-app:1.0
    • -p 80:3000: 호스트(로컬 컴퓨터)의 80번 포트를 컨테이너의 3000번 포트에 연결(매핑)합니다. 이제 http://localhost/로 접속하면 컨테이너 내부의 앱에 접근할 수 있습니다.
    • my-node-app:1.0: 실행할 이미지의 이름과 태그입니다.

이제 웹 브라우저에서 http://localhost/로 접속하면 “Hello from Docker! 🐳✨” 메시지를 볼 수 있습니다!

3.2. Docker Compose로 멀티 서비스 관리하기 (복합 환경 구축의 마법! 🪄)

실제 프로젝트에서는 웹 서버뿐만 아니라 데이터베이스, 캐시 서버 등 여러 서비스가 함께 필요합니다. 이때 Docker Compose가 빛을 발합니다.

Node.js 웹 앱과 PostgreSQL 데이터베이스가 함께 필요한 환경을 구축해 봅시다.

프로젝트 구조:

my-node-app-with-db/
├── docker-compose.yml
├── web/
│   ├── Dockerfile (Node.js 앱용)
│   ├── package.json
│   └── app.js (PostgreSQL 연결 코드 추가)
└── db_data/  (PostgreSQL 데이터가 저장될 볼륨)

web/app.js (PostgreSQL 연결 코드 추가):

const express = require('express');
const { Pool } = require('pg'); // PostgreSQL 드라이버
const app = express();
const port = 3000;

// PostgreSQL 연결 설정
const pool = new Pool({
  user: process.env.PGUSER,
  host: process.env.PGHOST,
  database: process.env.PGDATABASE,
  password: process.env.PGPASSWORD,
  port: process.env.PGPORT,
});

app.get('/', async (req, res) => {
  try {
    const result = await pool.query('SELECT NOW() as now');
    res.send(`Hello from Docker! Current time from DB: ${result.rows[0].now} 🐳✨`);
  } catch (err) {
    console.error(err);
    res.status(500).send('Database connection error!');
  }
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

web/Dockerfile: (이전과 동일)

FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

docker-compose.yml 작성하기:

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

services:
  # 1. 웹 애플리케이션 서비스 정의
  web:
    build: ./web # web 디렉토리의 Dockerfile을 사용하여 이미지 빌드
    ports:
      - "80:3000" # 호스트 80번 포트를 컨테이너 3000번 포트에 매핑
    volumes:
      - ./web:/app # 소스 코드를 호스트와 컨테이너 간에 동기화 (개발 시 필수!)
      - /app/node_modules # node_modules는 호스트에 복사되지 않도록 제외
    environment: # 환경 변수 설정 (DB 연결 정보)
      PGUSER: user
      PGHOST: db # db 서비스의 이름으로 접근
      PGDATABASE: mydatabase
      PGPASSWORD: password
      PGPORT: 5432
    depends_on: # web 서비스가 db 서비스보다 먼저 시작되도록 설정
      - db
    networks: # 서비스들이 동일한 네트워크에 속하도록 지정
      - my_network

  # 2. PostgreSQL 데이터베이스 서비스 정의
  db:
    image: postgres:13 # PostgreSQL 13 공식 이미지 사용
    environment: # 환경 변수 설정 (DB 초기 설정)
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydatabase
    volumes:
      - db_data:/var/lib/postgresql/data # DB 데이터 지속성을 위한 볼륨 마운트
    networks:
      - my_network

volumes:
  db_data: # db_data라는 이름의 볼륨 정의

networks:
  my_network: # my_network라는 이름의 네트워크 정의

docker-compose.yml 설명:

  • version: '3.8': Docker Compose 파일의 버전을 명시합니다.
  • services:: 여러 개의 서비스를 정의하는 섹션입니다.
    • web:: web이라는 이름의 서비스를 정의합니다.
      • build: ./web: web 디렉토리의 Dockerfile을 사용하여 이미지를 빌드합니다.
      • ports: - "80:3000": 호스트의 80번 포트를 web 컨테이너의 3000번 포트에 연결합니다.
      • volumes:: 데이터 볼륨을 설정합니다.
        • - ./web:/app: 호스트의 ./web 디렉토리를 컨테이너의 /app 디렉토리에 마운트합니다. 이는 개발 중 소스 코드 변경 시 컨테이너를 다시 빌드할 필요 없이 즉시 반영되게 합니다 (핫 리로드).
        • - /app/node_modules: node_modules 디렉토리는 컨테이너 내부의 것을 사용하도록 호스트와의 마운트에서 제외합니다.
      • environment:: 컨테이너 내부에서 사용할 환경 변수를 설정합니다. PGHOST: dbdb 서비스의 호스트 이름을 나타내며, Docker Compose는 서비스 이름을 자동으로 DNS처럼 해석하여 연결해 줍니다.
      • depends_on: - db: web 서비스가 db 서비스가 시작된 후에 시작되도록 의존성을 설정합니다. (단, db 서비스가 완전히 준비될 때까지 기다리는 것은 아님)
      • networks: - my_network: web 서비스를 my_network라는 가상 네트워크에 연결합니다.
    • db:: db라는 이름의 서비스를 정의합니다.
      • image: postgres:13: Docker Hub에서 postgres:13 공식 이미지를 가져와 사용합니다.
      • environment:: PostgreSQL 컨테이너의 초기 설정(사용자, 비밀번호, 데이터베이스 이름)을 위한 환경 변수를 설정합니다.
      • volumes: - db_data:/var/lib/postgresql/data: db_data라는 이름의 명명된 볼륨을 사용하여 PostgreSQL 데이터가 컨테이너가 삭제되어도 유지되도록 합니다.
      • networks: - my_network: db 서비스도 my_network에 연결하여 web 서비스와 통신할 수 있도록 합니다.
  • volumes:: 명명된 볼륨(db_data)을 정의하는 섹션입니다.
  • networks:: 사용자 정의 네트워크(my_network)를 정의하는 섹션입니다.

컨테이너 실행 및 관리:

my-node-app-with-db 디렉토리에서 다음 명령어를 실행합니다.

  1. 모든 서비스 빌드 및 실행 (백그라운드 모드):

    docker compose up -d
    • -d: Detached 모드로 백그라운드에서 실행합니다. 터미널이 잠기지 않습니다.
  2. 로그 확인:

    docker compose logs -f
    • -f: Follow 모드로 실시간 로그를 확인합니다. Ctrl+C로 종료할 수 있습니다.
  3. 특정 서비스 실행:

    docker compose start web # web 서비스만 시작
    docker compose stop db  # db 서비스만 중지
  4. 컨테이너 내부 명령 실행:

    docker compose exec web bash # web 컨테이너 내부로 들어가 bash 쉘 실행
    docker compose exec db psql -U user mydatabase # db 컨테이너에서 psql 명령 실행
  5. 모든 서비스 중지 및 컨테이너 삭제:

    docker compose down
    • 컨테이너, 네트워크 등 모든 리소스를 삭제합니다. 볼륨은 기본적으로 삭제되지 않습니다.
  6. 모든 서비스 중지 및 컨테이너, 볼륨까지 모두 삭제:

    docker compose down --volumes
    • 개발 환경을 완전히 초기화하고 싶을 때 사용합니다.

이제 웹 브라우저에서 http://localhost/로 접속하면 “Hello from Docker! Current time from DB: …” 메시지를 볼 수 있습니다! 🚀 DB 연결까지 완벽하네요!

3.3. 개발 시 유용한 Docker 팁 (생산성 UP! 팁들 ✨)

Docker를 개발 환경에서 더욱 효율적으로 활용하기 위한 몇 가지 팁입니다.

  • 볼륨 마운트를 통한 실시간 코드 변경 반영 (Hot Reload) 🔄:docker-compose.yml에서 volumes: - ./web:/app과 같이 설정하면, 호스트(로컬 컴퓨터)의 소스 코드 변경이 컨테이너 내부에 즉시 반영됩니다. Node.js, Python 등의 개발 서버가 파일 변경을 감지하여 자동으로 재시작되도록 설정하면, 마치 로컬에서 개발하는 것처럼 편리하게 작업할 수 있습니다. node_modules 같은 불필요한 의존성 폴더는 - /app/node_modules처럼 따로 제외하는 것이 좋습니다.

  • .dockerignore 파일 활용 🗑️: Git의 .gitignore와 유사하게, .dockerignore 파일을 사용하여 이미지 빌드 시 컨테이너로 복사할 필요 없는 파일이나 디렉토리(예: node_modules, .git, .env)를 제외할 수 있습니다. 이는 이미지 크기를 줄이고, 빌드 속도를 높이는 데 매우 효과적입니다.

    # .dockerignore 예시
    node_modules
    .git
    .env
    temp/
    *.log
  • 환경 변수를 통한 설정 분리 🔑: 민감한 정보(DB 비밀번호)나 환경별 설정값(API 키, 개발/운영 모드)은 Dockerfile에 직접 하드코딩하기보다 환경 변수를 사용하는 것이 좋습니다.

    • Dockerfile에서 ENV 명령으로 기본값 설정.
    • docker run -e MY_VAR=value 또는 docker-compose.ymlenvironment 섹션에서 오버라이드.
    • dotenv 라이브러리처럼 .env 파일을 활용할 수도 있습니다 (단, .env.dockerignore에 추가하여 이미지에 포함되지 않도록 주의).
  • 멀티 스테이지 빌드 (Multi-stage Builds) 🏭: 단일 Dockerfile 내에서 여러 FROM 문을 사용하여 빌드 단계를 분리하는 기법입니다. 예를 들어, 빌드에 필요한 도구(컴파일러, 패키지 매니저 등)를 첫 번째 스테이지에서 사용하고, 최종 런타임 이미지에는 빌드된 결과물만 복사하여 최종 이미지 크기를 극적으로 줄일 수 있습니다. 특히 Go, Java, React/Vue 같은 프론트엔드 프로젝트 빌드에 유용합니다.

    # 1단계: 빌드 스테이지 (무거운 빌드 도구 포함)
    FROM node:16-alpine AS builder
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    RUN npm run build # React 앱 빌드 가정
    
    # 2단계: 최종 런타임 스테이지 (가벼운 Nginx만 포함)
    FROM nginx:alpine
    COPY --from=builder /app/build /usr/share/nginx/html # 빌드 결과만 복사
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]
  • Docker Desktop GUI 활용 🖥️: Docker Desktop은 실행 중인 컨테이너, 이미지, 볼륨, 네트워크 등을 시각적으로 확인하고 관리할 수 있는 GUI를 제공합니다. 컨테이너의 로그를 확인하거나, 특정 컨테이너를 시작/중지/삭제하는 등의 작업을 CLI 명령어 없이도 편리하게 할 수 있습니다. 초보자에게 특히 유용합니다.


4. 실제 시나리오: Docker로 풀스택 개발 환경 구축 (프론트 + 백 + DB 🧩)

조금 더 복잡한 시나리오로, React 프론트엔드 + Spring Boot 백엔드 + MySQL 데이터베이스로 구성된 풀스택 개발 환경을 Docker Compose로 구축해 보겠습니다.

프로젝트 구조:

my-fullstack-app/
├── docker-compose.yml
├── frontend/             # React 앱
│   ├── Dockerfile
│   ├── package.json
│   └── ... (React 소스 코드)
├── backend/              # Spring Boot 앱
│   ├── Dockerfile
│   ├── pom.xml
│   └── ... (Java 소스 코드)
└── mysql_data/           # MySQL 데이터 볼륨용 디렉토리

frontend/Dockerfile (React 앱):

# 빌드 스테이지
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 런타임 스테이지
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
  • npm run build는 React 앱을 빌드하여 정적 파일을 생성합니다.
  • Nginx는 빌드된 정적 파일을 서빙합니다.

backend/Dockerfile (Spring Boot 앱):

FROM openjdk:11-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar # Spring Boot 빌드 결과물 (jar 파일) 복사
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
  • target/*.jar는 Maven/Gradle 빌드 후 생성되는 실행 가능한 JAR 파일입니다. (이 Dockerfile은 이미 JAR 파일이 빌드되었다고 가정합니다. 실제 개발 환경에서는 Maven/Gradle을 컨테이너 내에서 빌드하는 멀티 스테이지 빌드도 고려할 수 있습니다.)

docker-compose.yml (풀스택 통합):

version: '3.8'

services:
  # 1. 프론트엔드 서비스 (React + Nginx)
  frontend:
    build: ./frontend
    ports:
      - "3000:80" # 로컬 3000번 포트로 React 앱 접근
    volumes:
      - ./frontend:/app # 소스 코드 동기화 (개발 시)
      - /app/node_modules # node_modules 제외
    networks:
      - my_fullstack_network

  # 2. 백엔드 서비스 (Spring Boot)
  backend:
    build: ./backend
    ports:
      - "8080:8080" # 로컬 8080번 포트로 Spring Boot 앱 접근
    volumes:
      # 개발 시 hot-reloading을 위해 소스 코드를 마운트할 수 있습니다.
      # Spring Boot DevTools 사용 시 유용.
      # - ./backend:/app
    environment: # 백엔드가 DB에 연결하기 위한 환경 변수
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/mydatabase?useSSL=false
      SPRING_DATASOURCE_USERNAME: user
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db # DB 서비스가 먼저 시작되어야 함
    networks:
      - my_fullstack_network

  # 3. 데이터베이스 서비스 (MySQL)
  db:
    image: mysql:8.0
    ports:
      - "3306:3306" # 로컬 3306번 포트로 MySQL 외부 접근 (선택 사항)
    environment:
      MYSQL_ROOT_PASSWORD: root_password # root 계정 비밀번호
      MYSQL_DATABASE: mydatabase        # 초기 생성될 데이터베이스
      MYSQL_USER: user                  # 초기 생성될 사용자
      MYSQL_PASSWORD: password          # 초기 생성될 사용자 비밀번호
    volumes:
      - mysql_data:/var/lib/mysql # DB 데이터 지속성을 위한 볼륨
    networks:
      - my_fullstack_network

volumes:
  mysql_data:

networks:
  my_fullstack_network:

실행 방법:

  1. Dockerfile이 있는 디렉토리에 소스 코드를 준비합니다. (Spring Boot는 mvn package 등으로 미리 .jar 파일을 생성해두세요.)
  2. my-fullstack-app 디렉토리로 이동합니다.
  3. docker compose up -d 명령을 실행합니다.

이제 다음과 같이 접속할 수 있습니다:

  • 프론트엔드 (React): http://localhost:3000 (Nginx가 React 빌드 결과 서빙)
  • 백엔드 (Spring Boot): http://localhost:8080 (백엔드 API 호출)

이처럼 Docker Compose를 활용하면 복잡한 풀스택 환경도 한 번에 구축하고 관리할 수 있습니다. 각 서비스는 독립적으로 작동하며, 필요한 경우 개별적으로 스케일링하거나 업데이트할 수 있습니다. 🥳


결론: Docker와 함께라면 개발은 즐겁다! 🎉

오늘 우리는 Docker가 어떻게 개발 생산성을 200% 이상 끌어올릴 수 있는지, 그 핵심 원리와 실질적인 사용 방법을 알아보았습니다.

  • 환경 일관성: “제 노트북에서는 되는데요?”는 이제 옛말! 🛡️
  • 쉬운 의존성 관리: 복잡한 설치 과정은 Dockerfiledocker-compose.yml에게 맡기세요! 😇
  • 빠른 온보딩: 신규 팀원은 바로 코딩 시작! 🚀
  • 운영 환경과의 일치: 배포는 이제 안정적으로! ✅
  • 리소스 효율성: 가볍고 빠르게! 🌱

Docker는 단순히 가상화 도구를 넘어, 현대 소프트웨어 개발의 필수적인 도구이자 문화로 자리 잡고 있습니다. 아직 Docker를 사용해보지 않았다면, 지금 바로 시작해보세요! 처음에는 약간의 학습 곡선이 있을 수 있지만, 일단 익숙해지면 개발의 자유와 효율성에 놀라게 될 것입니다.

여러분도 Docker와 함께 지긋지긋한 환경 설정에서 벗어나, 오직 코드에만 집중하는 즐거운 개발을 경험하시길 바랍니다! 궁금한 점이 있다면 언제든 댓글로 남겨주세요! 😊

해피 코딩! 💻✨

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다