[STEP 3] 이미지 및 파일 첨부 (File Upload)
이번 단계에서는 게시글에 이미지(사진)를 첨부하고 화면에 보여주는 기능을 구현해 보겠습니다.
자바(Spring)에서 파일 업로드를 구현할 때 스트림 처리나 경로 설정이 복잡했던 것과 달리, Django는 설정 몇 줄과 모델 필드 추가만으로 간편하게 구현 가능합니다.
1. 사전 준비 (라이브러리 설치)
파이썬에서 이미지를 다루려면 Pillow라는 라이브러리가 필수입니다. 터미널에 아래 명령어를 입력하여 설치해주세요.
pip install Pillow
💡 Pillow란? 파이썬의 대표적인 이미지 처리 라이브러리입니다.
Django의 ImageField는 단순히 파일을 저장하는 것을 넘어, "이 파일이 깨진 파일은 아닌지", "실제 이미지 파일이 맞는지(확장자 위조 방지)" 등을 검사하기 위해 내부적으로 이 라이브러리를 사용합니다.
2. 프로젝트 설정 (config/settings.py)
Django에게 "업로드된 파일은 어디에 저장하고, 어떤 주소로 접근할지" 알려줘야 합니다.
- Static 파일: 개발자가 미리 넣어둔 파일 (CSS, JS, 로고 등)
- Media 파일: 사용자가 업로드하는 파일 (프로필 사진, 게시글 첨부 등)
config/settings.py 맨 아래에 두 줄을 추가하세요.
import os
# [추가] 사용자가 업로드한 파일(Media) 설정
# 1. 브라우저에서 접근할 URL 주소 (예: http://127.0.0.1:8000/media/photo.jpg)
MEDIA_URL = '/media/'
# 2. 실제 파일이 저장될 하드디스크 경로 (프로젝트폴더/media/)
MEDIA_ROOT = BASE_DIR / 'media'
3. URL 연결 (config/urls.py)
Django는 보안과 성능상의 이유로 기본적으로 미디어 파일을 직접 서빙하지 않습니다.
개발 모드(DEBUG=True)에서만 편의를 위해 설정이 필요합니다.
config/urls.py 파일을 열고 아래 내용을 추가하세요.
from django.contrib import admin
from django.urls import path, include
from django.conf import settings # [추가] 설정 가져오기
from django.conf.urls.static import static # [추가] 정적파일 연결 함수
urlpatterns = [
path('admin/', admin.site.urls),
path('board/', include('boards.urls')),
path('accounts/', include('accounts.urls')),
]
# [추가] 개발 모드일 때만 미디어 파일 서빙 설정
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
🌏 실무(Production) 환경에서는? 개발 모드(DEBUG=True)가 아닌 실제 배포 환경에서는 위 코드가 작동하지 않습니다. Django는 애플리케이션 서버이지 파일 서버가 아니기 때문입니다. 실무에서는 Nginx나 Apache 같은 웹 서버가 미디어 파일을 직접 제공하거나, AWS S3 같은 클라우드 스토리지 서비스를 연결하여 사용하는 것이 정석입니다.
4. 모델 수정 (boards/models.py)
Post 모델에 이미지를 담을 그릇(ImageField)을 만듭니다.
boards/models.py의 Post 클래스에 필드를 추가하세요.
class Post(models.Model):
# ... 기존 필드들 ...
# [추가] 이미지 필드
# upload_to='board/images/%Y/%m/%d/': 파일을 저장할 때 '년/월/일' 폴더로 자동 분류해줍니다. (관리 용이)
# blank=True, null=True: 이미지를 첨부하지 않아도 글 작성이 가능하도록 허용합니다.
image = models.ImageField(upload_to='board/images/%Y/%m/%d/', blank=True, null=True, verbose_name="이미지")
5. DB 반영
모델이 바뀌었으니 마이그레이션을 진행합니다.
[Bash]
python manage.py makemigrations
python manage.py migrate
6. 폼 수정 (boards/forms.py)
모델에 필드를 추가했더라도 폼에서 설정해주지 않으면 화면에 나타나지 않습니다.
boards/forms.py를 수정하세요.
class PostForm(forms.ModelForm):
class Meta:
model = Post
# [수정 1] 리스트에 'image' 필드를 반드시 추가해야 합니다.
fields = ['title', 'content', 'image']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
# [수정 2] 파일 업로드 위젯에도 form-control 클래스 적용
'image': forms.ClearableFileInput(attrs={
'class': 'form-control'
}),
}
7. 로직 수정 (boards/views.py)
파일 데이터를 폼으로 넘겨줄 때는 request.POST뿐만 아니라 request.FILES도 반드시 함께 넘겨야 합니다.
boards/views.py에서 board_write 함수와 board_edit 함수 두 군데를 수정하세요.
# 1. 글쓰기 함수 (board_write)
if request.method == 'POST':
# [수정] request.FILES 추가! (이게 없으면 파일 업로드 안됨)
form = PostForm(request.POST, request.FILES)
# ...
# 2. 글수정 함수 (board_edit)
if request.method == 'POST':
# [수정] 여기도 request.FILES 추가!
form = PostForm(request.POST, request.FILES, instance=post)
# ...
8. 화면 수정 (1) - 글쓰기 폼 (boards/templates/boards/board_write.html)
이 부분이 가장 중요합니다! 두 가지를 꼭 챙겨야 합니다.
- <form> 태그에 enctype="multipart/form-data" 추가.
- 수동 렌더링 방식을 사용 중이므로, 이미지 입력 필드({{ form.image }})를 직접 추가.
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label for="{{ form.title.id_for_label }}" class="form-label">제목</label>
{{ form.title }}
</div>
<div class="mb-3">
<label for="{{ form.content.id_for_label }}" class="form-label">내용</label>
{{ form.content }}
</div>
<div class="mb-3">
<label for="{{ form.image.id_for_label }}" class="form-label">첨부 이미지</label>
{{ form.image }}
</div>
</form>
9. 화면 수정 (2) - 상세 페이지 (boards/templates/boards/board_detail.html)
업로드한 이미지가 있으면 보여주도록 수정합니다.
<div class="card-body">
{% if post.image %}
<div class="mb-4 text-center">
<img src="{{ post.image.url }}" alt="첨부 이미지" class="img-fluid rounded">
</div>
{% endif %}
<div class="content">
{{ post.content|linebreaks }}
</div>
</div>
10. 테스트
- [글쓰기] 버튼을 누르면 이제 파일 선택 버튼이 보이나요?
- 이미지를 선택하고 저장해 봅니다.
- 상세 페이지에서 이미지가 잘 나오는지 확인합니다.
'Step by Step > [phase 2] django community upgrade' 카테고리의 다른 글
| 5. 메인 페이지(대시보드 & 데이터 조회) 구현 (0) | 2025.12.07 |
|---|---|
| 4. 좋아요(추천) 기능 (M:N 관계) 구현 (0) | 2025.12.02 |
| 2. 댓글 수정 및 삭제 (0) | 2025.12.01 |
| 1. 댓글 작성, 조회 기능 구현 (0) | 2025.12.01 |
| 0. 들어가기 전 (0) | 2025.12.01 |