십대를 위한 코딩

Django 프로젝트 Autobiography 단계별 가이드

forSilver 2025. 8. 22. 16:28
반응형

Django 프로젝트 Autobiography 단계별 가이드(도커 적용까지)

사전 준비

목표: 개발 환경 점검
실행

python3 --version
docker --version
docker compose version

확인: 버전이 정상 출력되면 통과.


1. 프로젝트 생성(가상환경 포함)

목표: Django 기반 뼈대 마련
실행

# 새 디렉터리
mkdir autobiography && cd autobiography

# 가상환경
python3 -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip

# 장고 설치 및 프로젝트/앱 생성
pip install "Django>=5.0,<6.0" gunicorn
django-admin startproject config .
python manage.py startapp autobiography

확인

python manage.py runserver

브라우저에서 http://127.0.0.1:8000 확인.


2. 최소 도메인 모델 설계

목표: 자서전(Autobiography)용 핵심 엔티티 정의
실행: autobiography/models.py

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    birth_year = models.IntegerField(null=True, blank=True)
    bio = models.TextField(blank=True)
    def __str__(self): return self.name

class Chapter(models.Model):
    title = models.CharField(max_length=200)
    order = models.PositiveIntegerField(default=1)
    summary = models.TextField(blank=True)
    def __str__(self): return f"{self.order}. {self.title}"
    class Meta: ordering = ["order"]

class Entry(models.Model):
    chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE, related_name="entries")
    date = models.DateField(null=True, blank=True)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    def __str__(self): return f"Entry for {self.chapter.title}"

config/settings.py의 INSTALLED_APPS에 'autobiography' 추가 후:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

확인: python manage.py runserver → /admin 에서 Author/Chapter/Entry 등록 가능.:

관리자 표시: autobiography/admin.py

from django.contrib import admin
from .models import Author, Chapter, Entry
admin.site.register([Author, Chapter, Entry])


3. 최소 페이지(View/URL/Template)

목표: 독자가 장/에피소드를 열람
실행

  • autobiography/views.py
from django.views.generic import ListView, DetailView
from .models import Chapter, Entry

class ChapterListView(ListView):
    model = Chapter
    template_name = "autobiography/chapter_list.html"

class ChapterDetailView(DetailView):
    model = Chapter
    template_name = "autobiography/chapter_detail.html"
  • config/urls.py
from django.contrib import admin
from django.urls import path
from autobiography.views import ChapterListView, ChapterDetailView

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", ChapterListView.as_view(), name="chapter_list"),
    path("chapters/<int:pk>/", ChapterDetailView.as_view(), name="chapter_detail"),
]
  • 템플릿(예시)
    • templates/autobiography/chapter_list.html
    • <h1>Autobiography</h1> <ul> {% for ch in object_list %} <li><a href="{% url 'chapter_detail' ch.pk %}">{{ ch.order }}. {{ ch.title }}</a></li> {% empty %}<li>아직 장이 없습니다.</li>{% endfor %} </ul>
    • templates/autobiography/chapter_detail.html
    • <h2>{{ object.order }}. {{ object.title }}</h2> <p>{{ object.summary }}</p> <hr> {% for e in object.entries.all %} <article> <small>{{ e.date }} · {{ e.created_at|date:"Y-m-d H:i" }}</small> <p style="white-space:pre-wrap">{{ e.content }}</p> </article> <hr> {% empty %}에피소드가 없습니다.{% endfor %}

확인: 루트 페이지에서 장 목록/상세가 보이면 통과.


4. 환경변수/정적파일 설정

목표: 도커화를 위한 설정 분리
실행: config/settings.py

import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "unsafe-dev")
DEBUG = os.environ.get("DJANGO_DEBUG", "1") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "*").split(",")

STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

루트에 .env.dev:

DJANGO_SECRET_KEY=change_me_dev
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=*

확인

python manage.py collectstatic --noinput

staticfiles/가 생성되면 통과.


5. 의존성 고정

목표: 재현 가능한 빌드
실행

pip freeze > requirements.txt

확인: requirements.txt 생성 확인.


6. 도커화(Dockerfile, .dockerignore, EntryPoint)

목표: 컨테이너로 실행 가능한 이미지 만들기

  • .dockerignore
.git
__pycache__/
*.pyc
*.log
venv/
.env*
media/
staticfiles/
  • Dockerfile
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential pkg-config && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app

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

COPY . .

# 비루트 사용자
RUN useradd -m appuser
USER appuser

EXPOSE 8000

# 엔트리포인트(정적수집/마이그레이션/서버)
CMD python manage.py collectstatic --noinput && \
    python manage.py migrate --noinput && \
    gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3

확인(빌드/실행)

# 프로젝트 루트에서
docker build -t autobiography:dev .
docker run -d --name autobiography \
  --env-file .env.dev -p 8000:8000 autobiography:dev
docker logs -f autobiography

브라우저에서 http://localhost:8000 정상 확인.

권한 오류 발생 시
sudo usermod -aG docker $USER && newgrp docker 후 재시도.
ALLOWED_HOSTS 에러 시 .env.dev에 실제 접근 IP/도메인 추가.


7. Docker Compose(선택, 개발 편의)

목표: 한 줄로 올리고 내리기
루트에 compose.yml:

services:
  web:
    build: .
    image: autobiography:dev
    env_file: .env.dev
    ports: ["8000:8000"]
    volumes:
      - .:/app:delegated
      - static_volume:/app/staticfiles
    restart: unless-stopped

volumes:
  static_volume:

실행/확인

docker compose up -d --build
docker compose logs -f web
docker compose down

8. MySQL로 확장(선택)

목표: 운영형 DB 학습
config/settings.py에 조건부 DB 설정 추가:

if os.environ.get("DB_HOST"):
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.mysql",
            "NAME": os.environ.get("DB_NAME"),
            "USER": os.environ.get("DB_USER"),
            "PASSWORD": os.environ.get("DB_PASSWORD"),
            "HOST": os.environ.get("DB_HOST"),
            "PORT": os.environ.get("DB_PORT", "3306"),
            "OPTIONS": {"charset": "utf8mb4"},
        }
    }

.env.mysql

DJANGO_SECRET_KEY=change_me_dev
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=*

DB_NAME=autobio
DB_USER=autobio
DB_PASSWORD=autobiopw
DB_HOST=db
DB_PORT=3306

compose.mysql.yml

services:
  web:
    build: .
    image: autobiography:mysql-dev
    env_file: .env.mysql
    depends_on: [db]
    ports: ["8000:8000"]
    volumes:
      - .:/app:delegated
      - static_volume:/app/staticfiles
    restart: unless-stopped

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: rootpw
      TZ: Asia/Seoul
    command: ["--character-set-server=utf8mb4","--collation-server=utf8mb4_unicode_ci"]
    volumes: [ "db_data:/var/lib/mysql" ]
    ports: ["3307:3306"]

volumes:
  db_data:
  static_volume:

실행/확인

docker compose -f compose.mysql.yml up -d --build
docker compose -f compose.mysql.yml exec web python manage.py migrate
docker compose -f compose.mysql.yml exec web python manage.py createsuperuser

9. Docker Hub 배포(선택)

목표: 이미지 공유/배포
실행

docker login
docker tag autobiography:dev esyfly/autobiography:dev-1
docker push esyfly/autobiography:dev-1

다른 서버:

docker pull esyfly/autobiography:dev-1
docker run -d --name autobiography -p 8000:8000 --env-file .env.dev esyfly/autobiography:dev-1

10. 학습 점검표(스스로 평가)

  • 관리자에서 Author/Chapter/Entry를 만들 수 있는가
  • 루트 페이지에서 장 목록/상세가 보이는가
  • 로컬에서 도커 이미지 빌드/실행이 되는가
  • Compose로 올리고 내릴 수 있는가
  • MySQL로 전환 후 이민(마이그레이션)과 접속이 되는가
  • ALLOWED_HOSTS, 정적파일(collectstatic), 권한 이슈를 스스로 해결할 수 있는가

11. 자주 겪는 오류와 빠른 해결

  • docker.sock 권한: sudo usermod -aG docker $USER && newgrp docker
  • pip/venv 권한: venv 작업에 sudo 금지, 문제 시 rm -rf venv 후 재생성
  • 정적파일 미표시: STATIC_ROOT 설정 + collectstatic 실행 여부 확인
  • DB 연결 실패: Compose 내부 접속은 DB_HOST=db(서비스명) 사용
  • Gunicorn 경로 오류: gunicorn config.wsgi:application에서 config는 프로젝트 모듈명과 일치해야 함

마무리

위 과정을 완료하면 문학적 자서전 프로젝트(Autobiography)의 최소 기능이 동작하고, 도커 이미지로 실행/배포할 수 있습니다.