본문 바로가기

공부 일지 #43 | Django 실습2: 글 & 이미지 업로드 절차

@studying:)2025. 9. 30. 11:52

학습 날짜: 2025.09.29


 

공부 일지 #42 | Flask & Django 실습

학습 날짜: 2025.09.22 ~ 2025.09.26🔷 Flask 학습 정리1. Bootstrap12 Grid → 한 행을 12등분한다고 생각하면 된다.Grid의 max-width → xs, sm, md, lg, xl, xxl 순으로 커진다.이미지 넣기static 디렉토리 생성이미지 파

whatistudied.tistory.com

 

🔷 Django 이어서

1. ImageField와 Pillow

  • Django의 ImageField를 사용하면 이미지를 업로드할 수 있는 형태
  • 단, 이미지를 다루려면 Pillow 라이브러리가 반드시 필요
    • 터미널에 → pip install Pillow
  • Pillow는 이미지를 열고, 크기 조정, 편집, 저장 등을 지원하는 라이브러리
  • 업로드한 이미지는 media 폴더 안의 post 폴더에 저장되고 Django가 경로를 관리하는 구조
    → 마치 웹디스크처럼 별도 함수 없이 이미지 URL로 접근 가능.

2. Media URL 연결

  • 이미지를 불러오기 위해 urls.py에 media 경로 설정 필요
# urls.py

from django.conf import settings
from django.conf.urls.static import static

urlpatterns += static(
    prefix=settings.MEDIA_URL,
    document_root=settings.MEDIA_ROOT,
)

3. Admin에 썸네일 표시

  • admin 페이지에서 Post 모델 등록 시 list_display 옵션으로 썸네일 출력 가능
# admin.py
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "thumbnail"]

4. 글 리스트에 썸네일 출력

<!-- post_list.html -->
{% for post in posts %}
<li>
    <h2>{{ post.title }}</h2>
    <p>{{ post.content | linebreaksbr }}</p>
    <div>
        {% for comment in post.comment_set.all %}
            <li>{{ comment.content }}</li>
        {% empty %}
            <li>댓글이 없음</li>
        {% endfor %}
    </div>
    {% if post.thumbnail %}
        <img src="{{ post.thumbnail.url }}">
    {% else %}
        <img src=" ">
    {% endif %}
</li>
<hr>
{% endfor %}
  • 줄바꿈 적용을 위해 linebreaksbr 필터 사용

5. 글을 클릭하면 상세 페이지 나오게 하기

a. views.py - 상세 페이지 함수 추가

# views.py
def post_detail(request, post_id):
    return render(request, "post_detail.html")

 

b. urls.py - 상세 페이지 링크 추가

# urls.py
urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
    path("posts/", post_list),
    path("posts/<int:post_id>/", post_detail), # 추가!
]

 

c. 글 제목에 링크 연결

<!-- post_list.html -->
<h2><a href="/posts/{{ post.id }}/">{{ post.title }}</a></h2>

6. 글 작성 기능 추가

a. html 파일 생성 (post_add.html)

<!-- post_add.html -->
    <form method="POST"  enctype="multipart/form-data">
        {% csrf_token %} <!-- 중요!! -->
        <div>
            <label>제목</label>
            <input type="text" name="title" placeholder="제목을 입력하세요."></input>
        </div><br><br>
        <div>
            <label>내용</label>
            <textarea name = "content" cols="30" rows="10" placeholder="내용을 입력하세요."></textarea>
        </div>
        <div>
            <label>썸네일</label>
            <input type="file" name="thumbnail">
        </div>
        <div>
            <button type="submit">작성하기</button>
        </div>
    </form>
  • POST 요청 시 CSRF 검증이 자동 수행되므로 {% csrf_token %} 삽입 필수
  • CSRF(크로스 사이트 요청 위조)? 인증된 사용자가 자신의 의지와는 무관하게 공격자가 원하는 특정 요청을 웹사이트에 보내게 만드는 보안 취약점
  • 만약 생략하면, Forbidden (403), CSRF verification failed. Request aborted. 오류가 나타남.

 

b. views.py - 글 작성 페이지 함수 추가

  • 클라이언트는 무조건 먼저 GET 방식으로 접근하는 구조
  • 따라서 title = request.POST['title'] 같은 코드를 그대로 쓰면 페이지를에 접속했을 때, 값이 없어서 MultiValueDictKeyError 발생 가능성 있음
  • Django에서는 GET 요청과 POST 요청을 분리해서 처리하는 방식 필요
# views.py
# 입력받은 정보를 테이블(DB)에 입력하려면, models에 있는 object(Post)의 도움을 받아야 함.
from blog.models import Post 
from django.shortcuts import render, redirect

def post_add(request):
    if request.method == "POST":
        title = request.POST['title']
        content = request.POST['content']
        thumbnail = request.FILES.get("thumbnail") # 이미지 파일은 request.FILES로 처리

        post = Post.objects.create(
            title=title,
            content=content,
            thumbnail=thumbnail
        ) # 작성한 내용 DB에 추가
        
        return redirect(f"/posts/{post.id}/") # 작성 후 상세 페이지로 이동, 앞뒤로 / 꼭꼭 붙여야함!!!
        
    else:
    	return render(request, "post_add.html")

 

c. urls.py - 글 작성 페이지 링크 추가

# urls.py
urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
    path("index/", index),
    path("posts/", post_list),
    path("posts/<int:post_id>/", post_detail),
    path("posts/add/", post_add) # 추가!!
]

7. 상세페이지에 댓글 기능 추가

a. 상세 페이지 html 수정

<!-- post_detail.html -->
        <form method="POST">
            {% csrf_token %}
            <label>댓글: </label>
            <textarea name = "comment" cols="40" rows="3" placeholder="내용을 입력하세요."></textarea>
            <button type="submit">댓글 달기</button>
        </form>

 

b. views.py - 상세 페이지 함수 수정

# views.py
def post_detail(request, post_id):
    # SELECT * FROM blog_post WHERE id=post_id
    post = Post.objects.get(id=post_id)

    if request.method == "POST":
        comment = request.POST['comment']
        Comment.objects.create(post = post, content = comment)

    context = { "post": post }
    return render(request, "post_detail.html", context)

🔳 Python shell 사용하기

1. 접속 방법

  • Pycharm 터미널에서
python manage.py shell
  • Django의 ORM을 직접 테스트할 수 있는 인터랙티브 환경 → SQL문을 작성하지 않고 Python 코드로 DB를 조작할 수 있는 장점이 있음

2. 테스트 예시

  • blog App에는 두 가지 모델(Post, Comment)이 있음
  • Post 테스트: DB에 데이터가 정상적으로 들어가는지 확인하는 과정
from blog.models import Post

# 객체 생성
Post.objects.create(title="test", content="testcontent")

# 최신순 정렬
Post.objects.order_by("-id")
  • Comment 테스트: ForeignKey가 없는 상태에서 Comment를 생성하면 에러가 발생하므로, post 객체와 함께 생성해야 하는 구조 확인
from blog.models import Comment, Post

# 댓글 생성 (FK 필요)
post = Post.objects.get(id=1)
Comment.objects.create(post=post, content="test comment")

# 모든 댓글 삭제
Comment.objects.all().delete()

 

👉 터미널을 사용하는 목적은 ORM 구문을 빠르게 시험해보고, 실제 DB 반영 전 동작을 확인하기 위한 실습 환경이라는 개념


⭐ 보충 개념 정리

  • media 폴더 구조: 업로드된 파일은 media 폴더에 저장됨. 로컬에 있을 수도, 클라우드에 있을 수도 있는 구조
  • 이미지 처리 방식: 이미지는 binary 데이터로 전달되고, Django가 URL 경로로 관리하는 구조
  • ORM 자동 처리: Post.objects.create(...) 같은 메서드가 대부분 자동 처리해주어 ORM이 좋아보이기도 하지만, SQL문으로 직접 개발하는 경우도 있기 때문에 항상 좋은 건 아님
  • 마이그레이션/버전관리 규칙: 모델 변경시 makemigrations → migrate 순서 준수하기!
  • redirect 시 주의: redirect("/posts/")처럼 앞뒤로 /를 반드시 붙여야 하는 특징. 잘못 작성하면 URL 매칭 에러가 발생할 수 있음.
  • 글 작성(post_add) 요청 처리 절차

studying:)
@studying:) :: what i studied

studying:) 님의 학습 여정을 기록하는 블로그입니다.

목차