[코담]
웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트
01 전반 구조, URL, 모델 | ✅저자: 이유정(박사)
📅 Django + Tailwind Todo 프로젝트 (전반 구조, URL, 모델) 해석
프로젝트 개요
- 포탈을 조합한 Django 프로젝트로, 다양한 UI/가이드 파일과 TailwindCSS가 개입되어 있습니다.
- Django 5.2, TailwindCSS 4.1, SQLite3(개발 DB)를 기본 환경으로 구성했습니다.
화면 구성
1.메인화면
2.회원가입
3.로그인
4.Todo 목록
5.Todo 등록
6.Todo 수정
📁 프로젝트 구조
todoList_django2/
│
├── accounts/ # 사용자 인증 및 관련 기능 앱
├── config/ # Django 설정 파일 (settings.py, urls.py 등)
├── node_modules/ # npm 패키지 저장소 (Tailwind 등 프론트엔드용)
├── static/ # 정적 파일(css, js 등)
├── templates/ # HTML 템플릿 폴더
├── todo/ # ToDo 기능 관련 앱
│
├── .gitignore # Git에서 제외할 파일 정의
├── db.sqlite3 # SQLite 데이터베이스 파일
├── manage.py # Django 명령어 실행용 스크립트
├── package.json # 프론트엔드 패키지 정의 (Tailwind 포함 예상)
├── pnpm-lock.yaml # pnpm 패키지 잠금 파일
├── README.md # 프로젝트 설명 문서
├── requirements.txt # Python 패키지 목록
├── tailwind.config.js # Tailwind CSS 설정 파일
└── venv/ # 가상환경 폴더 (Python 패키지 독립 환경)
📁 프로젝트 구조
📁 todo 앱 디렉토리 구조
todo/
│
├── __pycache__/ # 파이썬 캐시 파일
├── migrations/ # 마이그레이션 파일 (DB 스키마 관리)
│
├── templates/
│ └── todo/
│ ├── include/ # 공통 include 템플릿 폴더
│ ├── base.html # 기본 레이아웃 템플릿
│ ├── head.html # <head> 태그 관련 HTML 분리
│ ├── header.html # 일반 헤더
│ ├── header-account.html # 로그인/계정 관련 헤더
│ ├── footer.html # 공통 푸터
│ ├── todo_create.html # 할 일 생성 페이지
│ ├── todo_detail.html # 할 일 상세 페이지
│ ├── todo_list.html # 할 일 목록 페이지
│ └── todo_update.html # 할 일 수정 페이지
│
├── __init__.py # 패키지 초기화
├── admin.py # Django 관리자 페이지 설정
├── apps.py # 앱 구성 정보
├── form.py # 폼 클래스 정의 (Form, ModelForm 등)
├── models.py # DB 모델 정의 (ToDo 모델 등)
├── tests.py # 테스트 코드
├── urls.py # URL 라우팅 정의
└── views.py # 뷰 함수/클래스 정의
📁 todo 앱 디렉토리 구조
⚙️ 개발 환경
- Python 3.12.3
- Django 5.2.1
- 가상환경: venv 사용
- tailwindcss v4.1 사용
✅ Django settings.py 주의할 설정 항목 정리
🔒 1. 보안 관련 설정
✅ SECRET_KEY
SECRET_KEY = 'django-insecure-...'
- Django 프로젝트의 핵심 보안 키로, 절대 외부에 노출되면 안 됨
- 실제 서비스에서는
.env
파일로 분리하거나 환경변수로 관리 필요
✅ DEBUG
DEBUG = True
- 개발 환경에서는 True로 설정 가능
- 배포 시에는 반드시 False로 설정해야 함
- False일 때만 Django가 보안 기능(에러 페이지 숨김 등)을 제대로 작동함
✅ ALLOWED_HOSTS
ALLOWED_HOSTS = []
-
비워두면 외부 요청 거부됨
-
배포 시에는 반드시 도메인이나 IP를 지정해야 함
ALLOWED_HOSTS = ['yourdomain.com', '127.0.0.1']
📁 2. 정적 파일 설정
✅ STATICFILES_DIRS
STATICFILES_DIRS = [ BASE_DIR / "static" ]
- 개발 환경에서 정적 파일을 불러오기 위해 사용
- 운영 환경에서는 STATIC_ROOT도 설정 후 collectstatic 명령 필요
🔧 추가 필요 (운영 환경)
STATIC_ROOT = BASE_DIR / "staticfiles"
python manage.py collectstatic
실행 시 이 경로로 파일이 복사됨
🧩 3. 템플릿 설정
✅ TEMPLATES["DIRS"]
"DIRS": [BASE_DIR / "templates"]
- 전역 템플릿 폴더 경로
- 각 앱의
templates/
폴더와 충돌하지 않도록 주의
🔐 4. 로그인/로그아웃 관련
✅ 로그인/로그아웃 후 리디렉션 경로
LOGIN_REDIRECT_URL = "/todo/"
LOGOUT_REDIRECT_URL = "/accounts/login/"
- 실제 해당 경로가 존재해야 하며, 설정이 잘못되면 로그인 후 오류 발생 가능
🌍 5. 시간대 및 지역 설정
✅ TIME_ZONE, USE_TZ
TIME_ZONE = 'Asia/Seoul'
USE_TZ = True
- USE_TZ=True이면 DB에는 UTC로 저장되고, 템플릿에서는
TIME_ZONE
기준으로 변환됨 - 시간대 오차 발생 여부 주의
🗃 6. 데이터베이스 설정
✅ SQLite 사용 (기본값)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
- 가볍고 편리하지만, 운영 환경에서는 PostgreSQL/MySQL로 변경 권장
✅ 추가로 고려할 사항
.env
환경 파일 분리- 이메일 설정 (에러 알림용)
- CORS 설정 (API 연동 시 필요)
- 보안 관련 미들웨어 강화 (예: XSS, CSRF 정책 추가)
가상 URL Routing 해석
▶ config/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path("todo/", include("todo.urls")),
path("", root_index_view), # 루트 / 메인 index.html 보이게
path("accounts/", include("accounts.urls")),
path("accounts/", include("django.contrib.auth.urls")),
path('signup/', signup_redirect_view),
path('login/', login_redirect_view),
]
/
: 우선index.html
개입/todo/
: ToDo 앱과 관련된 건 모두 바이네이션/accounts/
: 로그인, 회원가입, 비밀번호 교체 등 계정 관리
▶ todo/urls.py
urlpatterns = [
path("", TodoListView.as_view(), name="todo_list"),
path("create/", TodoCreateView.as_view(), name="todo_create"),
path("<int:pk>/", TodoDetailView.as_view(), name="todo_detail"),
path("<int:pk>/update/", TodoUpdateView.as_view(), name="todo_update"),
path("<int:pk>/delete/", TodoDeleteView.as_view(), name="todo_delete"),
]
/todo/
: 목록 페이지/todo/create/
: 새 할일 등록/todo/3/
: ID=3 할일 상세 보기/todo/3/update/
: 할일 수정/todo/3/delete/
: 할일 삭제
▶ Todo 목록 모델 (models.py)
class Todo(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="작성자")
name = models.CharField("이름", max_length=100)
description = models.TextField("설명", blank=True)
complete = models.BooleanField("완료 유무", default=False)
exp = models.PositiveIntegerField("경험치", default=0)
completed_at = models.DateTimeField("완료 시간", null=True, blank=True)
created_at = models.DateTimeField("생성 시간", auto_now_add=True)
updated_at = models.DateTimeField("수정 시간", auto_now=True)
def clean(self):
if self.complete and self.completed_at is None:
raise ValidationError("\uc644\ub8cc\ub41c \ud56d\ubaa9\uc740 completed_at \ub0a0\uc9dc\uac00 \ud544\uc694\ud569\ub2c8\ub2e4.")
def save(self, *args, **kwargs):
if self.complete and self.completed_at is None:
self.completed_at = timezone.now()
elif not self.complete:
self.completed_at = None
super().save(*args, **kwargs)
class Meta:
verbose_name = "\ud560 \uc77c"
verbose_name_plural = "\ud560 \uc77c \ubaa9\ub85d"
🔍 이 로직의 장점
-
✅
clean()
에서 완료 상태에 맞는 유효성 검사를 선행 → 데이터 일관성 유지 -
✅
save()
에서completed_at
자동 세팅 → 개발자 편의성과 실수 방지 -
✅
admin
,form
,API
등 여러 입력 경로에서도 일관된 처리 가능
단점 설명
- 그룹 목록에 충분한 정보 (작성자, 이름, 설명, 경험치, 완료 유무)가 포함됨
clean()
를 통해 목록 유효성 검사 수행save()
메소드를 통해 자동으로 completed_at 처리
✅ Todo 모델 상세 설명
from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
get_user_model()
은 현재 프로젝트에서 사용하는 사용자 모델을 반환합니다. 기본적으로는auth.User
지만, 커스텀 유저 모델을 사용하는 경우에도 자동으로 연결됩니다.
📦 모델 정의: Todo
class Todo(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="작성자")
name = models.CharField("이름", max_length=100)
description = models.TextField("설명", blank=True)
complete = models.BooleanField("완료 여부", default=False)
exp = models.PositiveIntegerField("경험치", default=0)
completed_at = models.DateTimeField("완료 시각", null=True, blank=True)
created_at = models.DateTimeField("생성 시각", auto_now_add=True)
updated_at = models.DateTimeField("수정 시각", auto_now=True)
🔸 필드 설명
필드명 | 타입 | 설명 |
---|---|---|
author |
ForeignKey(User) |
작성자 (로그인한 사용자와 연결됨) |
name |
CharField |
할 일 제목, 최대 100자 제한 |
description |
TextField |
상세 설명, 비워둘 수 있음 |
complete |
BooleanField |
완료 여부 (True /False ) |
exp |
PositiveIntegerField |
이 할 일의 경험치 (0 이상 정수) |
completed_at |
DateTimeField |
완료된 시점, 완료 안 했으면 빈 값 가능 |
created_at |
DateTimeField |
처음 생성된 시각 (자동 저장) |
updated_at |
DateTimeField |
마지막으로 수정된 시각 (자동 저장) |
🧪 clean()
메서드
def clean(self):
if self.complete and self.completed_at is None:
raise ValidationError("완료된 항목은 completed_at 날짜가 필요합니다.")
-
모델 유효성 검사 로직을 정의
-
완료된 항목(
complete=True
)인데 완료시각(completed_at
)이 없다면 예외 발생 -
주석 처리된 반대 조건도 활용 가능:
if not self.complete and self.completed_at is not None: raise ValidationError("완료되지 않은 항목은 completed_at 날짜를 가질 수 없습니다.")
💾 save()
메서드 오버라이드
def save(self, *args, **kwargs):
if self.complete and self.completed_at is None:
self.completed_at = timezone.now()
elif not self.complete:
self.completed_at = None
super().save(*args, **kwargs)
- 저장 직전에 완료 상태에 따라
completed_at
필드 자동 설정 complete = True
인데completed_at
이 없다면 현재 시각으로 자동 설정complete = False
라면completed_at
은 무조건 None 처리 (초기화)
🔧 Meta 클래스 및 문자열 표현
class Meta:
verbose_name = "할 일"
verbose_name_plural = "할 일 목록"
def __str__(self):
return f"이름: {self.name}, 설명: {self.description}, 완료: {self.complete}, 경험치: {self.exp}, 완료시각: {self.completed_at}"
Meta
클래스는 관리자 페이지 등에서의 모델 명칭 지정__str__()
은 객체를 문자열로 표현할 때 출력되는 포맷 지정
✅ 핵심 요약
기능 | 역할 |
---|---|
작성자 연결 | 로그인 사용자와 각 할 일을 연결함 |
유효성 검사 | 완료된 항목에는 완료시각이 있어야 함 |
자동 처리 | 완료 시점 자동 저장 (save() 활용) |
경험치 시스템 | 할 일 완료 시 보상처럼 사용할 수 있음 |
🔗 관련 기능 흐름도 예시
- 사용자 할 일 작성 →
TodoCreateView
에서save()
호출 - 체크박스로 완료 여부 선택 시,
completed_at
자동 기록 - 수정 시 완료 여부 변경하면
save()
가 적절히 동작 - 관리자 페이지에서도
__str__()
덕분에 요약 정보 확인 가능