-
[무한댓글 게시판] 6. 조회수 / 좋아요Spring Boot 2024. 2. 22. 15:37
조회수
게시글의 상세페이지로 이동하는 동시에 조회수가 올라가야하는 거니까 게시글 상세 이동 메서드에 로직을 추가해줬다.
동영상 시연
좋아요
좋아요 기능구현을 하기 위해 좋아요의 상태를 담기위해 따로 테이블이 필요할 것 같아서 likes 테이블 설계를 하고 테이브블 생성을 해주었다.
create table likes( useremail varchar(25), board_id int, PRIMARY KEY (useremail, board_id) );
likes 테이블은 단일 식별자가 아닌 user 테이블의 PK useremail과 board 테이블의 PK board_id 값을 Likes 테이블을 복합키로 설정해주었다.
Likes.java
Likes entity
package com.se.social.entity; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Transient; import com.se.social.domain.LikesId; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Table(name = "likes") @Data @AllArgsConstructor @NoArgsConstructor @IdClass(LikesId.class) public class Likes implements Serializable { @Id private String useremail; @Id @Column(name = "board_id") private int board_id; }
LikesId.java
LikesId domain
package com.se.social.domain; import java.io.Serializable; import javax.persistence.Id; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class LikesId implements Serializable { private static final long serialVersionUID = 1L; // 기본값(초기화) private String useremail; private int board_id; }
LikesRepository.java
LikesRepository도 생성해주었다.
package com.se.social.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import com.se.social.domain.LikesId; import com.se.social.entity.Likes; import com.se.social.entity.QLikes; import com.querydsl.jpa.impl.JPAQueryFactory; public interface LikesRepository extends JpaRepository<Likes, LikesId> { }
LikesService.java
package com.se.social.service; import com.se.social.domain.LikesId; import com.se.social.entity.Likes; public interface LikesService { // selectDetail public Likes selectDetail(LikesId likesId); // insert, update public int save(Likes entity); // delete public LikesId delete(LikesId likesId); }
LikesService.java
package com.se.social.service; import org.springframework.stereotype.Service; import com.se.social.domain.LikesId; import com.se.social.entity.Likes; import com.se.social.repository.LikesRepository; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class LikesServiceImpl implements LikesService { private final LikesRepository repository; // selectDetail @Override public Likes selectDetail(LikesId likesId) { return repository.findById(likesId).orElse(null); } // insert, update @Override public int save(Likes entity) { repository.save(entity); return entity.getBoard_id(); } // delete @Override public LikesId delete(LikesId likesId) { repository.deleteById(likesId); return likesId; } }
LikesService와 LikesServiceImpl을 만들어주었고,
여기서 주요하게 봐야할 점은 . selectDetail 메서드.
selectDetail 메서드를 생성할때,Likes 타입으로 만들어주고 타입도 같은 타입으로 반환해준다.
매개변수는 복합키를 만들어주었던 LikesId를 받아서 복합키를 찾아서 반환해준다. 없다면 null을 반환.
BoardController.java
// 좋아요 @PostMapping("/toggle") @ResponseBody public ResponseEntity<?> toggleLike(@RequestBody Likes entity) { try { LikesId id = new LikesId(entity.getUseremail(), entity.getBoard_id()); Board boardEntity = boardService.selectDetail(entity.getBoard_id()); if (boardEntity != null) { Likes existingLike = likesService.selectDetail(id); if (existingLike == null) { // 좋아요 추가 boardEntity.setBoard_likes(boardEntity.getBoard_likes() + 1); likesService.save(entity); boardService.save(boardEntity); // 트랜잭션 처리 System.out.println("좋아요 추가 완료. 현재 좋아요 개수: " + boardEntity.getBoard_likes()); return ResponseEntity.ok().build(); } else { // 좋아요 취소 boardEntity.setBoard_likes(boardEntity.getBoard_likes() - 1); likesService.delete(id); boardService.save(boardEntity); // 트랜잭션 처리 System.out.println("좋아요 취소 완료. 현재 좋아요 개수: " + boardEntity.getBoard_likes()); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("해당 게시글을 찾을 수 없습니다."); } } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); } }
다음은 controller에 토글 방식으로 만들었던, 좋아요 기능.
새로운 useremail과 board_id 값으로 좋아요를 누르면 likes 테이블에 데이터 추가,
또 같은 useremail과 board_id 값으로 좋아요를 누른 상태에서 한번더 누르게 된다면 likes 테이블에서 데이터가 제거
⮕ likes 테이블을 복합키로 구성한 이유
BoardDetail.jsp
<div class="likes"> <span id="likeButton" style="cursor:pointer;" onclick="toggleLikes(${requestScope.boardDetail.board_id},'${sessionScope.loginUser.useremail}')">❤︎</span> <span id="likeCount">${requestScope.boardDetail.board_likes}</span> </div>
onclick을 추가해서 비동기 방식으로 js를 호출을 해주었다.
toggleLikes 함수에 매개변수로는 likes 테이블에 필요한 값.
아까 복합키로 지정해둔 값들을 파라미터 값으로 전달해주었다. 아래 두 값.
${requestScope.boardDetail.board_id},'${sessionScope.loginUser.useremail}'
useremail은 string 타입이므로 '' 싱글쿼테이션으로 묶어서 전달해주어야한다.
Board.js
function toggleLikes(board_id, useremail) { let url = "/board/toggle"; axios.post(url, { useremail: useremail, board_id: parseInt(board_id) }, { headers: { 'Content-Type': 'application/json' } } ).then(response => { let likeCountElement = document.getElementById('likeCount'); let currentLikeCount = parseInt(likeCountElement.textContent); if (response.status === 200) { likeCountElement.textContent = currentLikeCount + 1; // 좋아요 추가 } else if (response.status === 204) { likeCountElement.textContent = currentLikeCount - 1; // 좋아요 삭제 } }).catch(err => { if (err.response && err.response.status === 502) { alert("[삭제 오류]" + err.response.data); alert("[시스템 오류]" + err.message); } }); }
js파일에서는 axios로 값을 넘겨주는 방식을 이용하였다.
동영상 시연
'Spring Boot' 카테고리의 다른 글
[Spring] AOP (0) 2024.04.17 [무한댓글 게시판] 7. 댓글 수정 / 삭제 (0) 2024.02.29 [무한댓글 게시판] 5. CRUD (0) 2024.02.19 [무한댓글 게시판] 4. QueryDSL로 게시판 리스트 출력하기 (+페이지네이션) (0) 2024.02.16 [무한댓글 게시판] 3. Service 작성시 QueryDSL 사용하기 (1) 2024.02.15