본문 바로가기
컴퓨터 활용/노년에 즐기는 코딩

Django + MySQL 웹서비스 개발

by easyfly 2025. 8. 16.
반응형

 

Docker로 셋업하는 Django + MySQL 개발환경

1) 디렉터리 구조 만들기

작업 폴더를 하나 만들고 아래 파일들을 채웁니다.

myproject/
 ├─ docker-compose.yml
 ├─ .env
 ├─ .dockerignore
 ├─ web/
 │   ├─ Dockerfile
 │   └─ requirements.txt
 └─ (처음엔 장고 소스가 없음: 컨테이너에서 생성)

2) 환경변수 파일(.env)

비밀번호 같은 값은 코드에 박지 말고 .env에 두겠습니다.

# .env
MYSQL_ROOT_PASSWORD=secret-root
MYSQL_DATABASE=mydb
MYSQL_USER=myuser
MYSQL_PASSWORD=secret-pass
TZ=Asia/Seoul

3) docker-compose.yml

웹(장고), DB(MySQL), DB관리(Adminer)를 한 번에 띄웁니다.

# docker-compose.yml
version: "3.9"

services:
  db:
    image: mysql:8.0
    container_name: mp_db
    env_file: .env
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - TZ=${TZ}
    command: [
      "mysqld",
      "--character-set-server=utf8mb4",
      "--collation-server=utf8mb4_unicode_ci",
      "--default-time-zone=+09:00"
    ]
    ports:
      - "3306:3306"   # 로컬 DB 클라이언트로 접속 필요 없으면 주석 처리 가능
    volumes:
      - dbdata:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 3s
      retries: 20

  web:
    build: ./web
    container_name: mp_web
    depends_on:
      db:
        condition: service_healthy
    env_file: .env
    environment:
      - DJANGO_SETTINGS_MODULE=config.settings
      - PYTHONDONTWRITEBYTECODE=1
      - PYTHONUNBUFFERED=1
      - TZ=${TZ}
    volumes:
      - ./:/app
    working_dir: /app
    command: bash -lc "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    ports:
      - "8000:8000"

  adminer:
    image: adminer:latest
    container_name: mp_adminer
    depends_on:
      - db
    environment:
      - ADMINER_DEFAULT_SERVER=db
    ports:
      - "8080:8080"

volumes:
  dbdata:

4) 웹용 Dockerfile

mysqlclient 빌드를 위해 최소한의 개발 패키지를 설치합니다.

# web/Dockerfile
FROM python:3.12-slim

# 시스템 패키지 (mysqlclient 빌드에 필요)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential default-libmysqlclient-dev pkg-config curl \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt

5) Python 패키지 목록

# web/requirements.txt
Django>=5.0,<6.0
mysqlclient>=2.2
django-environ>=0.11

6) .dockerignore

컨테이너 빌드 시 불필요한 파일 제외

.git
__pycache__/
*.pyc
.env
db.sqlite3
staticfiles/
media/

7) 컨테이너 기동 및 장고 프로젝트 생성

7-1. 컨테이너 빌드 & 시작

docker compose up -d --build

7-2. 장고 프로젝트 골격 만들기

처음 한 번만 실행합니다. (config가 프로젝트명)

docker compose exec web django-admin startproject config .

7-3. settings.py에 DB 연결 설정

config/config/settings.py를 열어 아래처럼 수정합니다. (django-environ 사용)

# config/settings.py
from pathlib import Path
import environ, os

BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env(
    DEBUG=(bool, True),
)
environ.Env.read_env(os.path.join(BASE_DIR.parent, '.env'))

SECRET_KEY = env('SECRET_KEY', default='dev-secret')
DEBUG = True

ALLOWED_HOSTS = ['*']

INSTALLED_APPS = [
    'django.contrib.admin','django.contrib.auth','django.contrib.contenttypes',
    'django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'
WSGI_APPLICATION = 'config.wsgi.application'
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_TZ = True

# MySQL 연결
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': env('MYSQL_DATABASE'),
        'USER': env('MYSQL_USER'),
        'PASSWORD': env('MYSQL_PASSWORD'),
        'HOST': 'db',         # compose 서비스 이름
        'PORT': 3306,
        'OPTIONS': {
            'charset': 'utf8mb4',
        },
        'CONN_MAX_AGE': 60,   # 커넥션 재사용
    }
}

# 정적 파일
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

참고: .env에 SECRET_KEY도 추가하시면 좋습니다.
SECRET_KEY=your-dev-secret

7-4. 마이그레이션 & 관리자 계정

docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser

7-5. 접속 확인

  • 장고: http://localhost:8000
  • Adminer: http://localhost:8080
    • 서버: db
    • 사용자: .env의 MYSQL_USER
    • 비번: .env의 MYSQL_PASSWORD
    • DB: MYSQL_DATABASE

8) 장고 앱 추가 예시

docker compose exec web python manage.py startapp blog

INSTALLED_APPS에 blog 등록 → 모델 작성 → makemigrations → migrate 순서대로 진행합니다.


9) 정적 파일 수집(배포 준비)

개발은 DEBUG=True로 진행하고, 배포 시:

docker compose exec web python manage.py collectstatic --noinput

Nginx 등 역프록시를 두고 staticfiles/를 서빙하는 구성을 추가합니다.


10) 자주 겪는 문제와 해결

  1. 장고가 DB에 못 붙음 (OperationalError: Can't connect)
    • 해결: depends_on + healthcheck로 DB 준비를 기다리게 했지만, 최초 기동이 오래 걸릴 수 있습니다.
      문제가 지속되면 docker compose logs db로 에러 확인 후 docker compose restart web.
  2. 한글 깨짐
    • 해결: compose의 mysqld 옵션에 utf8mb4/utf8mb4_unicode_ci 지정함.
      그래도 깨지면 테이블/컬럼의 Collation을 확인.
  3. 권한 문제(파일 생성이 루트 소유)
    • 해결: 개발 시에는 큰 문제없지만 필요하면 web 서비스에 user: "1000:1000" 지정.
  4. 장고에서 ‘DisallowedHost’
    • 해결: ALLOWED_HOSTS = ['*'](개발용). 배포 시 도메인만 열어두세요.
  5. DB 비번 잊음 / 초기화
    • dbdata 볼륨을 삭제하면 DB가 초기화됩니다.
    • docker compose down -v docker compose up -d

11) 프로덕션으로 확장할 때

  • Gunicorn + Nginx 컨테이너 추가
  • 환경변수 분리(dev/prod .env) 및 DEBUG=False
  • 보안/성능: SECURE_* 설정, ALB/프록시, 캐시(Redis), 백업 전략
  • 마이그레이션 자동화: command 대신 엔트리포인트 스크립트로 migrate/collectstatic 수행

12) 빠른 체킹 명령 모음

# 전체 기동/중지/로그
docker compose up -d --build
docker compose logs -f web
docker compose down

# DB 접속(Adminer 없이)
docker compose exec db mysql -umyuser -psecret-pass mydb

# 장고 명령
docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser
docker compose exec web python manage.py shell

 

.

반응형

댓글