내용이 길어 10-1과 10-2 두 개의 포스팅으로 나누어 설명합니다. 이번 강의에서는 실제로 풀스택 애플리케이션(백엔드, 프론트엔드, DB) 환경을 Docker를 통해 **개발용(로컬)**과 **프로덕션용(운영)**으로 구축해 봅니다.
10-1: Docker Compose 기반 로컬 개발 환경 구성
10-2: Swarm/Kubernetes 기반 프로덕션 환경 배포
🚀 1. 프로젝트 개요
- 목표: 간단한 SNS 형태의 풀스택 애플리케이션을 구성하여, Node.js(백엔드) + React(프론트엔드) + MySQL(데이터베이스) + **Redis(캐시)**로 이루어진 서비스를 Docker Compose 한 장으로 로컬에서 실행해 봅니다.
- 개발 편의: 로컬에서 소스 코드를 수정하면 즉시 반영되도록 바인드 마운트를 활용할 예정입니다.
📝 2. 디렉토리 구조 예시
my-sns-project/
├─ backend/
│ ├─ Dockerfile
│ ├─ package.json
│ ├─ app.js (예: Express.js 진입점)
│ └─ ...
├─ frontend/
│ ├─ Dockerfile
│ ├─ package.json
│ ├─ src/
│ └─ ...
├─ docker-compose.yml
├─ init.sql (MySQL 초기 스크립트)
└─ README.md
- backend: Node.js + Express.js 기반 간단한 API 서버
- frontend: React 기반 간단한 웹 프론트엔드
- init.sql: MySQL 초기 테이블 생성 및 샘플 데이터 삽입 스크립트
Tip: 실제 개발 환경
보통 프론트엔드와 백엔드 저장소를 분리할 수도 있지만, 여기서는 실습 편의상 하나의 리포지토리에 묶어서 진행합니다.
👨💻 3. 백엔드(Node.js) 설정
3.1 예시 app.js (간단한 SNS API)
// backend/app.js
const express = require('express');
const redis = require('redis');
const mysql = require('mysql2/promise');
const app = express();
app.use(express.json());
// MySQL 연결
let db;
async function initDB() {
db = await mysql.createConnection({
host: 'db', // docker-compose에서 정의한 서비스 이름
user: 'root',
password: 'secret',
database: 'sns_db'
});
}
// Redis 연결
const redisClient = redis.createClient({ url: 'redis://cache:6379' });
redisClient.on('error', (err) => console.error('Redis Error', err));
app.get('/api/posts', async (req, res) => {
try {
// 간단하게 DB 테이블에서 post 목록을 가져온다고 가정
const [rows] = await db.execute('SELECT * FROM posts');
res.json(rows);
} catch (err) {
res.status(500).json({ error: err.toString() });
}
});
app.post('/api/posts', async (req, res) => {
const { content } = req.body;
try {
await db.execute('INSERT INTO posts (content) VALUES (?)', [content]);
res.json({ status: 'OK' });
} catch (err) {
res.status(500).json({ error: err.toString() });
}
});
(async () => {
await initDB();
await redisClient.connect();
app.listen(4000, () => {
console.log('Backend listening on port 4000');
});
})();
3.2 backend/Dockerfile
# 개발용 Dockerfile
FROM node:16
# 작업 디렉토리 생성
WORKDIR /usr/src/app
# package.json, package-lock.json 복사
COPY package*.json ./
# 의존성 설치
RUN npm install
# 소스 코드 복사 (개발 때는 bind mount를 권장)
COPY . .
EXPOSE 4000
CMD ["npm", "start"]
Tip: Bind Mount vs COPY
개발 환경에서는 바인드 마운트를 사용해 수정 시 자동 반영하는 것이 편리합니다. 프로덕션 빌드 시에는 COPY 방식을 사용해 이미지에 소스를 포함시킵니다.
🌐 4. 프론트엔드(React) 설정
4.1 간단한 React 예시
// frontend/src/App.js
import React, { useEffect, useState } from 'react';
function App() {
const [posts, setPosts] = useState([]);
const [content, setContent] = useState('');
useEffect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => setPosts(data));
}, []);
const handleSubmit = async () => {
await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify({ content })
});
setContent('');
};
return (
<div>
<h1>My SNS</h1>
<div>
<textarea value={content} onChange={e => setContent(e.target.value)} />
<button onClick={handleSubmit}>Post</button>
</div>
<ul>
{posts.map((p) => (
<li key={p.id}>{p.content}</li>
))}
</ul>
</div>
);
}
export default App;
4.2 frontend/Dockerfile
FROM node:16
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
# 개발 시에는 npm start (개발 서버)
EXPOSE 3000
CMD ["npm", "start"]
Tip: 프록시 설정
React 개발 서버(npm start)는 로컬에서 3000번 포트로 구동됩니다. 백엔드(4000번 포트) 호출 시 CORS 문제를 피하려면 package.json에 "proxy": "http://backend:4000"와 같이 설정하거나, CORS 미들웨어를 백엔드에 추가합니다.
🏦 5. MySQL & Redis 설정
5.1 init.sql (MySQL 초기 스크립트)
CREATE DATABASE IF NOT EXISTS sns_db;
USE sns_db;
CREATE TABLE IF NOT EXISTS posts (
id INT AUTO_INCREMENT PRIMARY KEY,
content VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO posts (content) VALUES ('Hello Docker Compose!');
5.2 Redis
Redis는 별도의 초기화 스크립트 없이도 기본 이미지(redis:latest)로 충분합니다.
📦 6. docker-compose.yml (개발 환경)
version: "3.8"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: my-backend
ports:
- "4000:4000"
volumes:
- ./backend:/usr/src/app # 바인드 마운트로 소스 실시간 반영
depends_on:
- db
- cache
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: my-frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/usr/src/app
depends_on:
- backend
db:
image: mysql:5.7
container_name: my-db
environment:
MYSQL_ROOT_PASSWORD: secret
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
cache:
image: redis:latest
container_name: my-redis
ports:
- "6379:6379"
volumes:
mysql-data:
driver: local
Tip: docker-entrypoint-initdb.d
MySQL 5.7 이미지는 /docker-entrypoint-initdb.d/*.sql에 존재하는 스크립트를 컨테이너 처음 실행 시 자동으로 실행합니다. 이를 통해 DB와 테이블 초기 설정을 자동화할 수 있습니다.
🏃 7. 실행 및 테스트
- 이미지 빌드 & 컨테이너 실행
docker compose up -d
- 상태 확인
docker compose ps
- my-backend, my-frontend, my-db, my-redis가 모두 Up 상태여야 합니다.
- 접속 테스트
- 프론트엔드: 브라우저에서 http://localhost:3000
- 백엔드: curl http://localhost:4000/api/posts
- DB: docker exec -it my-db mysql -u root -p (비밀번호 secret)
- Redis: docker exec -it my-redis redis-cli
Tip: 코드 수정 반영
프론트엔드나 백엔드 소스 코드를 변경하면, 바인드 마운트 덕분에 컨테이너 내부에 실시간 반영됩니다.
단, Nodemon 등을 사용하지 않았다면 백엔드를 재시작해야 반영될 수 있으니, 필요하면 nodemon을 개발 의존성으로 추가하세요.
📝 정리
- Docker Compose 하나로 풀스택 애플리케이션(백엔드, 프론트엔드, DB, 캐시)을 손쉽게 구동할 수 있습니다.
- 개발 환경에서는 바인드 마운트를 통해 소스 수정 → 즉시 반영이 가능하며, MySQL 초기화 스크립트나 Redis 등도 간단히 설정할 수 있습니다.
다음(10-2) 예고
- 프로덕션 환경으로 넘어가서, Swarm 또는 Kubernetes를 이용해 무중단 업데이트, 스케일링, 롤백 등을 실습해 봅니다.
- CI/CD와 연계해 코드가 푸시될 때마다 자동으로 컨테이너 이미지를 빌드 & 배포하는 흐름도 짚어볼 예정입니다.
이제 로컬 개발환경은 완성! 다음 강의에서 실제 배포 전략을 고도화해 봅시다. 😊
'소프트웨어 개발 > Docker' 카테고리의 다른 글
🐳 Docker 강의 10-2: 실전 프로젝트 (운영 환경) - Swarm/K8S를 이용한 배포 (0) | 2025.01.27 |
---|---|
🐳 Docker 강의 8강: Docker Swarm 기본 클러스터링 (0) | 2025.01.27 |
🐳 Docker 강의 7강: Docker 배포 전략 – 이미지 최적화와 Private Registry 활용 (0) | 2025.01.27 |
🐳 Docker 강의 5강: Docker 볼륨과 데이터 관리 (0) | 2025.01.27 |
🐳 Docker 강의 6강: Docker Compose를 활용한 멀티 컨테이너 환경 구성 (0) | 2025.01.27 |