[STEP 4] 좋아요(추천) 기능 (M:N 관계)
이번 단계에서는 사용자가 게시글에 '좋아요(추천)'를 누를 수 있는 기능을 버튼을 한 번 누르면 추천이 되고, 다시 누르면 취소되는 토글(Toggle) 방식으로 구현합니다.
이 기능의 핵심은 데이터베이스의 M:N (다대다) 관계를 이해하는 것입니다.
- 1:N 관계 (댓글): 댓글은 오직 하나의 게시글에만 속할 수 있습니다.
- M:N 관계 (좋아요): 한 명의 유저가 여러 글에 좋아요를 누를 수 있고, 하나의 글도 여러 유저에게 좋아요를 받을 수 있습니다.
자바(Spring/MyBatis)에서는 이를 구현하려면 중간 테이블(Mapping Table)을 직접 만들고, 복잡한 조인 쿼리를 짜야 했지만, Django는 ManyToManyField 한 줄이면 중간 테이블 생성부터 관리까지 알아서 처리해 줍니다.
1. 모델 수정 (boards/models.py)
Post 모델에 좋아요를 누른 사람들의 목록을 저장할 필드를 추가합니다.
boards/models.py 파일을 열고 Post 클래스를 수정하세요.
# ... 기존 imports ...
from django.conf import settings # User 모델 참조용
class Post(models.Model):
# ... 기존 필드 (author, title, content, image, views 등) ...
# [추가] 좋아요 (Many-to-Many)
# User 모델과 다대다 관계를 맺습니다.
# related_name='like_posts': 나중에 유저 입장에서 "내가 좋아요 누른 글들"을 가져올 때 사용할 이름입니다.
# (주의: related_name을 안 쓰면 author 필드와 충돌이 날 수 있습니다.)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='like_posts', blank=True, verbose_name="좋아요")
def __str__(self):
return f"[{self.board.title}] {self.title}"
2. DB 반영 (마이그레이션)
Django가 자동으로 boards_post_likes 라는 중간 테이블을 만들어줍니다.
[Bash]
python manage.py makemigrations
python manage.py migrate
3. URL 설정 (boards/urls.py)
좋아요 요청을 처리할 URL을 만듭니다.
urlpatterns = [
# ... 기존 url들 ...
# [추가] 좋아요 토글 URL
path('<str:board_code>/<int:pk>/like/', views.post_like, name='post_like'),
]
4. 로직 작성 (boards/views.py)
View에서는 아주 간단한 로직만 있으면 됩니다. "이 사람이 이미 좋아요 명단에 있나? 있으면 빼고, 없으면 넣자!"
boards/views.py 하단에 함수를 추가하세요.
@login_required
def post_like(request, board_code, pk):
post = get_object_or_404(Post, pk=pk)
# [로직] 좋아요 토글 (Toggle)
# filter(id=...): 현재 게시글의 좋아요 목록에 내 아이디가 있는지 확인
if post.likes.filter(id=request.user.id).exists():
# 이미 눌렀다면 -> 취소 (remove)
post.likes.remove(request.user)
else:
# 안 눌렀다면 -> 추가 (add)
post.likes.add(request.user)
# 처리가 끝나면 상세 페이지로 다시 이동
return redirect('boards:board_detail', board_code=board_code, pk=pk)
5. 아이콘 라이브러리 추가 (templates/base.html)
하트 아이콘(❤️)을 사용하기 위해 Bootstrap Icons 라이브러리를 연결해야 합니다.
(Bootstrap 5부터는 아이콘이 별도로 분리되었습니다.)
templates/base.html의 <head> 태그 안에 아래 링크를 추가해 주세요.
<head>
<meta charset="UTF-8">
<title>My Django Board</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
6. 화면 수정 (boards/templates/boards/board_detail.html)
아래 코드를 복사해서 board_detail.html의 본문 영역 아래, 댓글 영역 위쪽에 붙여넣으세요.
<div class="text-center my-4">
{% if user.is_authenticated %}
<a href="{% url 'boards:post_like' board.code post.pk %}" class="text-decoration-none">
{% if user in post.likes.all %}
<button type="button" class="btn btn-danger">
<i class="bi bi-heart-fill"></i> 좋아요 취소
<span class="badge bg-light text-dark ms-1">{{ post.likes.count }}</span>
</button>
{% else %}
<button type="button" class="btn btn-outline-danger">
<i class="bi bi-heart"></i> 좋아요
<span class="badge bg-danger ms-1">{{ post.likes.count }}</span>
</button>
{% endif %}
</a>
{% else %}
<button type="button" class="btn btn-outline-secondary" disabled>
좋아요 <span class="badge bg-secondary ms-1">{{ post.likes.count }}</span>
</button>
<div class="small text-muted mt-1">로그인 후 추천 가능합니다.</div>
{% endif %}
</div>
<hr class="my-4">
7. 테스트 (확인 리스트)
- 로그인 상태에서 글 상세 페이지로 들어갑니다.
- [좋아요] 버튼에 빈 하트 아이콘이 잘 나오나요?
- 버튼을 누르면 화면이 새로고침 되면서 [좋아요 취소](빨간색 버튼 + 꽉 찬 하트)로 바뀌고 숫자가 1 올라가는지 확인합니다.
- 다시 누르면 취소되고 숫자가 내려가는지 확인합니다.
- 다른 아이디로 로그인해서 같은 글을 보면 숫자가 유지되어 있고, 내 기준으로는 아직 안 누른 상태로 나오는지 확인합니다.
'Step by Step > [phase 2] django community upgrade' 카테고리의 다른 글
| 6. 회원 프로필(닉네임 & 아바타) (0) | 2025.12.07 |
|---|---|
| 5. 메인 페이지(대시보드 & 데이터 조회) 구현 (0) | 2025.12.07 |
| 3. 이미지 및 파일 첨부 기능 구현 (0) | 2025.12.02 |
| 2. 댓글 수정 및 삭제 (0) | 2025.12.01 |
| 1. 댓글 작성, 조회 기능 구현 (0) | 2025.12.01 |