스프링boot/정리

Spring MVC) 좋아요, 조회수를 만들어 보자.

acid7937 2023. 4. 21. 13:44

좋아요와 조회수인데 좋아요는 말그대로 좋아요를 해주는 기능이다. 요청이 들어오면 1증가 할꺼고

조회수는 get요청이 들어올때마다 숫자를 1식 증가 시킬꺼다.

 

그리고 이번 게시글에서는 전체적으로 수정된 미설명 코드들을 설명하는 시간을 가지겠다.

 

코드를 살펴보자.

먼저 Board에 Count 올릴 값들을 추가해주자.

 

@Entity
@Getter @Setter
@NoArgsConstructor
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long boardId;

    private String title;

    private String content;

    private Integer boardCount =0;
    private Integer likeCount =0;

    @OneToMany(mappedBy = "board", cascade = {CascadeType.PERSIST,CascadeType.REMOVE})
    private List<Reply> reply = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

}

위처럼 추가할때 초기 숫자를 넣어주자 안그럼 숫자 인식을 못한다.

 

@Entity(name = "likes") //이거 이름 안바꿔 주면 충돌남 SQL 예약어라서..
@Setter @Getter
@NoArgsConstructor
public class Like {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long likeId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;
    // MEMBER_ID

    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;


    //likeId는 생성자가 필요없어서 @AllArgsConstructor 사용 안했음.
    public Like(Member member, Board board) {
        this.member = member;
        this.board = board;

    }
}

 

Entitiy 부분인데 likes로 안바꿔주면 서버가 안돌아 간다. 왜냐하면 SQL에서 기본적으로 사용하는 단어라 그렇다.

그리고 @AllArgsConstructor쓰면 likeId까지 긁어와서 에러터진다. 좋아요는 기능이 단순해서 dto를 사용안할꺼다.

 

@Repository
public interface LikeRepository extends JpaRepository<Like,Long> {

    //있는지 없는지 검토
    boolean existsByMemberAndBoard(Member member, Board board);
    //삭제
    void deleteByMemberAndBoard(Member member, Board board);

}

 

Repository는 Service부분 보면 이해갈꺼다.

 

@Service
@RequiredArgsConstructor
@Transactional
public class LikeService {

    private final BoardService boardService;
    private final BoardRepository boardRepository;
    private final LikeRepository likeRepository;

    public void addLike(Long boardId, Member member) {

        Board board = boardService.findBoardId(boardId);
        if (!likeRepository.existsByMemberAndBoard(member, board)) {
            // 호출되면 board에 있는 count 증가
            board.setLikeCount(board.getLikeCount()+1);
            // likeRepository에 memberId 값이랑 boardId값 저장해버림
            likeRepository.save(new Like(member, board));
        }
        else {
        board.setLikeCount(board.getLikeCount()-1);
        likeRepository.deleteByMemberAndBoard(member,board);
    }
//        boardRepository.save(board); //@Transactional 사용하니깐 더티 체킹으로 필요없어짐
    }
}

 

Service를 살펴보자.

 

친절하게 주석 첨부했지만 좋아요를 두번 누르면 else에서 취소를 해버리는 방식이다. 이젠 Repository가 이해가 갈것이다.

 

//        boardRepository.save(board); //@Transactional 사용하니깐 더티 체킹으로 필요없어짐

 

이부분은 내가 헷갈렸던 부분이였다. 이게 처음에는 board에 숫자 증가는 안하고 likeId만 만들고 있었던 것이였다 두번 누르면 삭제도 잘 해주었고...

 

주석 처리 부분을 살려서 해결할수도 있지만 @Transactional를 사용하면 spring이 묶여서 연괸되어 있는걸 다같이 처리하기에 간편하게 문제를 해결 할수 있다.

 

@RestController
@RequestMapping("/api/likes")
@RequiredArgsConstructor
public class LikeController {

    private final LikeService likeService;
    private final MemberService memberService;

    @PostMapping("up/{boardId}")
    public ResponseEntity addLike(@PathVariable("boardId")@Positive Long boardId,
                                  @AuthenticationPrincipal String email ) {
        //이메일을 불러옴
        Member member = memberService.findByEmail(email);
        //id 랑 멤버 추가해 버림
        likeService.addLike(boardId,member);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

 

마지막으로 이건 Controller인데 @AuthenticationPrincipal String email 부분이 눈에 들어올 것이다.

 

이건 현재 로그인된 사용자 정보를 얻을때 쓰는건데 email로 사용자를 식별한 것이다. 즉 인증된 사용자만 해당 테이블 수정 권한을 얻는것이다. (로그인 한사람이 본인 게시글만 건들수 있음.)

 

public void isPermission(Member member, String email) {
    if (!member.getEmail().equals(email)) {
        throw new BusinessLogicException(ExceptionCode.NO_PERMISSION);
    }
}

 

지금까지 NO_PERMISSION 문구를 봤을텐데 위의 코드를 추가하여 Member에서 사용자가 권한이 있는지 확인하였다.

 

이게 없으면 아무것도 못한다. Board를 Controller를 다시 보자.

 

@Getter @Setter
@RestController
@RequestMapping("/api/boards")
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;


    @PostMapping("/post")
    public ResponseEntity postBoard(@RequestBody @Validated BoardPostDto boardPostDto) {

        Long boardId = boardService.createBoard(boardPostDto);
        return ResponseEntity.status(HttpStatus.CREATED).body(boardId);
    }
    @PatchMapping("/{boardId}")
    public ResponseEntity patchBoard(@PathVariable("boardId")Long boardId,
                                     @RequestBody @Validated BoardPatchDto boardPatchDto,
                                     @AuthenticationPrincipal String email) {
        boardService.updateBoard(boardPatchDto, boardId,email);
        return ResponseEntity.status(HttpStatus.OK).body(boardId);
    }

    @GetMapping("/{boardId}")
    public ResponseEntity getBoard(@PathVariable("boardId") Long boardId) {
        BoardResponseDto boardResponseDto = boardService.findByBoardId(boardId);
        return ResponseEntity.status(HttpStatus.OK).body(boardResponseDto);
    }

    @GetMapping
    public ResponseEntity<Page<BoardResponseDto>> getAllBoards(
            @RequestParam(value = "page",defaultValue = "1")int page,
            @RequestParam(value = "size",defaultValue = "5")int size) {

        Pageable pageable = PageRequest.of(page - 1, size);
        Page<BoardResponseDto> boards = boardService.findAllBoards(pageable);

        return ResponseEntity.status(HttpStatus.OK).body(boards);
    }
    @DeleteMapping("/{boardId}")
    public ResponseEntity deleteBoard(@PathVariable("boardId") Long boardId,
                                      @AuthenticationPrincipal String email) {
        boardService.deleteBoard(boardId,email);
        return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
    }
}

 

잘보면 Patch와 Delete에 @AuthenticationPrincipal이 추가 된게 보일꺼다 이게 없다면 로그인한 유저 누구나 삭제가 가능하게 된다. 그래서 개인 권한을 찾아 주는거다. 상식적으로 로그인한뒤 인가받았다고 해서 다른 사람들 게시글을 수정 삭제하면 안될것 아닌가

 

 

이젠 Postman에서 좋아요를 올려보자.

 

 

좋아요 요청을 해보았다. 게시판 에서도 올라가고 좋아요 테이블도 생긴게 확인된다.

 

그러나 한번더 요청을 한다면?

 

 

DB에서 삭제와 감소가 일어나게 된다.

 

 

마지막으로 Board가 Get 요청을 받을때 카운트 올리고 싶으면

 

public BoardResponseDto findByBoardId(Long boardId) {

    Board board = findBoardId(boardId);
    board.setBoardCount(board.getBoardCount() + 1);
    boardRepository.save(board); // 게시글의 조회수를 증가시킨 후 저장하는 용도
    return BoardResponseDto.FindFromBoard(board);
}

Service에서 추가하면 그만이다. 결과를 보자

 

일부러 3번 요청을 하였다 그랬더니 boardCount에 3이 들어가는걸 볼수 있다.

 

 

 

요즘 Next.js로 포폴 완성해서 이력서 만들고 넣느라 정신이 없지만...

 

중간에 추가하고 싶은 기능이 생기면 블로그에 글 올리겠다.