[STEP 1] 댓글(Comment) 작성 및 조회 기능 구현
💡 핵심 개념: 1:N (일대다) 관계
게시글(Post) 하나에는 여러 개의 댓글(Comment)이 달릴 수 있죠? 이 관계를 DB에서는 Foreign Key(외래키)로 연결합니다.
- Post: 부모 (1)
- Comment: 자식 (N) -> "나는 3번 게시글에 소속된 댓글이야"라고 부모의 ID를 가지고 있습니다.
1. 댓글 모델 만들기 (boards/models.py)
boards/models.py 파일을 열고, 기존 Post 클래스 밑에 Comment 클래스를 새로 작성해 주세요.
# ... 기존 imports ...
# from django.conf import settings
# from django.db import models
# ... Board, Post 클래스 ...
class Comment(models.Model):
# [FK] 어떤 게시글에 달린 댓글인지 (Post와 1:N 관계)
# related_name='comments': 나중에 post.comments 로 댓글들을 쉽게 가져오기 위함
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', verbose_name="게시글")
# [FK] 댓글 작성자
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="작성자")
# 댓글 내용 (짧을 수 있으니 TextField 대신 CharField도 가능하지만, 넉넉하게 Text로 갑니다)
content = models.TextField(verbose_name="댓글 내용")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="작성일")
updated_at = models.DateTimeField(auto_now=True, verbose_name="수정일")
def __str__(self):
return f"[{self.post.title}] {self.content[:20]}..."
2. DB 반영 (마이그레이션)
모델을 만들었으니 DB에 테이블을 생성해야 합니다. 터미널에 입력하세요.
python manage.py makemigrations
python manage.py migrate
3. 관리자 페이지 등록 (boards/admin.py)
개발 중에 관리자 페이지에서 댓글을 관리할 수 있으면 편합니다. boards/admin.py에 Comment 모델을 등록합니다.
from django.contrib import admin
from .models import Board, Post, Comment # [수정] Comment 추가
admin.site.register(Board)
admin.site.register(Post)
admin.site.register(Comment) # [추가] 댓글 등록
4. 댓글 입력 폼 만들기 (boards/forms.py)
사용자가 댓글을 입력할 때 사용할 폼을 만듭니다. 이번에는 Bootstrap 스타일을 아예 처음부터 입혀서 만듭시다.
boards/forms.py 파일을 열고 CommentForm 클래스를 추가하세요.
# ... 기존 imports ...
from .models import Post, Comment # [수정] Comment 추가
# ... PostForm 클래스 ...
# [추가] 댓글 입력 폼
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
widgets = {
'content': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3, # 댓글창은 작게 3줄 정도
'placeholder': '댓글을 남겨보세요.'
}),
}
labels = {
'content': '' # 라벨(이름)을 없애서 깔끔하게 입력창만 보여줌
}
5. URL 설정 (boards/urls.py)
모델과 폼은 준비되었으니, 이제 URL 연결, 저장 로직(View), 화면 표시(Template) 3박자를 맞추면 끝납니다.
댓글 저장을 처리할 주소를 만듭니다. 게시글 상세 페이지(.../<int:pk>/) 아래에 comment/를 붙이는 구조가 직관적입니다.
boards/urls.py에 comment_create 경로를 추가하세요.
urlpatterns = [
# ... 기존 list, detail, write, edit, delete ...
path('<str:board_code>/<int:pk>/', views.board_detail, name='board_detail'),
# [추가] 댓글 저장 URL
path('<str:board_code>/<int:pk>/comment/', views.comment_create, name='comment_create'),
]
6. 로직 작성 (boards/views.py)
여기서 할 일은 두 가지입니다.
- board_detail 수정: 상세 페이지 들어갈 때 빈 댓글 폼(CommentForm)을 같이 가져가야 합니다.
(그래야 화면에 입력창을 그릴 수 있으니까요.) - comment_create 추가: 실제 댓글 저장 로직을 처리합니다.
boards/views.py를 열고 코드를 수정/추가해 주세요.
# [추가] CommentForm 임포트 확인
from .forms import PostForm, CommentForm
# 1. 기존 board_detail 함수 수정 (폼 전달 추가)
def board_detail(request, board_code, pk):
board = get_object_or_404(Board, code=board_code)
post = get_object_or_404(Post, pk=pk, board=board)
# 조회수 증가 로직 (기존 유지)
post.views += 1
post.save()
# [추가] 댓글 입력 폼을 생성해서 템플릿으로 보냄
comment_form = CommentForm()
context = {
'board': board,
'post': post,
'comment_form': comment_form, # 템플릿에서 {{ comment_form }}으로 쓸 수 있음
}
return render(request, 'boards/board_detail.html', context)
# 2. 댓글 저장 함수 (새로 추가)
@login_required
def comment_create(request, board_code, pk):
board = get_object_or_404(Board, code=board_code)
post = get_object_or_404(Post, pk=pk, board=board)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user # 작성자: 현재 로그인한 사람
comment.post = post # 게시글: 현재 보고 있는 글
comment.save()
# 댓글 저장 후 다시 상세 페이지로 리다이렉트
return redirect('boards:board_detail', board_code=board.code, pk=post.pk)
7. 화면에 표시하기 (boards/board_detail.html)
이제 상세 페이지 하단에 댓글 목록과 입력창을 붙입니다. board_detail.html의 맨 아래(버튼 영역 밑)에 다음 코드를 추가하세요.
<hr class="my-4">
<div class="card">
<div class="card-header">
댓글 <span class="badge bg-secondary">{{ post.comments.count }}</span>
</div>
<div class="card-body">
<ul class="list-group list-group-flush mb-3">
{% for comment in post.comments.all %}
<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>
</div>
<div class="mt-1 text-break">
{{ comment.content|linebreaks }}
</div>
</li>
{% empty %}
<li class="list-group-item text-center text-muted">
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!
</li>
{% endfor %}
</ul>
{% if user.is_authenticated %}
<form action="{% url 'boards:comment_create' board.code post.pk %}" method="post">
{% csrf_token %}
<div class="d-flex gap-2">
<div class="flex-grow-1">
{{ comment_form.content }} </div>
<button type="submit" class="btn btn-primary" style="height: fit-content;">등록</button>
</div>
</form>
{% else %}
<div class="text-center py-3 bg-light rounded">
댓글을 작성하려면 <a href="{% url 'accounts:login' %}">로그인</a>이 필요합니다.
</div>
{% endif %}
</div>
</div>
<div class="mb-5"></div>
</div>
</div>
{% endblock %}
8. 테스트
- 로그인 상태에서 게시글 상세 페이지로 들어갑니다.
- 하단에 "댓글 0" 카드가 생겼나요?
- 입력창에 내용을 쓰고 [등록] 버튼을 눌러보세요.
- 화면이 깜빡이고 방금 쓴 댓글이 리스트에 추가되었나요?
- DB 관리자 페이지(admin)나 SQLite 등에서 확인해보면 Comment 테이블에 데이터가 잘 들어갔을 겁니다.
'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 |
| 2. 댓글 수정 및 삭제 (0) | 2025.12.01 |
| 0. 들어가기 전 (0) | 2025.12.01 |