LostCatBox

SpringProject-Board-CH04

Word count: 1.3kReading time: 8 min
2022/12/24 8 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. 문제발생 및 해결방법
  3. 3. 개요
  4. 4. 구현