티스토리 뷰

회원로직, 게시글, 댓글 CRUD까지 구현 완료했으니 이젠 댓글 좋아요 기능을 구현해보겠다.

 

Entity 설계

package com.example.MyFreshmanCommunity.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@ToString
@Entity
public class LikeComment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "member_id")
    @OnDelete(action = OnDeleteAction.CASCADE) //멤버가 지워지면, likeComment 레코드도 삭제됨
    private Member member;

    @ManyToOne
    @JoinColumn(name = "comment_id")
    private Comment comment;

    @Column(nullable = false)
    private boolean status;

    public static LikeComment createLike(Member member, Comment comment) {
        return new LikeComment(
                null,
                member,
                comment,
                true
        );
    }

    public void deleteLike(Comment comment) {
        this.status = false;
    }
}

 

member가 지워지면, likeComment 레코드도 사라질 수 있도록 하기 위해 외래키 제약조건 CASCADE를 사용해주었다.

 

또한 boolean 타입의 status가 있는데, 이는 좋아요 상태를 나타내주는 값이다. 좋아요를 눌렀으면 true, 좋아요가 누르지 않았거나 취소했다면 false 이다. 이를 통해 각 댓글에 대한 사용자의 좋아요 상태를 명확하게 관리할 수 있다.

 

또한 createLike, deleteLike 두 개의 메소드를 만들어서 새로운 좋아요(LikeComment) 객체를 생성하고 초기 상태를 '좋아요 됨'(true)으로 설정하거나, Comment에 대한 좋아요의 상태를 '좋아요 취소됨'(false)으로 변경할 수 있도록 해주었다.


DTO

 

LikeDto

package com.example.MyFreshmanCommunity.dto;

import com.example.MyFreshmanCommunity.entity.LikeComment;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LikeDto {
    private String message;
    private boolean status;
    
    public static LikeDto createLikeDto(String message, LikeComment likeComment) {
        return new LikeDto(
                message,
                likeComment.isStatus()
        );
    }
}

 

LikeDto는 서버에서 클라이언트로 데이터를 전송하는 데 사용되는 dto로, 

좋아요 처리가 되었다는 message와 좋아요 상태(true, false)를 반환한다.

 


Repository

 

LikeCommentRepository

 

package com.example.MyFreshmanCommunity.repository;

import com.example.MyFreshmanCommunity.entity.Comment;
import com.example.MyFreshmanCommunity.entity.LikeComment;
import com.example.MyFreshmanCommunity.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface LikeCommentRepository extends JpaRepository<LikeComment, Long> {

    LikeComment findByMemberAndComment(Member member, Comment comment);

    void deleteByMemberAndComment(Member member, Comment comment);
}

 

특정 Member(사용자)와 Comment(댓글) 조합으로 좋아요 객체(LikeComment)를 찾아 반환하는 findByMemberAndComment 메서드를 만들어주었다. 이 메서드는 특정 사용자가 특정 댓글에 대해 좋아요를 했는지 확인하는 데 사용된다.

 

반면, deleteByMemberAndComment는 특정 Member(사용자)와 Comment(댓글) 조합에 해당하는 좋아요를 데이터베이스에서 삭제하는 메서드이다. 사용자가 댓글에 대한 좋아요를 취소할 때 이 메서드가 사용된다.

 


Service

 

LikeCommentService

package com.example.MyFreshmanCommunity.service;

import com.example.MyFreshmanCommunity.dto.LikeDto;
import com.example.MyFreshmanCommunity.entity.Comment;
import com.example.MyFreshmanCommunity.entity.LikeComment;
import com.example.MyFreshmanCommunity.entity.Member;
import com.example.MyFreshmanCommunity.exception.MemberNotFoundException;
import com.example.MyFreshmanCommunity.exception.NotFoundException;
import com.example.MyFreshmanCommunity.repository.CommentRepository;
import com.example.MyFreshmanCommunity.repository.LikeCommentRepository;
import jakarta.servlet.http.HttpSession;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class LikeCommentService {

    private final CommentService commentService;
    private final CommentRepository commentRepository;
    private final LikeCommentRepository likeCommentRepository;

    @Transactional
    public LikeDto addLike(Long commentId, HttpSession session) {
        Member member = (Member) session.getAttribute("member");
        Comment comment = commentRepository.findById(commentId).
                orElseThrow(() -> new NotFoundException("대상 댓글이 없습니다."));

        if(member == null) throw new MemberNotFoundException("로그인하지 않은 상태에선 좋아요를 누를 수 없습니다.");

        if (likeCommentRepository.findByMemberAndComment(member, comment) == null) { //좋아요가 없으면
            comment.setLikesCount(comment.getLikesCount() + 1); //1씩 증가
            LikeComment likeComment = LikeComment.createLike(member, comment); //true처리
            likeCommentRepository.save(likeComment);
            return LikeDto.createLikeDto("좋아요 처리 완료", likeComment);
        }
        else { //이미 좋아요 눌렀으면
            comment.setLikesCount(comment.getLikesCount() - 1);
            LikeComment likeComment = likeCommentRepository.findByMemberAndComment(member, comment);
            likeComment.deleteLike(comment); //false처리
            likeCommentRepository.deleteByMemberAndComment(member, comment);
            return LikeDto.createLikeDto("좋아요 취소 완료", likeComment);
        }
    }
}

 

사용자가 댓글에 좋아요를 추가하거나 취소할 때 사용되는 addLike 메서드는 사용자 세션에서 멤버 정보를 가져오고, 댓글 ID를 사용하여 해당 댓글을 찾는다.

좋아요가 아직 없으면  해당 댓글의 좋아요 수를 1만큼 증가시켜 주고 새로운 좋아요를 추가하고,

이미 좋아요를 눌렀다면 좋아요를 취소하는 것이므로 해당 댓글의 좋아요 수를 1만큼 감소시켜주고, 좋아요를 취소해준다. (LikeComment 테이블에서 삭제)

 


Controller

 

LikeCommentApiController

package com.example.MyFreshmanCommunity.api;

import com.example.MyFreshmanCommunity.dto.LikeDto;
import com.example.MyFreshmanCommunity.service.LikeCommentService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class LikeCommentApiController {

    private final LikeCommentService likeCommentService;

    @PostMapping("/comment/{commentId}/like")
    public ResponseEntity<LikeDto> addLike(@PathVariable Long commentId, HttpServletRequest request) {

        HttpSession session = request.getSession();
        LikeDto like = likeCommentService.addLike(commentId, session);
        return ResponseEntity.status(HttpStatus.OK).body(like);
    }

}

 

@PathVariable을 사용하여 URL 경로의 일부(commentId) 를 메서드의 파라미터로 바인딩해주었다.

 


Postman을 통한 데이터 확인

postman을 통해 데이터가 잘 넘어가는지 확인해주었다.

댓글 좋아요
댓글 좋아요 취소
댓글 좋아요 실패

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함