1. 댓글 작성, 조회 기능 구현

[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.pyComment 모델을 등록합니다.

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.pycomment_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)

여기서 할 일은 두 가지입니다.

  1. board_detail 수정: 상세 페이지 들어갈 때 빈 댓글 폼(CommentForm)을 같이 가져가야 합니다.
    (그래야 화면에 입력창을 그릴 수 있으니까요.)
  2. 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. 테스트

  1. 로그인 상태에서 게시글 상세 페이지로 들어갑니다.
  2. 하단에 "댓글 0" 카드가 생겼나요?
  3. 입력창에 내용을 쓰고 [등록] 버튼을 눌러보세요.
  4. 화면이 깜빡이고 방금 쓴 댓글이 리스트에 추가되었나요?
  5. DB 관리자 페이지(admin)나 SQLite 등에서 확인해보면 Comment 테이블에 데이터가 잘 들어갔을 겁니다.