게시글의 좋아요 기능을 구현해 보자!
들어가며
이전포스팅에서는 좋아요 기능을 구현하기 위한 엔티티를 설계해 봤습니다. 이번 포스팅에서는 사용자 게시글마다 좋아요를 할 수 있으며 좋아요 버튼을 누를 때마다 좋아요 , 좋아요 해제하는 기능을 구현해 보도록 하겠습니다.
사전 준비
만약 게시판 프로젝트를 하시려는 분은
이전포. 스팅 따라 해 만들어주시기 바랍니다!
[SpringBoot] 스프링부트 - 무작정 (REST API)CRUD 게시판 만들기 좋아요 기능 엔티티 설계 [13-1]
좋아요 기능을 구현하기 전에 엔티티를 설계하자! 들어가며 이번 포스팅까지 게시판 만들기 프로젝트에서 댓글 기능까지 구현해 봤다. 이번 포스팅부터는 게시글의 좋아요 기능을 추가할 예정
back-stead.tistory.com
전체 코드는 깃에 올려두었습니다.
GitHub - CHISANW/message-board
Contribute to CHISANW/message-board development by creating an account on GitHub.
github.com
구현할 기능
1. 각 사용자는 각 게시글마다 따로 좋아요를 할 수 있다.
2. 좋아요가 클릭하면 좋아요가 되며 채워진 하트로 바뀌며, 반대로 좋아요 된 게시글에 좋아요 버튼을 클릭하면 빈하트로 변경하게 한다.
Repository
BoardLIkeRepository.class
@Repository
public interface BoardLIkeRepository extends JpaRepository<Board_Like_check,Long> {
@Query("select b from Board_Like_check b where b.member.id = :memberId and b.board.Id=:boardId")
Board_Like_check findMemberId(@Param("memberId")Long memberId, @Param("boardId") Long boardId);
@Modifying
@Query("delete from Board_Like_check b where b.board.Id=:boardId")
void deleteBoard_Id(@Param("boardId") Long boardId);
}
- findMemberId() : Member 키값과 Board의 키값으로 Board_like_check를 조회하는 메서드 jpql을 사용
- deleteBoard_Id() : 각 게시글마다 좋아요를 해제할 수 있도록 하는 메서드 (성능적으로 좋아요 시마다 true <-> false 바꾸는 것은 칼럼을 삭제하는 것보다 좋지 않을 것 같아서 해제 기능은 삭제로 구현)
Service
BoardLikeServiceImpl.class
@Service
@RequiredArgsConstructor
@Transactional
public class BoardLikeServiceImpl implements BardLikeService {
private final MemberServiceImpl memberService;
private final BoardLIkeRepository boardLIkeRepository;
public boolean isBoardCheck(Member loginMember,Long boardId){
String loginMemberUsername = loginMember.getUsername();
Member member = memberService.findByUsername(loginMemberUsername);
Board_Like_check boardLike = boardLIkeRepository.findMemberId(member.getId(),boardId);
if (boardLike!=null && boardLike.isLike_check()){
return true;
}
return false;
}
@Override
public void deleteByBoardId(Long boardId) {
boardLIkeRepository.deleteBoard_Id(boardId);
}
}
- isBoardCheck() : 좋아요 엔티티에 파라미터로 넘어온 사용자 정보와 게시글의 값을 통해서 좋아요 상태를 확인하며 만약 좋아요가 되어있다면 true로 반환하는 메서드
- dleteByBoardId() : 게시글의 키값으로 좋아요를 삭제하는 메서드
이제는 BoardServiceImpl 부분에 BoardLikeRepostory를 생성자 주입을 해준후에 아래의 코드를 추가해 준다.
BoardServiceImpl.class 내용 추가
@Service
@Transactional
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService {
...
private final BoardLIkeRepository boardLIkeRepository; //추가
@Override
public Board save(BoardDto boardDto) {
... 이전과 동일
Board build = Board.builder()
.title(boardDto.getTitle())
.dateTime(LocalDateTime.now())
.writer(byUsername.getUsername())
.password(boardDto.getPassword())
.content(boardDto.getContent())
.count(0)
.member(byUsername)
.views(0)
.boardLike(0) //추가 기본값은 false
.build();
... 이전과 동일
}
//해당 로직을 추가
@Override
public int board_like(BoardDto boardDto) {
Long boardDtoId = boardDto.getId();
String username = boardDto.getMemberDto().getUsername(); //로그인 사용자 정보
Board board = findByBoardId(boardDtoId);
Member member = memberService.findByUsername(username);
if (member!=null){
Board_Like_check byMemberId = boardLIkeRepository.findMemberId(member.getId(),board.getId()); //로그인 사용자 정보를 사용해 좋아요했는지 찾는과정
if (byMemberId!=null&&byMemberId.getBoard().getId().equals(board.getId())) {
board.setBoardLike(board.getBoardLike()-1);
boardRepository.save(board);
boardLIkeRepository.deleteBoard_Id(board.getId());
return -1;
}else {
board.setBoardLike(board.getBoardLike() + 1); //좋아요 1회 증가
Board_Like_check boardLike = Board_Like_check.builder()
.like_check(true)
.member(member)
.board(board).build();
boardLIkeRepository.save(boardLike);
return 1;
}
}else
throw new Login_RestException("로그인을 한후에 이용할수 있습니다.");
}
}
- svae() : 로직 안에 boardlike의 값을 저장해주지 않고 실행하면 NullpointException이 발생합니다.
- board_like() : 파라미터로 넘오언 boardDto에서 값을 조회한 후 좋아요 엔티티에 해당 게시글의 사용자가 좋아요 했다면 boarlike필드를 -1 감소하고 좋아요를 삭제하고 -1을 반환합니다. 좋아요 한 값이 없다면 true로 변경하고 좋아요를 +1을 하고 1을 반환합니다.
Controller
BoardController.class 내용 추가
@Controller
@RequiredArgsConstructor
@Slf4j
public class BoardController {
...이전과 동일
private final BoardLikeServiceImpl boardLikeService; //추가
@GetMapping("/board/{boardId}")
public String boardInfo(@PathVariable(name = "boardId") Long boardId,Model model,HttpSession session){
... 이전과 동일
//추가
boolean boardCheck = false;
if (loginMember != null) {
boardCheck = boardLikeService.isBoardCheck(loginMember, boardId);
}
model.addAttribute("board_like_check", boardCheck);
..이전과 동일
}
//추가
@PostMapping("/board/likes")
@ResponseBody
public ResponseEntity<?> boardLikes(@RequestBody BoardDto boardDto){
try{
int num = boardService.board_like(boardDto);
return ResponseEntity.ok(num);
}catch (Login_RestException e) {
e.printStackTrace();
throw new Login_RestException("로그인후 이용할수 있습니다.");
}
}
}
뷰에서 좋아요 한 사용자가 있을 때 채워진 하트로 보여주기 위해서 전에 만들었던 isBoradCehck() 메서드를 통해서 반환값을 뷰로 전달합니다.
@PostMapping("/board/likes")를 좋아요 API를 작성해 줍니다.
VIEW
boardInfo.html 내용추가
<button th:if="${board_like_check == false}" type="button" onclick="board_like()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart" viewBox="0 0 16 16">
<path d="m8 2.748-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"/>
</svg>
</button>
<button th:if="${board_like_check == true}" type="button" onclick="board_like()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
</svg>
</button>
채워진 하트와 빈 하트 좋아요 버튼을 추가해 줍니다. (이미지는 부트스트랩의 이미지를 사용했습니다.)
function board_like() {
const loginEl = document.getElementById('loginMember_username');
var loginMember;
if (loginEl != null) {
loginMember = loginEl.value;
}
var id = document.getElementById('input_id').value;
var dto = {
"id": id,
"memberDto": {
"username": loginMember
}
}
$.ajax({
url: "/board/likes",
method: "post",
contentType: "application/json",
data: JSON.stringify(dto),
success: function(response) {
location.reload();
},
error: function(xhr) {
if (xhr.status === 401) {
var errorMessage = JSON.parse(xhr.responseText);
$.each(errorMessage, function(key, value) {
if (key === "message") {
alert(value);
}
});
}
}
});
}
좋아요 버튼 클릭 시 실행될 board_like() 함수를 작성해 줍니다.
실행
좋아요 클릭 시
좋아요를 한번 클릭하면 1(성공)이 반환된다.
이제 다시 한번 클릭하면
-1(이미 값이 있어 삭제함)이 반환되는 것을 볼수있다.
해당 영상을 보면 사용자 별로 좋아요가 되는것을 확인할 수 있다. 위와 같이 진행하고 아래의 db처럼 나온다면 성공한 것이다.(키값의 크기는 다를 수 있습니다.)
다음으로
이번 포스팅에서는 간단하게 좋아요 기능을 구현해 봤습니다. 각 정렬을 간단하게 구현해 보도록 하겠습니다.