LostCatBox

SpringProject-Board-CH04

Word count: 1.3kReading time: 8 min
2022/12/24 Share

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

Comment

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; // 작성자

/* 댓글 수정위한 setter */
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)/* JPA에게 해당 Entity는 Auditing 기능을 사용함을 알립니다. */
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 //매개변수를 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

CommentDto

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;

/* Dto -> Entity */
public Comment toEntity() {
Comment comments = Comment.builder()
.id(id)
.comment(comment)
.createdDate(createdDate)
.modifiedDate(modifiedDate)
.user(user)
.post(post)
.build();

return comments;
}

/* Entity -> Dto*/
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;

/* Entity -> Dto*/
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

CommentService

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;

/* CREATE */
@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();
}
/* Update */
@Transactional
public void update(Long commentId, CommentDto commentDto){
Comment comment = commentRepository.findById(commentId).orElseThrow(() ->
new IllegalArgumentException("해당댓글이 존재하지 않습니다"));
comment.update(commentDto.getComment());
}
/* Delete */
@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;
}
}

CommentController

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);
}

/*
댓글 update
*/
@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);
}
/*
댓글 delete
*/
@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"> <!-- HiddenHttpMethodFilter 사용 -->
<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"> <!-- HiddenHttpMethodFilter 사용 -->
<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> <!-- /container -->
</div>
CATALOG
  1. 1. Spring 게시판 프로젝트 4편 (댓글 구현 수정삭제)
  2. 2. 문제발생 및 해결방법
    1. 2.1. HiddenHttpMethodFilter 로 method해결
  3. 3. 개요
  4. 4. 구현
    1. 4.1. Entity
      1. 4.1.1. Comment
      2. 4.1.2. Post
    2. 4.2. Repository
    3. 4.3. Dto
      1. 4.3.1. CommentDto
      2. 4.3.2. PostResponseDto
    4. 4.4. Controller
      1. 4.4.1. PostController
    5. 4.5. Service
      1. 4.5.1. CommentService
      2. 4.5.2. PostController
    6. 4.6. CommentController
    7. 4.7. template
      1. 4.7.1. item