- 이번 시간에는 댓글을 작성하고 삭제하는 기능을 구현해보려고 합니다.
📝 Entity
- 우선 아래와 같이 댓글(
Comment
) 객체를 구현하였습니다.
package com.cos.photogram.domain.comment;
...
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Comment {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 100, nullable = false)
private String content;
@JsonIgnoreProperties({"images"})
@JoinColumn(name = "user_id")
@ManyToOne
private User user;
@JoinColumn(name = "image_id")
@ManyToOne
private Image image;
private LocalDateTime create_date;
@PrePersist //데이터베이스에 INSERT 되기 직전에 실행
public void createDate() {
this.create_date = LocalDateTime.now();
}
}
- 이후 스토리 페이지를 로드할 때 댓글도 함께 로드해야 하기 때문에
Image
객체와 양방향 매핑을 수행해주었습니다.
package com.cos.photogram.domain.image;
...
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Image {
...
@OrderBy("id DESC")
@JsonIgnoreProperties({"image"})
@OneToMany(mappedBy = "image")
private List<Comment> comments;
...
}
📝 Repository
package com.cos.photogram.domain.comment;
...
public interface CommentRepository extends JpaRepository<Comment, Long> {
}
📝 Service
package com.cos.photogram.service;
...
@RequiredArgsConstructor
@Service
public class CommentService {
private final CommentRepository commentRepository;
private final UserRepository userRepository;
@Transactional
public Comment writeComment(String content, Long imageId, Long userId) {
Image image = new Image();
image.setId(imageId);
User userEntity = userRepository.findById(userId).orElseThrow(() -> {
return new CustomApiException("찾을 수 없는 사용자입니다.");
});
Comment comment = new Comment();
comment.setContent(content);
comment.setImage(image);
comment.setUser(userEntity);
return commentRepository.save(comment);
}
@Transactional
public void deleteComment(Long commentId) {
try {
commentRepository.deleteById(commentId);
} catch(Exception e) {
throw new CustomApiException(e.getMessage());
}
}
}
Image
객체의 경우id
값만 필요하기 때문에 따로 Repository를 사용하지 않았으며User
객체의 경우id
값 이외에도 사용해야할 속성들이 있었기 때문에 Repository를 사용하여 따로 개체를 뽑아내었습니다.
📝 Controller
package com.cos.photogram.web.api;
...
@RequiredArgsConstructor
@RestController
public class CommentApiController {
private final CommentService commentService;
@PostMapping("/api/comment")
public ResponseEntity<?> writeComment(@Valid @RequestBody CommentDto commentDto, BindingResult bindingResult, @AuthenticationPrincipal PrincipalDetails principalDetails) {
if (bindingResult.hasErrors()) {
Map<String, String> errorMap = new HashMap<>();
for (FieldError error : bindingResult.getFieldErrors()) {
errorMap.put(error.getField(), error.getDefaultMessage());
}
throw new CustomValidationApiException("유효성 검사에 실패하였습니다.", errorMap);
}
Comment comment = commentService.writeComment(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());
return new ResponseEntity<>(new CMRespDto<>(1, "댓글 쓰기 성공", comment), HttpStatus.CREATED);
}
@DeleteMapping("/api/comment/{commentId}")
public ResponseEntity<?> deleteComment(@PathVariable Long commentId) {
commentService.deleteComment(commentId);
return new ResponseEntity<>(new CMRespDto<>(1, "댓글 삭제 성공", null), HttpStatus.OK);
}
}
package com.cos.photogram.web.dto.comment;
...
@Data
public class CommentDto {
@NotNull
private Long imageId;
@NotBlank
private String content;
}
📝 Javascript
- 이후
js
파일에서 ajax 통신을 통해 View 페이지로 데이터를 뿌려주었습니다.
function getStoryItem(image) {
let item = `<div class="story-list__item">
<div class="sl__item__header">
<div>
<img class="profile-image" src="/upload/${image.user.profile_image_url}"
onerror="this.src='/images/person.jpeg'" />
</div>
<div>${image.user.username}</div>
</div>
<div class="sl__item__img">
<img src="/upload/${image.post_image_url}" />
</div>
<div class="sl__item__contents">
<div class="sl__item__contents__icon">
<button>`;
if (image.likesState)
item += `<i class="fas fa-heart active" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`;
else item += `<i class="far fa-heart" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`;
item += `
</button>
</div>
<span class="like"><b id="storyLikeCount-${image.id}">${image.likesCount}</b>likes</span>
<div class="sl__item__contents__content">
<p>${image.caption}</p>
</div>
<div id="storyCommentList-${image.id}">`;
image.comments.forEach((comment) => {
item +=
`<div class="sl__item__contents__comment" id="storyCommentItem-${comment.id}">
<p>
<b>${comment.user.username} :</b> ${comment.content}
</p>`;
if(principalId == comment.user.id) {
item +=
`<button onclick="deleteComment(${comment.id})">
<i class="fas fa-times"></i>
</button>`;
}
item += `</div>`;
});
item += `
</div>
<div class="sl__item__input">
<input type="text" placeholder="댓글 달기..." id="storyCommentInput-${image.id}" />
<button type="button" onClick="addComment(${image.id})">게시</button>
</div>
</div>
</div>`;
return item;
}
// (4) 댓글쓰기
function addComment(imageId) {
let commentInput = $(`#storyCommentInput-${imageId}`);
let commentList = $(`#storyCommentList-${imageId}`);
let data = {
imageId: imageId,
content: commentInput.val()
}
if (data.content === "") {
alert("댓글을 작성해주세요!");
return;
}
$.ajax({
type: "POST",
url: "/api/comment",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json"
}).done(resp => {
let content =
`<div class="sl__item__contents__comment" id="storyCommentItem-${resp.data.id}">
<p>
<b>${resp.data.user.username} :</b>
${resp.data.content}
</p>
<button onclick="deleteComment(${resp.data.id})"><i class="fas fa-times"></i></button>
</div>`;
commentList.prepend(content);
}).fail(error => {
console.log(error);
});
commentInput.val("");
}
// (5) 댓글 삭제
function deleteComment(commentId) {
$.ajax({
type: "DELETE",
url: `/api/comment/${commentId}`,
dataType: "json"
}).done(resp => {
$(`#storyCommentItem-${commentId}`).remove();
}).fail(error => {
console.log(error);
});
}
📝 Result
- 결과를 살펴보면 댓글 작성과 삭제가 알맞게 수행되는 것을 확인할 수 있으며, 댓글 작성자가 아닐 경우 삭제 버튼이 나타나지 않는 것을 확인할 수 있습니다. 또한 아무런 글도 적지 않을 경우 Validation 체크가 적절히 수행되어 예외 처리되는 것을 확인하였습니다.
'🚗 Backend Toy Project > Photogram' 카테고리의 다른 글
[Photogram] OAuth2 페이스북 로그인 (0) | 2022.07.19 |
---|---|
[Photogram] 유효성 검사 자동화 - AOP 처리 (0) | 2022.07.18 |
[Photogram] 프로필 사진 변경 (0) | 2022.07.16 |
[Photogram] 인기 페이지 구현 (2) | 2022.07.16 |
[Photogram] 좋아요 구현하기 (0) | 2022.07.15 |