-
Spring data JPA, Thymeleaf를 이용한 댓글 등록졸업과제/BackEnd 2023. 3. 8. 15:25
게시물에 댓글을 등록하는 기능을 구현해 보았다. 구글에는 검색해보니 ajax를 이용한 비동기방식이 많이 있었지만 나는 배우기에는 시간이 없고 이해 안 되는 코드를 그냥 가져다 붙여 넣기 해서 기능만 딱 구현하는 것은 왠지 모르게 싫고 나의 코드가 아닌 거 같은 고집이 있어서 Thymeleaf를 이용해 댓글 등록을 구현하고 싶었다.
시도해 보았던 방법들.
Swaager에서 API만 테스트 해보았을때는 직접 게시물의 pk를 내가 쳐서 넣어줘서 동작이 간단했는데 웹 페이지에서 현재 게시물의 pk를 넘기기 위해서 애를 많이 먹었다.
사용자가 작성한 글을 Controller로 넘기는 것은 어렵지 않았다. 하지만 댓글이 작성되고 있는 게시물의 pk를 Controller에 넘기기 위해 수많은 방법을 시도해 보았고 많은 방법이 되지 않았다. 현재 게시물의 pk를 알아야 댓글이 어떤 게시물과 연관이 있는지 알 수 있기 때문에 꼭 필요한 정보이다.
<form action="@{/post/detailPost/reply/{postId}(postId = ${post.postId})}" th:method="POST" th:object="${dto}">
a태그 안에서 th:src링크를 이용해 URL에 쿼리를 날리는것처럼 현재 게시물의 pk가 URL을 타고 전송된 뒤 Controller에서
@PathVariable을 이용해 postId값을 받아오고 싶었지만 되지 않았다.
postmapping method not allowed라는 오류가 자꾸 출력 되었다. 이 오류의 해결방법들을 다 해보아도 되지 않아서
Post 전송할때 쿼리를 전송할 수는 없는가 보다 하고 포기했다.
<form th:action="@{/post/detailPost/reply}" th:method="POST" th:object="${dto}"> <div class="my_inner_commnet"> <div class="my_id"> <i class="fa-solid fa-user fa fa-2x"></i> <span th:text="${user.uid}"></span> </div> <div class="my_text"> <input type="text" cols="30" class="my_textarea" th:field="*{comment}"> <input type="hidden" id="p_id" name="p_id" th:field="*{postId}(postId = ${post.postId})"> </div> <div class="text_submit"> <button type="submit">등록</button> </div> </div> </form>
2번째 방법은 억지를 좀 부려보았다. 쿼리에 변수를 넘기는 방식으로 th:field에 변수를 넘겨보자!! 하고 변수를 넘겨 보았지만
어림도 없이 실패했다.
성공 코드
@Service public class PostService { @Transactional(readOnly = false) // 추후에 /detailPost/{post_id}/Reply로변경 public ReplyResultDto Reply_write(ReplyRequestDto dto) { logger.info("[Reply_write] 댓글 저장 로직 동작. user:{},post:{}, comment:{}",dto.getId(),dto.getPostId(),dto.getComment()); Reply reply = new Reply(dto); User user = userRepository.findById(dto.getId()).orElse(null); Post post = postRepository.findById(dto.getPostId()).orElse(null); //user가 똑같은 게시물에 댓글을 한번 더달을 경우 reply.setUser(user); reply.setPost(post); post.getReplies().add(reply); replyRepository.save(reply); ReplyResultDto replyResultDto = ReplyResultDto.builder() .userId(reply.getUser().getId()) .postId(reply.getPost().getPostId()) .comment(reply.getComment()) .build(); if(reply.getUser().getUid()==user.getUid()&&reply.getPost().getPostId()==post.getPostId()) { setSuccessResult(replyResultDto); }else { setFailResult(replyResultDto); } return replyResultDto; } }
사용자가 작성한 댓글과 현재 게시물의 pk를 담은 dto객체가 DB에 저장되도록 Repository를 호출하는 Service이다.
댓글과 유저, 게시물의 연관관계를 세팅해 주고 게시물에는 여러 개의 댓글이 존재하기 때문에 게시물이 가지고 있는
댓글 컬랙션에 댓글을 add 해준다.
@Controller public class PostController{ @GetMapping(value = "/post/detailPost/{postId}") public String post_detailPost(@PathVariable("postId")Long PostId, Model model) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal(); User user = (User)principal; logger.info("[post_detailPost] 게시물 세부사항 관련 로직 동작 PostId:{},userId: {}",PostId,user.getUid()); Post post = postService.findPost(PostId); RoomPicture photo = postService.findPhoto(PostId); List<Reply> replies = postService.findReply(PostId); DetailPostDto detailPostDto = postService.detail_post(post, photo, replies); model.addAttribute("post",detailPostDto); model.addAttribute("user",user); model.addAttribute("postId",PostId); //여기서 아예 postId pk를 model에 넘겨버림 return "post/detailPost"; } }
사용자가 게시물 목록 중 마음에 드는 게시물을 자세히 보고 싶을 때 클릭하면 동작하는 Controller이다. {postId}를 통해
사용자가 클릭한 게시물의 pk를 받아오도록 한다. 이때 받아온 Pk를 model에 바로 전달해 줘서 detailPost 템플릿에서
게시물의 pk를 접근할 수 있도록 해준다.
@Controller public class ReplyController { @PostMapping(value = "/post/detailPost/reply") public String replyWrite(HttpServletRequest request) { logger.info("댓글 작성 ReplyController postId : {},comment: {}",request.getParameter("p_id"),request.getParameter("comment")); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal(); User user = (User)principal; String postId = request.getParameter("p_id"); long p_id = Long.parseLong(postId); ReplyRequestDto dto = ReplyRequestDto.builder() .postId(p_id) .id(user.getId()) .comment(request.getParameter("comment")) .build(); replyService.Reply_write(dto); return "redirect:/"; } }
사용자가 게시물에서 댓글을 등록하면 동작하는 ReplyController이다. 구현하다가 DTO 때문에 안되나 싶어서
HttpServletRequest로 파라미터를 받아왔지만 글을 작성하다 보니 DTO로도 받아올 수 있을 거 같다는 생각이 든다.
Spring Security를 이용해 현재 유저의 정보를 받아오고 Request안에 실려온 사용자가 작성한 댓글과 게시물의 pk를 Get 해서
DTO를 생성하고 위에 작성했던 Service를 호출해 댓글과 유저, 게시물의 연관관계를 설정해 주고 DB에 저장될 수 있도록 해준다.
<div class="my_comment"> <form action="/post/detailPost/reply" method="POST"> <!-- <fieldset>--> <div class="my_inner_commnet"> <div class="my_id"> <i class="fa-solid fa-user fa fa-2x"></i> <span th:text="${user.uid}"></span> </div> <div class="my_text"> <input type="text" cols="30" class="my_textarea" id="comment" name="comment"> <input type="hidden" id="p_id" name="p_id" th:value="${postId}"> </div> <div class="text_submit"> <button type="submit">등록</button> </div> </div> <!-- </fieldset>--> </form>
가장 중요하다고 생각하는 view 쪽이다. ServletRequest로 Controller에서 받아갈 것이기 때문에
id, name으로 Parameter의 이름을 설정해 주고 게시물의 pk값은 사용자가 입력하는 것이 아니기 때문에
input type: hidden으로 감춰준다. 그리고 th:value를 이용해 id, name에 전달시켜줄 아까 PostController에서 전달된
게시물의 pk를 p_id에 세팅해 준다.
테스트, 로그 확인
akahd135의 아이디를 가진 유저가 pk가 4번인 게시물에 댓글을 등록했을 때 정상 동작하는 로그를 확인할 수 있다.
DB에도 반영이 잘되었고 댓글이 게시물, 유저의 pk를 fk로 DB에 정확하게 반영한 모습을 확인할 수 있다.
. ajax를 이용한 비동기 방식도 구현해 보아야 하는데 왜 이리 JS는 하기 싫은지 모르겠다.
뭔가 문법이 마음에 안 들게 생기셨으세요..(글 초반에는 이런 이유때문에 ajax를 안쓴다고 했던것 아닌거 같은대 크..크흠)
'졸업과제 > BackEnd' 카테고리의 다른 글
Thymeleaf를 이용해 Option값 받아오기. (0) 2023.03.11 Thymeleaf를 이용한 게시물 댓글 출력 (0) 2023.03.10 Spring boot 쿠키에 JWT 토큰값 세팅 (0) 2023.02.27 Spring Data Jpa의 Pageable을 이용한 게시물 페이징 처리 (0) 2023.02.20 Spring Data Jpa를 이용한 게시물 댓글 삭제 (0) 2023.02.19