개발 일지 6 - 댓글 기능
댓글 기능을 완전히 구현을 하지 못했다. 코드를 하나하나 작성을 하다 보니까 어떤 문제가 생겼는지 감이 온다.
하지만 5시간을 이 문제를 붙잡고 있어서 지금까지 구현한 기능에 대해 이야기를 하려고 한다.
댓글과 대댓글을 구현을 하려고 했다.
계속 사람들이 서로 대화를 할 수 있게끔 댓글이 무한히 이어지게끔 구현을 하고자 했다.
그래서 만든 데이터 베이스가 3가지다. comment, comment_reply, like다. like DB는 좋아요 기능을 넣기 위해서 일단 만들기만 해두었다. 오늘은 앞선 2가지 DB를 사용하였다.
class Comment(models.Model):
post = models.ForeignKey(Post_information, related_name='comments', on_delete=models.CASCADE) # 게시물과의 관계
author = models.ForeignKey(User_information, on_delete=models.CASCADE) # 작성자
content = models.TextField(null=False) # 댓글 내용
created_at = models.DateTimeField(auto_now_add=True) # 작성 시간
likes = models.IntegerField(default=0) # 좋아요 수
def __str__(self):
return f'{self.author.nickname}: {self.content[:20]}'
class Comment_Reply(models.Model):
parent = models.ForeignKey('self', null=True, blank=True, related_name='replies', on_delete=models.CASCADE)
comment = models.ForeignKey(Comment, related_name='replies', on_delete=models.CASCADE) # 댓글과의 관계
author = models.ForeignKey(User_information, on_delete=models.CASCADE) # 작성자
content = models.TextField(null=False) # 대댓글 내용
created_at = models.DateTimeField(auto_now_add=True) # 작성 시간
likes = models.IntegerField(default=0) # 좋아요 수
def __str__(self):
return f'{self.author.nickname}: {self.content[:20]}'
이렇게 데이터 베이스의 구조를 짰다. Comment_Reply에 parent요소는 원래 존재하지 않았다. 실제로 대댓글을 작성할때까지는 문제가 발생하지 않았다.
view.py에서 댓글을 저장하는 함수를 구현하고 그 값을 post_detail에 넣어서 화면에 출력이 되게끔 구현을 했다.
def save_comment(request, pk): #댓글 저장
post = get_object_or_404(Post_information, pk=pk)
print(post)
print(request.POST)
user_id = request.session.get('username')
if request.method == 'POST':
if 'comment_submit' in request.POST:
print("save commmmmment")
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.author = User_information.objects.get(username=user_id)
new_comment.post = post
new_comment.save()
print(new_comment)
return redirect('post_detail', pk=pk)
else:
print(comment_form.errors)
elif 'reply_submit' in request.POST:
reply_form = CommentReplyForm(request.POST)
if reply_form.is_valid():
new_reply = reply_form.save(commit=False)
parent_comment_id = request.POST.get('comment_id')
print(f"Received comment_id: {parent_comment_id}")
if parent_comment_id:
parent_comment = get_object_or_404(Comment, id=parent_comment_id)
new_reply.comment = parent_comment
else:
parent_reply_id = request.POST.get('parent_reply_id')
print(f"Received parent_reply_id: {parent_reply_id}")
parent_reply = None
if parent_reply_id:
parent_reply = get_object_or_404(Comment_Reply, id=parent_reply_id)
# 최상위 부모 댓글을 찾는 로직
while parent_reply.parent: # 부모가 있을 경우 계속 올라감
parent_reply = parent_reply.parent
# 최상위 부모 댓글을 new_reply.comment에 설정
new_reply.comment = parent_reply.comment
# 여기서 parent_reply.comment는 최상위 부모 댓글
new_reply.author = User_information.objects.get(username=user_id)
new_reply.parent = parent_reply
new_reply.save()
return redirect('post_detail', pk=pk)
처음 댓글을 작성하는 할 때 commit_submit 값이 있으면 Comment DB에 저장이 되게끔 구현을 해두었다. 대댓글은 원래 저렇게 복잡하지 않았다. 대댓글의 대댓글을 다는 과정에서 저렇게 코드가 복잡하게 꼬여 버렸다.
if parent_comment_id 이 부분은 댓글에 대댓글을 다는 경우 실행이 되는 조건문이다.
대댓글이 아닌 대댓글의 대댓글을 다는 경우에는 comment_reply의 parent 값을 가져와야 했다. 대댓글의 위치를 가져와야 했다. 그게 저 아래로 쭉 적혀있는 코드이다.
뜬금없이 while문이 나온 이유는 new_reply.comment 즉 comment_reply의 comment 부분이 Comment의 외래키로 연결이 되어있어서 Comment DB의 부모 값 즉, 가장 첫번째로 적힌 댓글의 값을 찾아서 가져오는 기능이다.
아 여기까지 구현을 하고 이제 다 끝났다고 생각을 했다.
<ul class="comments-list">
{% for comment in post.comments.all %}
<li>
<strong>{{ comment.author.nickname }}</strong> {{ comment.content }}
<span>{{ comment.created_at|date:"Y-m-d H:i" }}</span>
<span>좋아요: {{ comment.likes }}</span>
<button onclick="likeComment({{ comment.id }})">좋아요</button>
{% if user in user_check %}
<!-- 대댓글 버튼 -->
<button onclick="toggleReplyForm({{ comment.id }},false)">대댓글</button>
<!-- 대댓글 작성 폼 (숨김 처리) -->
<div id="reply-form-{{ comment.id }}" style="display: none;">
<form method="post" action="{% url 'save_comment' post.pk %}">
{% csrf_token %}
<textarea name="content" placeholder="대댓글을 입력하세요." rows="2"></textarea>
<input type="hidden" name="comment_id" value="{{ comment.id }}">
<button type="submit" name="reply_submit">대댓글 작성</button>
</form>
</div>
{% endif %}
<ul class="replies-list">
{% for reply in comment.replies.all %}
<li>
<strong>{{ reply.author.nickname }}</strong> {{ reply.content }}
<span>{{ reply.created_at|date:"Y-m-d H:i" }}</span>
<span>좋아요: {{ reply.likes }}</span>
<span>{{reply.id}}</span>
<button onclick="likeComment({{ comment.id }})">좋아요</button>
{% if user in user_check %}
<!-- 대댓글 버튼 -->
<button onclick="toggleReplyForm({{ reply.id }},true)">대댓글</button>
<!-- 대댓글 작성 폼 (숨김 처리) -->
<div id="reply-form-{{ reply.id }}" style="display: none;">
<form method="post" action="{% url 'save_comment' post.pk %}">
{% csrf_token %}
<textarea name="content" placeholder="대댓글을 입력하세요." rows="2"></textarea>
<input type="hidden" name="parent_reply_id" value="{{ reply.id }}">
<button type="submit" name="reply_submit">대댓글 작성</button>
</form>
</div>
{% endif %}
<ul class="replies-list" style="margin-left: 20px;"> <!-- 대댓글의 대댓글을 다시 들여쓰기 -->
{% for sub_reply in reply.replies.all %}
<li>
<strong>{{ sub_reply.author.nickname }}</strong> {{ sub_reply.content }}
<span>{{ sub_reply.created_at|date:"Y-m-d H:i" }}</span>
<span>좋아요: {{ sub_reply.likes }}</span>
<button onclick="likeComment({{ sub_reply.id }})">좋아요</button>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
대댓글의 대댓글을 구현을 하고 나니 코드가 말도 안되게 길어지기 시작했고 반복되는 부분이 너무 많다고 생각이 들었다.
그제서야 잘 못되었음을 느꼈다. 그럼 대댓글의 대댓글의 대댓글 그 이후로 무한히 이어지게 하기 위해서는 내가 일일히 코드를 하나씩 짜주어야 한다.
이건 아니라는 것을 느꼈고 방법은 잘 모르겠지만 다른 방법이 있을 것 같다. 그 코드에 대해 고민을 좀 해봐야 할 것 같다.