과제
📝 Django Todo 프로젝트: 이미지 처리 + Summernote + Bootstrap5 통합 실습 정리
Chansman
2025. 5. 12. 18:15
📝 Django Todo 프로젝트: 이미지 처리 + Summernote + Bootstrap5 통합 실습 정리
✅ 목표 요약
- django-summernote를 활용한 rich text 편집 기능 적용
- ImageField + Pillow로 이미지 썸네일 자동 생성 로직 구현
- django-cleanup으로 이미지 수정/삭제 시 파일 정리 자동화
- Bootstrap5 기반 Form UI 개선 및 정리
📦 필요한 라이브러리 설치
poetry add django-summernote pillow django-cleanup
그리고 settings.py에 다음 설정 추가:
INSTALLED_APPS = [
...
'django_summernote',
'django_cleanup.apps.CleanupConfig', # ✅ 파일 자동 삭제 처리
]
📌 Summernote 보안 설정 예시:
X_FRAME_OPTIONS = 'SAMEORIGIN'
SUMMERNOTE_CONFIG = {
'iframe': False,
'summernote': {
'disableDragAndDrop': True,
},
}
🧱 1. Todo 모델 수정하기
from django.db import models
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
from pathlib import Path
class Todo(models.Model):
...
completed_image = models.ImageField(upload_to='todo/images/', null=True, blank=True)
thumbnail = models.ImageField(upload_to='todo/thumbs/', null=True, blank=True, default='todo/thumbs/default.png')
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.completed_image:
image = Image.open(self.completed_image)
image.thumbnail((300, 300))
image_path = Path(self.completed_image.name)
filename_stem = image_path.stem
file_ext = image_path.suffix
thumb_name = f'{filename_stem}_thumb{file_ext}'
temp_thumb = BytesIO()
image.save(temp_thumb, format=image.format)
temp_thumb.seek(0)
self.thumbnail.save(thumb_name, ContentFile(temp_thumb.read()), save=False)
temp_thumb.close()
super().save(*args, **kwargs)
🧾 2. TodoForm, TodoUpdateForm 구성
from django import forms
from django_summernote.widgets import SummernoteWidget
from .models import Todo
class TodoForm(forms.ModelForm):
class Meta:
model = Todo
fields = '__all__'
widgets = {
'description': SummernoteWidget(),
'title': forms.TextInput(attrs={'class': 'form-control'}),
'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
}
class TodoUpdateForm(forms.ModelForm):
class Meta:
model = Todo
fields = '__all__'
widgets = {
'description': SummernoteWidget(),
'title': forms.TextInput(attrs={'class': 'form-control'}),
'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'is_completed': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'completed_image': forms.FileInput(attrs={'class': 'form-control'}),
}
⚙️ 3. cb_views.py 설정
from .forms import TodoForm, TodoUpdateForm
class TodoCreateView(CreateView):
model = Todo
form_class = TodoForm
class TodoUpdateView(UpdateView):
model = Todo
form_class = TodoUpdateForm
🛠 4. admin.py 설정
from django_summernote.admin import SummernoteModelAdmin
class TodoAdmin(SummernoteModelAdmin):
summernote_fields = ('description',)
fieldsets = (
(None, {'fields': ('title', 'description', 'completed_image', 'thumbnail')}),
)
🎨 5. 템플릿 수정 (todo_form.html 등)
📄 todo_list.html
{% for todo in todos %}
<div class="card">
<img src="{{ todo.thumbnail.url }}" class="card-img-top" alt="썸네일">
<div class="card-body">
<h5>{{ todo.title }} {% if todo.is_completed %}<span class="badge bg-success">Completed</span>{% endif %}</h5>
...
</div>
</div>
{% endfor %}
📄 todo_form.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">저장</button>
</form>
📌 todo_create.html, todo_update.html → {% include 'todo_form.html' %} 방식으로 구성하세요.
✅ 최종 체크리스트
- django-summernote 적용 및 XSS 방지 설정
- Pillow로 썸네일 자동 생성 로직 구현
- django-cleanup으로 이미지 정리 자동화
- Bootstrap5로 템플릿 UI 개선
- Form 및 Admin 최적화 구성 완료
필요시 미디어 루트 설정, static 경로, default 이미지 등록 위치까지 추가 안내 가능합니다 ✅