Spring 게시판 프로젝트 4편 (댓글 구현 수정삭제) Created Time: July 18, 2022 10:44 AM Last Edited Time: August 3, 2022 11:07 AM Tags: Java, Spring, Computer
문제발생 및 해결방법
form에서 get, post 뿐만 아니라 다른 것도 포함하여, put, patch, delete 등을 사용하고싶었다. 따라서 HiddenHttpmethodFilter
를 활용하여 filter로 마치 다른 형식으로 요청들어온것으로 처리할 수 있도록 하였다.
HiddenHttpMethodFilter 로 method해결
1 2 3 4 5 6 7 8 @Configuration public class SpringConfig { @Bean public HiddenHttpMethodFilter httpMethodFilter () { HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); return hiddenHttpMethodFilter; } }
개요
Comment과 Post는 M:1 관계이므로 외래키는 Comment 주인이된다.
Comment와 User는 M:1 관계이므로 외래키는 Comment 주인이된다.
구현 Entity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Builder @AllArgsConstructor @NoArgsConstructor @Getter @Table (name = "comments" )@Entity public class Comment { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; @Column (columnDefinition = "TEXT" , nullable = false ) private String comment; @Column (name = "created_date" ) @CreatedDate private String createdDate; @Column (name = "modified_date" ) @LastModifiedDate private String modifiedDate; @ManyToOne @JoinColumn (name = "post_id" ) private Post post; @ManyToOne @JoinColumn (name = "user_id" ) private User user; public void update (String comment) { this .comment = comment; } }
Post 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Getter @Entity @NoArgsConstructor (access = AccessLevel.PROTECTED)@EntityListeners (AuditingEntityListener.class)public class Post { @Id @GeneratedValue private Long id; @Column (length =10 ,nullable = false ) private String postName; @Column (columnDefinition ="TEXT" , nullable = false ) private String content; @CreatedDate @Column (updatable = false ) private LocalDateTime createdDate; @LastModifiedDate private LocalDateTime modifiedDate; @ManyToOne (fetch =FetchType.LAZY) @JoinColumn (name="user_id" ) private User user; @OneToMany (mappedBy="post" , fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) @OrderBy ("id asc" ) private List<Comment> comments; @Builder public Post (Long id, String postName, String content, User user, List<Comment> comments) { this .id = id; this .postName = postName; this .content = content; this .user = user; this .comments =comments; } public void update (String postName,String content) { this .postName = postName; this .content = content; }
Repository 1 2 public interface CommentRepository extends JpaRepository <Comment , Long > {}
Dto 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Data @AllArgsConstructor @NoArgsConstructor @Builder public class CommentDto { private Long id; private String comment; private String createdDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm" )); private String modifiedDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm" )); private User user; private Post post; private String nickname; private String email; public Comment toEntity () { Comment comments = Comment.builder() .id(id) .comment(comment) .createdDate(createdDate) .modifiedDate(modifiedDate) .user(user) .post(post) .build(); return comments; } public CommentDto (Comment comment) { this .id = comment.getId(); this .comment = comment.getComment(); this .createdDate = comment.getCreatedDate(); this .modifiedDate = comment.getModifiedDate(); this .nickname = comment.getUser().getNickname(); this .email = comment.getUser().getEmail(); } }
PostResponseDto
게시글 정보를 리턴할 응답(Response) 클래스
Entity 클래스를 생성자 파라미터로 받아 데이터를 Dto로 변환하여 응답
별도의 전달 객체를 활용해 연관관계를 맺은 엔티티간의 무한참조를 방지(???)
comments 필드의 List 타입을 DTO 클래스로해서 엔티티간 무한 참조를 방지해줬다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Getter public class PostResponseDto { private Long id; private String postName; private String content; private User user; private LocalDateTime createdDate; private LocalDateTime modifiedDate; private List<CommentDto> comments; public PostResponseDto (Post post) { this .id = post.getId(); this .postName = post.getPostName(); this .content = post.getContent(); this .createdDate = post.getCreatedDate(); this .modifiedDate = post.getModifiedDate(); this .user = post.getUser(); this .comments = post.getComments().stream().map(CommentDto::new ).collect(Collectors.toList()); } }
Controller PostController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @GetMapping ("/{id}" ) public String getpost (@PathVariable Long id, Model model, HttpSession session) { UserSessionDto user = (UserSessionDto) session.getAttribute("user" ); PostResponseDto postResponseDto = postService.getResponseDtoPost(id); List<CommentDto> comments = postResponseDto.getComments(); if (comments != null && !comments.isEmpty()){ model.addAttribute("comments" , comments); } if (user != null ){ model.addAttribute("user" , user.getEmail()); if (postResponseDto.getUser().getEmail().equals(user.getEmail())) { model.addAttribute("writer" , true ); } } model.addAttribute("posting" , postResponseDto); return "basic/item" ; }
Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @RequiredArgsConstructor @Service public class CommentService { private final CommentRepository commentRepository; private final UserRepository userRepository; private final PostRepository postRepository; @Transactional public Long commentSave (String email, Long id, CommentDto commentDto) { User user = userRepository.findByEmail(email).get(); Post post = postRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("댓글 쓰기 실패: 해당 게시글이 존재하지 않습니다." + id)); commentDto.setUser(user); commentDto.setPost(post); Comment comment = commentDto.toEntity(); commentRepository.save(comment); return commentDto.getId(); } @Transactional public void update (Long commentId, CommentDto commentDto) { Comment comment = commentRepository.findById(commentId).orElseThrow(() -> new IllegalArgumentException("해당댓글이 존재하지 않습니다" )); comment.update(commentDto.getComment()); } @Transactional public void delete (Long commentId) { Comment comment = commentRepository.findById(commentId).orElseThrow(() -> new IllegalArgumentException("해당 댓글이 존재하지 않습니다" )); commentRepository.delete(comment); } }
PostController 1 2 3 4 5 6 7 8 9 10 @Service @RequiredArgsConstructor public class PostService { @Transactional public PostResponseDto getResponseDtoPost (Long id) { Post posting = postRepository.findById(id).get(); PostResponseDto postDto = new PostResponseDto(posting); return postDto; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @RequiredArgsConstructor @Controller public class CommentController { private final CommentService commentService; @PostMapping ("/basic/post/{id}/comments" ) public String commentSave (@PathVariable Long id, CommentDto commentDto, HttpSession session) { UserSessionDto user = (UserSessionDto) session.getAttribute("user" ); String email = user.getEmail(); commentService.commentSave(email, id, commentDto); return "redirect:/basic/post/" +Long.toString(id); } @PutMapping ({"basic/post/{id}/comments/{commentId}" }) public String update (@PathVariable Long id,@PathVariable Long commentId, CommentDto dto) { commentService.update(commentId, dto); return "redirect:/basic/post/" +Long.toString(id); } @DeleteMapping ("basic/post/{id}/comments/{commentId}" ) public String delete (@PathVariable Long id,@PathVariable Long commentId, CommentDto dto) { commentService.delete(commentId); return "redirect:/basic/post/" + Long.toString(id); } }
template item
comments가 넘어왔으므로 타임리프문법을 통해 for문 사용
comment에서 api에서 session에서 user 권한
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <div class ="card" > <ul class ="card-header bi bi-chat-dots" > <h2 text ="댓글" > 댓글리스트</h2 > <div th:each ="comment : ${comments}" > <li id ="comments" class ="list-group-item" > 작성자: <p style ="font-size: medium" th:text ="${comment.nickname}" > </p > 댓글내용: <p th:text ="${comment.comment}" > </p > </li > <div th:if ="${comment.email == user}" > <form action ="#" th:action ="@{|/basic/post/__${posting.id}__/comments/${comment.id}|}" th:object ="${comment}" method ="post" > <input type ="hidden" name ="_method" value ="put" > <input type ="text" id ="comment" name ="comment" class ="form-control" placeholder ="내용수정이 가능합니다" > <div class ="col" > <button class ="w-100 btn btn-primary btn-lg" type ="submit" > 댓글 수정</button > </div > </form > </div > <div th:if ="${comment.email == user}" > <form action ="#" th:action ="@{|/basic/post/__${posting.id}__/comments/${comment.id}|}" th:object ="${comment}" method ="post" > <input type ="hidden" name ="_method" value ="delete" > <div class ="col" > <button class ="w-100 btn btn-primary btn-lg" type ="submit" > 댓글 삭제</button > </div > </form > </div > </div > </ul > </div > <div class ="card" > <h2 text ="댓글" > 댓글작성하기</h2 > <form action ="item.html" th:action ='@{/basic/post/{posting.id}/comments(posting.id=${posting.id})}' method ="post" > <div > <input type ="text" id ="price" name ="comment" class ="form-control" placeholder ="내용을 입력하세요" > </div > <div class ="row" > <div class ="col" > <button class ="w-100 btn btn-primary btn-lg" type ="submit" > 댓글 작성</button > </div > </div > </form > </div > </div > </div >