Springboot 게시물 좋아요 기능

조정우·2025년 4월 7일
0

오늘의 할일 : 게시글 좋아요 기능 만들기

1.게시물 좋아요 DB 만들기

like_boardidx : 해당 게시글 번호
like_useridx : 현재 로그인 한 유저의 번호
like_at : 좋아요 누른 시간

2.Board_like Entity 만들기

public class Board_like {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long likeIdx;

    @Column(nullable = false)
    private Long likeBoardidx;

    @Column(nullable = false)
    private Long likeUseridx;

    @Column(nullable = false)
    private LocalDateTime likeAt;
}

3.Board_like Repository 만들기

findByLikeBoardidxAndLikeUseridx 는 현재 찜한 상태를 체크합니다

public interface BoardLikeRepository extends JpaRepository<Board_like,Long> {
    Optional<Board_like> findByLikeBoardidxAndLikeUseridx(Long likeBoardidx,Long likeUseridx);
}

4.Board_like Service 만들기

페이지가 로드 됐을 때 찜한 상태를 체크한다

    public boolean isBoardLiked(Long boardidx , Long useridx) {
        Optional<Board_like> existingBoardLike = boardLikeRepository.findByLikeBoardidxAndLikeUseridx(boardidx,useridx);
        return existingBoardLike.isPresent();
    }

실제로 찜하기를 누르면 LikeCount가 올라가고 내려가는 API
existingLike 로 DB안에 있는 유저의 정보랑 게시물 정보를 찾아서 가져온다
가져온 값으로 만약 있으면 isPresent 기존 값을 삭제 시키고 board에서 좋아요 수 감소
근데 만약 없으면 좋아요 수 증가하고 boardLike DB에 정보를 저장해준다

    // 찜하기를 누르면 실제로 저장 되는 것
    @Transactional
    public boolean addLike(Long boardidx,Long useridx) {

        Board board = boardRepository.findByBoardIdx(boardidx)
                .orElseThrow(() -> new RuntimeException("게시글을 찾을 수 없습니다."));

        Optional<Board_like> existingLike = boardLikeRepository.findByLikeBoardidxAndLikeUseridx(boardidx, useridx);

        if (existingLike.isPresent()) {
            // 이미 좋아요를 눌렀으면 취소
            board.setBoardLike(board.getBoardLike() - 1); // 좋아요 수 감소
            boardRepository.save(board);

            boardLikeRepository.delete(existingLike.get()); // 좋아요 삭제
            return false; // 좋아요 취소 상태 반환
        } else {
            // 좋아요 추가
            board.setBoardLike(board.getBoardLike() + 1); // 좋아요 수 증가
            board.setBoardView(board.getBoardView() + 1);
            boardRepository.save(board);

            Board_like boardLike = new Board_like(); // ✅ 객체 생성이 빠졌음
            boardLike.setLikeBoardidx(boardidx);
            boardLike.setLikeUseridx(useridx);
            boardLike.setLikeAt(LocalDateTime.now());

            boardLikeRepository.save(boardLike);
            return true; // 좋아요 추가 상태 반환
        }
    }

5.Board_like Controller 만들기

페이지 로드시 좋아요했는지를 체크 및 로그인 유무를 체크 한다
ResponseEntity를 사용해서 Spring에서 HTTP응답시 값을 받아온다
해당 게시글 boardIdx 값을 받아온다 그리고 현재 로인한 유저의 정보를 찾는다
그값을 Service에 있는 boardLiked에 보내서 값을 받아온다
받아온 값이 true이면 liked , 아니면 not_liked로 JSON data로 보내준다
likeCount 는 눌렀을때 실시간으로 개수를 보여주기 위해서 map으로 저장해서 보내준다

@GetMapping("/board/like-status/{boardIdx}")
    public ResponseEntity<Map<String, Object>> checkProductLike(@PathVariable Long boardIdx, Principal principal) {
        Map<String, Object> response = new HashMap<>();

        // 로그인 확인
        if (principal == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(Map.of("status", "error", "message", "로그인이 필요합니다."));
        }

        // 로그인한 유저 정보 가져오기
        String username = principal.getName();
        Member member = memberRepository.findByUserId(username)
                .orElseThrow(() -> new RuntimeException("유저 정보를 찾을 수 없습니다."));
        Long useridx = member.getIdx();

        // 찜 개수 조회
        Long likeCount = boardRepository.findByBoardIdx(boardIdx)
                .map(Board::getBoardLike) // 좋아요 개수 가져오기
                .orElse(0L);
        response.put("likeCount", likeCount);

        // 찜 상태 확인
        boolean isLiked = boardLikeService.isBoardLiked(boardIdx, useridx);
        response.put("status", isLiked ? "liked" : "not_liked");

        return ResponseEntity.ok(response);
    }

@PostMapping로 위에 코드와 비슷하게 해준다 현재 누른 찜하기의 상태를 체크한다
만약 이미 찜이 눌려저 있는 상태에서 누르면 removed 아니면 added를 반환한다

이후 클릭시 POST를 값으로 로그인을 하지않았을 경우 alert로 경고 메세지를 보내주고
로그인 시 클릭하면 data.status 로 검사를 한다 그값에 맞게 기능을 해준다 그리고 실시간으로 클릭하고 취소하면 likeCountSpan.textContent 좋아요 갯수를 보여준다

@PostMapping("/board/like/{boardIdx}")
    public ResponseEntity<Map<String, Object>> toggleProductLike(@PathVariable Long boardIdx, Principal principal) {
        Map<String, Object> response = new HashMap<>();

        // 로그인 확인
        if (principal == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(Map.of("status", "error", "message", "로그인이 필요합니다."));
        }

        // 로그인한 유저 정보 가져오기
        String username = principal.getName();
        Member member = memberRepository.findByUserId(username)
                .orElseThrow(() -> new RuntimeException("유저 정보를 찾을 수 없습니다."));
        Long useridx = member.getIdx();

        // 찜 개수 조회
        Long likeCount = boardRepository.findByBoardIdx(boardIdx)
                .map(Board::getBoardLike) // 좋아요 개수 가져오기
                .orElse(0L);
        response.put("likeCount", likeCount);

        // 찜 상태 토글
        boolean isLiked = boardLikeService.addLike(boardIdx, useridx);
        response.put("status", isLiked ? "added" : "removed");

        return ResponseEntity.ok(response);
    }

6.board_like html JSON으로 값 보내기

각 해당하는 게시물을 클릭했을때 forEach를 사용함 GET 페이지 로드시에 data.status 값을 검사한다 만약 liked이면 ❤️ 아니면 🖤 그리고 data.likeCount 이거는 실시간으로 값이 증가하는것이다

document.addEventListener("DOMContentLoaded",function (){
        const btns = document.querySelectorAll(".boardLike-btn");

        btns.forEach(btn => {
            const boardIdx = btn.dataset.boardIdx;
            const likeCountSpan = btn.querySelector(".board-like-count");

               fetch(`/board/like-status/${boardIdx}`,{
                  method: "GET",
                  headers: {
                      "Content-Type" : "application/json"
                  }
               })
                   .then(response => response.json())
                   .then(data => {
                      if(data.status === "liked"){
                          btn.textContent = "❤️";
                          likeCountSpan.textContent = data.likeCount;
                      } else if(data.status === "not_liked"){
                          btn.textContent = "🖤";
                          likeCountSpan.textContent = data.likeCount;
                      }
                   })
                   .catch(error => console.error("Error :", error));

               btn.addEventListener("click",function (){
                  fetch(`/board/like/${boardIdx}`, {
                     method: "POST",
                     headers: {
                         "Content-Type":"application/json"
                     }
                  })
                      .then(response => {
                          if(response.status === 401){
                              alert("로그인을 해주시기 바랍니다");
                          }
                          return response.json();
                      })
                      .then(data => {
                         if(data.status === "added"){
                             btn.textContent = "❤️";
                             likeCountSpan.textContent = Number(likeCountSpan.textContent) + 1;
                         } else if(data.status === "removed"){
                             btn.textContent = "🖤";
                             likeCountSpan.textContent = Number(likeCountSpan.textContent) - 1;
                         }
                      })
                      .catch(error => console.error("Error :", error));
            });
        });
    });

7. 구현 이미지

로그인 하지 않았을 경우 모든 좋아요 가 ❤️ 모양

로그인을 했을 경우 자기가 누르지 않은 것은 🖤

로그인을 했을 경우 자기가 누른 것은 ❤️

DB에는 이런식으로 들어온다

profile
)개발( 마구잡이로 글쓰기

0개의 댓글