댓글 기능 수정

뚜우웅이·2023년 5월 23일
0

SpringBoot웹

목록 보기
17/23

댓글 기능을 수정하기 전에 먼저 코드 전체적인 부분을 수정해줍니다.

@Autowired에서 private final로 교체

priavte final 즉 생성자 주입 방식으로 교체를 하면 생성자가 필요합니다.
기존에 사용하던 @Autowired 대신에 아래 코드처럼 사용해줍니다.

 private final UserService userService;

    public AccountController(UserService userService) {
        this.userService = userService;
    }

생성자 주입이 더 권장되는 이유

불변성 유지
생성자 주입 방법은 필드를 final로 선언하여 불변성을 유지할 수 있습니다. 이는 객체가 생성된 이후에는 필드 값을 변경할 수 없으므로, 코드의 안정성을 높이고 예측 가능성을 높일 수 있습니다.

의존성 명확하게 표시
생성자 주입 방법은 필드에 대한 의존성을 명시적으로 표시할 수 있습니다. 즉, 객체를 생성할 때 필요한 모든 의존성을 생성자 매개변수로 전달하여, 객체 간의 의존성이 명확하게 드러나도록 할 수 있습니다.

유닛 테스트 용이성
생성자 주입 방법은 객체를 생성할 때 모든 의존성을 외부에서 전달받으므로, 유닛 테스트에서 객체를 쉽게 모의(mock)하거나 대체할 수 있습니다. 이는 테스트 코드 작성을 쉽게 만들어줍니다.

DI 컨테이너 독립성
생성자 주입 방법은 DI(Dependency Injection) 컨테이너에 의존하지 않습니다. 이는 객체 생성 방법이 명시적이고 간단하므로, DI 컨테이너 없이도 객체를 생성할 수 있습니다. 이는 코드의 유연성을 높이고, DI 컨테이너를 교체해야 할 경우에도 유용합니다.

ctrl alt o 로 안 쓰는 import 정리하기

메소드 수준 권한 체크 코드 수정

@PreAuthorize("hasRole('ADMIN') or #board.user.username == authentication.name")
    @DeleteMapping("/boards/{id}")
    @Transactional
    void deleteBoard(@PathVariable Long id) {
        Board board = boardRepository.findById(id).orElseThrow();
        List<FileData> files = board.getFiles();
        if(files != null && !files.isEmpty()){
            for(FileData file : files){
                //파일 경로
                Path filePath = Paths.get("src/main/resources/static",file.getFilepath());
                //파일 삭제
                try{
                    Files.delete(filePath);
                }
                catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        //게시글 삭제
        boardRepository.deleteById(id);

    }

메소드 수준 권한 체크 수정

BoardApitController

 @PreAuthorize("hasRole('ADMIN') or @boardService.isBoardAuthor(#id, authentication.name)")

BoardService

//메소드 수준에서 권한 체크를 하기 위한 코드
    public boolean isBoardAuthor(Long id, String username) {
        Board board = boardRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Comment not found with id: " + id));
        return board.getUser().getUsername().equals(username);
    }

파일 다운로드 버튼 디자인 변경

<ul>
            <li th:each="file : ${files}">
                <span th:text="${file.filename}"></span>
                <a class="btn btn-primary" role="button" th:href="@{/api/file/download/{id}(id=${file.id})}">다운로드</a>
            </li>
        </ul>

a태그를 button 태그 처럼 보이게 class를 추가해줍니다.

댓글 페이징 처리

CommentRepository

Page<Comment> findAllByBoardId(Long boardId, Pageable pageable);

Comment 엔티티에서 boardId 필드를 기준으로 댓글을 조회하고, 페이징 처리된 결과를 반환합니다.

CommentService

public Page<Comment> getCommentsByBoardIdWithPaging(Long postId, Pageable pageable) {
        return commentRepository.findAllByBoardId(postId, pageable);
    }

BoardController

@GetMapping("/post")
    public String post(Model model, @RequestParam(required = false) Long id, Principal principal,
                       @PageableDefault(size = 5) Pageable pageable){
        Board board = boardRepository.findById(id).orElse(null);
        List<FileData> files = fileService.findByBoardId(id);
        User user = userService.findByUsername(principal.getName());
        model.addAttribute("board", board);
        model.addAttribute("files", files);
        model.addAttribute("userId", user.getId());

        Page<Comment> comments = commentService.getCommentsByBoardIdWithPaging(id, pageable);
        model.addAttribute("comments", comments);

        int block = 5;
        int currentBlock = (comments.getPageable().getPageNumber() / block) * block;
        int startPage = currentBlock + 1;
        int endPage = Math.min(comments.getTotalPages(), currentBlock + block);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);
        model.addAttribute("comments", comments);
        return "board/post";
    }

이전에 사용한 페이징 처리 방식을 그대로 사용해줍니다.
@RequestParam을 이용하여 url 뒤에 붙일 id를 가져오고 페이징 처리를 해줍니다.

댓글 정렬 기능

현재 댓글 페이징 처리에서 오름차순, 내림차순 기능을 추가해줍니다.

CommentRepository 수정

Page<Comment> findAllByBoardIdOrderByCreatedAtDesc(Long boardId, Pageable pageable);
    Page<Comment> findAllByBoardIdOrderByCreatedAtAsc(Long boardId, Pageable pageable);

CommentService 수정

public Page<Comment> getCommentsByBoardIdWithPagingDesc(Long boardId, Pageable pageable) {
        return commentRepository.findAllByBoardIdOrderByCreatedAtDesc(boardId, pageable);
    }

    public Page<Comment> getCommentsByBoardIdWithPagingAsc(Long boardId, Pageable pageable) {
        return commentRepository.findAllByBoardIdOrderByCreatedAtAsc(boardId, pageable);
    }

BoardController

 @GetMapping("/post")
    public String post(Model model, @RequestParam(required = false) Long id, Principal principal,
                       @PageableDefault(size = 5) Pageable pageable, @RequestParam(required = false) String commentOrderBy,
                       HttpSession session){
        if (commentOrderBy == null) {
            commentOrderBy = (String) session.getAttribute("commentOrderBy");
            if (commentOrderBy == null) {
                commentOrderBy = "desc";
            }
        }
        session.setAttribute("commentOrderBy", commentOrderBy);
        Board board = boardRepository.findById(id).orElse(null);
        List<FileData> files = fileService.findByBoardId(id);
        User user = userService.findByUsername(principal.getName());
        model.addAttribute("board", board);
        model.addAttribute("files", files);
        model.addAttribute("userId", user.getId());

        Page<Comment> comments;
        if (commentOrderBy.equals("asc")) {
            comments = commentService.getCommentsByBoardIdWithPagingAsc(id, pageable);
        } else {
            comments = commentService.getCommentsByBoardIdWithPagingDesc(id, pageable);
        }
        model.addAttribute("comments", comments);

        int block = 5;
        int currentBlock = (comments.getPageable().getPageNumber() / block) * block;
        int startPage = currentBlock + 1;
        int endPage = Math.min(comments.getTotalPages(), currentBlock + block);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);
        model.addAttribute("comments", comments);
        model.addAttribute("commentOrderBy", commentOrderBy);
        return "board/post";
    }

commentOrderBy = (String) session.getAttribute("commentOrderBy");
이 코드는 session에서 "commentOrderBy"라는 이름의 attribute값을 가져오는 코드입니다.
commentOrderBy라는 변수에는 세션에서 "commentOrderBy"로 저장된 값을 가져와서 할당합니다. 이후 commentOrderBy 변수를 사용하여 해당 속성 값을 활용할 수 있습니다.
이 전에 사용한 페이징 처리와 유사한 코드입니다.

post.html

<nav aria-label="Page navigation example">
        <ul class="pagination justify-content-center">
            <li class="page-item">
                <a class="page-link" th:href="@{/board/post(id=${board.id},page=0)}">First</a>
            </li>
            <li class="page-item" th:classappend="${1 == comments.pageable.pageNumber + 1} ? 'disabled'">
                <a class="page-link" th:href="@{/board/post(id=${board.id},page=${comments.pageable.pageNumber - 1})}">Previous</a>
            </li>
            <li class="page-item" th:classappend="${i == comments.pageable.pageNumber + 1 && i != 0} ? 'disabled'"
                th:each="i : ${#numbers.sequence(startPage, endPage)}">
                <a class="page-link" href="#" th:href="@{/board/post(id=${board.id},page=${i - 1})}"
                   th:text="${i}" th:if="${i != 0}"></a>
            </li>

            <li class="page-item" th:classappend="${comments.totalPages == comments.pageable.pageNumber + 1 || comments.totalPages == 0} ? 'disabled'">
                <a class="page-link" th:href="@{/board/post(id=${board.id},page=${comments.pageable.pageNumber + 1})}">Next</a>
            </li>
            <li class="page-item">
                <a class="page-link" th:href="@{/board/post(id=${board.id},page=${comments.totalPages - 1})}">Last</a>
            </li>
        </ul>
    </nav>

    <div>
        <label for="commentOrderBy" class="sr-only">정렬 방식</label>
        <select id="commentOrderBy" class="custom-select" style="width: 120px"
                th:onchange="|location.href='?id=' + ${board.id} + '&commentOrderBy=' + this.value;|">
            <option value="desc" th:selected="${commentOrderBy == 'desc'}">최신순</option>
            <option value="asc" th:selected="${commentOrderBy == 'asc'}">오래된순</option>
        </select>
    </div>

게시글 페이징 처리를 위한 버튼을 만들어줍니다.

th:onchange="|location.href='?id=' + ${board.id} + '&commentOrderBy=' + this.value;|">
이 코드는 사용자가 드롭다운 메뉴에서 선택한 값을 this.value로 가져와서 새로운 URL을 만들어 페이지를 새로고침합니다. 이 URL에는 두 개의 쿼리 매개변수가 포함됩니다: id와 commentOrderBy. id는 ${board.id}로 설정되고 commentOrderBy는 사용자가 선택한 값으로 설정됩니다.

list.html 페이징 처리 수정

게시글이 하나도 없을 경우 1번 페이지 다음에 0번 페이지가 뜨고
게시글이 없는 경우에도 next 버튼이 눌리는 문제를 수정해줍니다.

<li class="page-item" th:classappend="${i == boards.pageable.pageNumber + 1} ? 'disabled'"
                th:each="i : ${#numbers.sequence(startPage, endPage)}"><a class="page-link" href="#"
                                                                          th:href="@{/board/list(page=${i -1},searchText=${param.searchText})}"
                                                                          th:text="${i}" th:if="${i != 0}"></a></li>
            <li class="page-item" th:classappend="${boards.totalPages == boards.pageable.pageNumber + 1 || boards.totalPages == 0}  ? 'disabled'">
                <a class="page-link" th:href="@{/board/list(page=${boards.pageable.pageNumber + 1},searchText=${param.searchText})}">Next</a>
            </li>

댓글 페이징 사진입니다.

profile
공부하는 초보 개발자

0개의 댓글