지난 단계에서 댓글을 작성하고 조회하는 기능(Create/Read)을 만들었습니다.
이번에는 작성자가 자신의 댓글을 수정하고 삭제할 수 있는 기능을 구현하여 댓글 시스템(CRUD)을 완성해 보겠습니다.
댓글 수정은 "페이지 이동 없이 제자리에서 수정(Ajax)"하는 방식과 "별도 페이지로 이동해서 수정"하는 방식이 있는데,
우리는 현재 배우고 있는 Django의 정석 패턴(MVT)대로 "별도 수정 페이지로 이동"하는 방식으로 구현하겠습니다.
[ STEP 2] 댓글 수정 및 삭제 (Update & Delete)
이번 단계의 핵심은 "권한 체크"입니다.
아무나 남의 댓글을 지우면 안 되기 때문에, 서버에서 "요청한 사람이 작성자와 같은지" 확인하는 로직이 반드시 필요합니다.
1. URL 설정 (boards/urls.py)
댓글을 수정하거나 삭제할 때 요청을 보낼 주소를 만듭니다. 이때 URL 패턴의 순서가 매우 중요합니다.
boards/urls.py 파일을 열고 아래와 같이 수정합니다.
from django.urls import path
from . import views
urlpatterns = [
# [중요] 'comment/...' 처럼 고정된 단어가 들어가는 패턴을 위쪽에 배치해야 합니다.
# 댓글 수정 URL (어떤 댓글인지 식별하기 위해 int:comment_pk 필요)
path('comment/<int:comment_pk>/edit/', views.comment_edit, name='comment_edit'),
# 댓글 삭제 URL
path('comment/<int:comment_pk>/delete/', views.comment_delete, name='comment_delete'),
# ... 변수(<str:board_code>)를 받는 패턴들은 아래쪽에 배치 ...
path('<str:board_code>/', views.board_list, name='board_list'),
path('<str:board_code>/write/', views.board_write, name='board_write'),
path('<str:board_code>/<int:pk>/', views.board_detail, name='board_detail'),
path('<str:board_code>/<int:pk>/edit/', views.board_edit, name='board_edit'),
path('<str:board_code>/<int:pk>/delete/', views.board_delete, name='board_delete'),
path('<str:board_code>/<int:pk>/comment/', views.comment_create, name='comment_create'),
]
⚠️ [주의] URL 순서를 지키지 않으면 발생하는 오류
만약 comment/... 패턴을 <str:board_code>/... 패턴보다 아래쪽에 적게 되면 404 에러(No Board matches the given query)가 발생합니다.
- 원인: Django는 URL을 위에서부터 순서대로 검사합니다.
- 상황: board/comment/1/edit/ 라는 주소가 들어왔을 때, 위에 있는 <str:board_code>/... 패턴이 이를 먼저 낚아채버립니다.
- 결과: Django는 'comment'라는 단어를 게시판 코드(board_code)로 인식하여 DB에서 찾으려 시도하고, 당연히 그런 게시판은 없으므로 에러가 발생합니다.
- 해결: 따라서 구체적인 단어(comment)가 있는 패턴을 변수 패턴보다 위로 올려주어야 정확하게 인식합니다.
2. 로직 작성 (boards/views.py)
View(컨트롤러)에서 실제 수정과 삭제를 처리합니다.
여기서 가장 중요한 것은 if comment.author == request.user: 조건을 통해 본인 확인을 하는 것입니다.
boards/views.py 하단에 함수 두 개를 추가합니다.
# ... 기존 imports ...
from .models import Post, Comment # Comment 모델 확인
# 1. 댓글 수정 기능
@login_required
def comment_edit(request, comment_pk):
# 수정할 댓글을 DB에서 가져옵니다.
comment = get_object_or_404(Comment, pk=comment_pk)
# [보안] 작성자가 아니면 권한 없음 에러를 띄우거나, 원래 페이지로 돌려보냅니다.
if comment.author != request.user:
return redirect('boards:board_detail', board_code=comment.post.board.code, pk=comment.post.pk)
if request.method == 'POST':
# [POST] 수정 내용을 저장할 때
# instance=comment : 기존에 있던 댓글 데이터를 폼에 채운 상태로 시작 (수정 모드)
form = CommentForm(request.POST, instance=comment)
if form.is_valid():
form.save()
# 수정이 끝나면 원래 있던 게시글 상세 페이지로 돌아갑니다.
return redirect('boards:board_detail', board_code=comment.post.board.code, pk=comment.post.pk)
else:
# [GET] 수정 폼을 보여줄 때
form = CommentForm(instance=comment)
# 댓글 수정 전용 템플릿을 렌더링합니다.
return render(request, 'boards/comment_edit.html', {'form': form, 'comment': comment})
# 2. 댓글 삭제 기능
@login_required
def comment_delete(request, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
# [보안] 작성자 본인 확인
if comment.author == request.user:
comment.delete() # DB에서 삭제
# 삭제 후 원래 있던 게시글 상세 페이지로 돌아갑니다.
return redirect('boards:board_detail', board_code=comment.post.board.code, pk=comment.post.pk)
3. 댓글 수정 화면 (boards/templates/boards/comment_edit.html)
수정 버튼을 눌렀을 때 보여질 화면을 만듭니다. 게시글 수정 화면과 비슷하지만 훨씬 간단합니다.
boards/templates/boards/ 폴더 안에 comment_edit.html 파일을 생성하세요.
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card mt-5">
<div class="card-header bg-white">
<strong>댓글 수정</strong>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<div class="mb-3">
{{ form.content }}
</div>
<div class="text-end">
<a href="{% url 'boards:board_detail' comment.post.board.code comment.post.pk %}" class="btn btn-secondary btn-sm">취소</a>
<button type="submit" class="btn btn-primary btn-sm">수정 완료</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
4. 상세 페이지에 버튼 달기 (boards/board_detail.html)
이제 댓글 목록에서 내가 쓴 댓글 옆에만 [수정], [삭제] 버튼이 보이도록 수정해야 합니다.
board_detail.html의 댓글 반복문({% for comment in ... %}) 내부를 찾아 아래와 같이 수정 버튼 영역을 추가합니다.
<li class="list-group-item">
<div class="d-flex justify-content-between">
<div>
<strong>{{ comment.author.username }}</strong>
<span class="text-muted small ms-2">{{ comment.created_at|date:"Y-m-d H:i" }}</span>
</div>
{% if user == comment.author %}
<div>
<a href="{% url 'boards:comment_edit' comment.pk %}" class="text-decoration-none small text-muted me-2">수정</a>
<a href="{% url 'boards:comment_delete' comment.pk %}"
class="text-decoration-none small text-danger"
onclick="return confirm('정말 삭제하시겠습니까?');">삭제</a>
</div>
{% endif %}
</div>
<div class="mt-1 text-break">
{{ comment.content|linebreaks }}
</div>
</li>
5. 결과 확인 (테스트)
이제 서버를 켜고 기능을 점검해 봅니다.
- 로그인 후 게시글 상세 페이지로 이동합니다.
- 본인 확인: 내가 쓴 댓글에는 [수정/삭제] 버튼이 보이고, 다른 사람 댓글에는 안 보이는지 확인합니다.
- 수정: [수정] 버튼 클릭 -> 수정 페이지 이동 -> 내용 변경 후 저장 -> 상세 페이지로 잘 돌아오는지 확인합니다.
- 삭제: [삭제] 버튼 클릭 -> "정말 삭제하시겠습니까?" 확인 -> 댓글이 목록에서 사라지는지 확인합니다.
'Step by Step > [phase 2] django community upgrade' 카테고리의 다른 글
| 5. 메인 페이지(대시보드 & 데이터 조회) 구현 (0) | 2025.12.07 |
|---|---|
| 4. 좋아요(추천) 기능 (M:N 관계) 구현 (0) | 2025.12.02 |
| 3. 이미지 및 파일 첨부 기능 구현 (0) | 2025.12.02 |
| 1. 댓글 작성, 조회 기능 구현 (0) | 2025.12.01 |
| 0. 들어가기 전 (0) | 2025.12.01 |