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 공식 홈페이지에서 다운로드하여 쉽게 설치할 수 있습니다.
- Docker Desktop 다운로드: https://www.docker.com/products/docker-desktop/
- 설치 후 터미널에서
docker --version
과docker compose version
을 입력하여 정상적으로 설치되었는지 확인해 보세요! ✅
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.json
과package-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 start
는app.js
를 실행합니다.
이미지 빌드 및 컨테이너 실행:
-
이미지 빌드:
my-node-app
디렉토리로 이동하여 다음 명령어를 실행합니다.docker build -t my-node-app:1.0 .
-t my-node-app:1.0
: 빌드된 이미지에my-node-app
이라는 이름과1.0
이라는 태그를 부여합니다..
: Dockerfile이 현재 디렉토리에 있음을 의미합니다.
-
컨테이너 실행:
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: db
는db
서비스의 호스트 이름을 나타내며, 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
디렉토리에서 다음 명령어를 실행합니다.
-
모든 서비스 빌드 및 실행 (백그라운드 모드):
docker compose up -d
-d
: Detached 모드로 백그라운드에서 실행합니다. 터미널이 잠기지 않습니다.
-
로그 확인:
docker compose logs -f
-f
: Follow 모드로 실시간 로그를 확인합니다. Ctrl+C로 종료할 수 있습니다.
-
특정 서비스 실행:
docker compose start web # web 서비스만 시작 docker compose stop db # db 서비스만 중지
-
컨테이너 내부 명령 실행:
docker compose exec web bash # web 컨테이너 내부로 들어가 bash 쉘 실행 docker compose exec db psql -U user mydatabase # db 컨테이너에서 psql 명령 실행
-
모든 서비스 중지 및 컨테이너 삭제:
docker compose down
- 컨테이너, 네트워크 등 모든 리소스를 삭제합니다. 볼륨은 기본적으로 삭제되지 않습니다.
-
모든 서비스 중지 및 컨테이너, 볼륨까지 모두 삭제:
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.yml
의environment
섹션에서 오버라이드.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:
실행 방법:
- 각
Dockerfile
이 있는 디렉토리에 소스 코드를 준비합니다. (Spring Boot는mvn package
등으로 미리.jar
파일을 생성해두세요.) my-fullstack-app
디렉토리로 이동합니다.docker compose up -d
명령을 실행합니다.
이제 다음과 같이 접속할 수 있습니다:
- 프론트엔드 (React):
http://localhost:3000
(Nginx가 React 빌드 결과 서빙) - 백엔드 (Spring Boot):
http://localhost:8080
(백엔드 API 호출)
이처럼 Docker Compose를 활용하면 복잡한 풀스택 환경도 한 번에 구축하고 관리할 수 있습니다. 각 서비스는 독립적으로 작동하며, 필요한 경우 개별적으로 스케일링하거나 업데이트할 수 있습니다. 🥳
결론: Docker와 함께라면 개발은 즐겁다! 🎉
오늘 우리는 Docker가 어떻게 개발 생산성을 200% 이상 끌어올릴 수 있는지, 그 핵심 원리와 실질적인 사용 방법을 알아보았습니다.
- 환경 일관성: “제 노트북에서는 되는데요?”는 이제 옛말! 🛡️
- 쉬운 의존성 관리: 복잡한 설치 과정은
Dockerfile
과docker-compose.yml
에게 맡기세요! 😇 - 빠른 온보딩: 신규 팀원은 바로 코딩 시작! 🚀
- 운영 환경과의 일치: 배포는 이제 안정적으로! ✅
- 리소스 효율성: 가볍고 빠르게! 🌱
Docker는 단순히 가상화 도구를 넘어, 현대 소프트웨어 개발의 필수적인 도구이자 문화로 자리 잡고 있습니다. 아직 Docker를 사용해보지 않았다면, 지금 바로 시작해보세요! 처음에는 약간의 학습 곡선이 있을 수 있지만, 일단 익숙해지면 개발의 자유와 효율성에 놀라게 될 것입니다.
여러분도 Docker와 함께 지긋지긋한 환경 설정에서 벗어나, 오직 코드에만 집중하는 즐거운 개발을 경험하시길 바랍니다! 궁금한 점이 있다면 언제든 댓글로 남겨주세요! 😊
해피 코딩! 💻✨