728x90
이전글
댓글 Entity
package com.likelion.finalproject.domain.entity;
import com.likelion.finalproject.domain.dto.comment.CommentReadResponse;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.*;
import java.time.LocalDateTime;
import static javax.persistence.FetchType.LAZY;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Entity
public class Comment extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String comment;
@JoinColumn(name = "user_id")
@ManyToOne(fetch = LAZY)
private User user;
@JoinColumn(name = "post_id")
@ManyToOne(fetch = LAZY)
private Post post;
private LocalDateTime deletedAt;
public void update(String comment) {
this.comment = comment;
}
public CommentReadResponse toResponse() {
return CommentReadResponse.builder()
.id(this.id)
.comment(this.comment)
.userName(this.user.getUserName())
.postId(this.post.getId())
.createdAt(this.getCreatedAt())
.build();
}
}
댓글 작성
- 앤드포인트
POST /posts/{postsId}/comments
- 입력
{
"comment" : "comment test4"
}
- 리턴
{
"resultCode": "SUCCESS",
"result":{
"id": 4,
"comment": "comment test4",
"userName": "test",
"postId": 2,
"createdAt": "2022-12-20T16:15:04.270741"
}
}
Comment Requst Dto
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class CommentRequest {
private String comment;
public Comment toEntity(Integer id, User user, Post post) {
System.out.println("toentity = " + post.getCreatedAt());
return Comment.builder()
.id(id)
.comment(this.comment)
.user(user)
.post(post)
.build();
}
}
Comment Write Response
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentWriteResponse {
private Integer id;
private String comment;
private String userName;
private Integer postId;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime createdAt;
public static CommentWriteResponse of(Comment savedComment) {
return CommentWriteResponse.builder().
id(savedComment.getId())
.comment(savedComment.getComment())
.userName(savedComment.getUser().getUserName())
.postId(savedComment.getPost().getId())
.createdAt(savedComment.getCreatedAt())
.build();
}
}
테스트 코드 작성
class CommentControllerTest {
@Test
@DisplayName("댓글 작성 성공")
void success_write_comment() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
CommentRequest request = new CommentRequest("댓글 작성");
CommentWriteResponse response = CommentWriteResponse.builder()
.id(1)
.comment(request.getComment())
.userName(user.getUserName())
.postId(post.getId())
.createdAt(LocalDateTime.now())
.build();
given(commentService.writeComment(any(), any(), any())).willReturn(response);
String url = String.format("/api/v1/posts/%d/comments", post.getId());
mockMvc.perform(post(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").exists())
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.id").exists())
.andExpect(jsonPath("$.result.id").value(1))
.andExpect(jsonPath("$.result.comment").exists())
.andExpect(jsonPath("$.result.comment").value("댓글 작성"))
.andExpect(jsonPath("$.result.postId").exists())
.andExpect(jsonPath("$.result.postId").value(1))
.andExpect(jsonPath("$.result.createdAt").exists())
.andDo(print());
}
}
api 구현
Controller
...
public class CommentController {
...
private final CommentService commentService;
@ApiOperation(value = "댓글 작성")
@PostMapping("/{postId}/comments")
public Response<CommentWriteResponse> writeComment(@PathVariable Integer postId,
@RequestBody CommentRequest request,
Authentication authentication){
String userName = authentication.getName();
CommentWriteResponse response = commentService.writeComment(postId, request, userName);
return Response.success(response);
}
}
- String 문자열을 담은 Comment Request Dto를 사용하여 Comment를 추가한다.
Service
public class CommentService {
private final CommentRepository commentRepository;
private final Services service;
public CommentWriteResponse writeComment(Integer postId, CommentRequest requset, String userName) {
User user = service.validateGetUserByUserName(userName);
Post post = service.validateGetPostById(postId);
Comment savedComment = Comment.builder()
.user(user)
.post(post)
.comment(requset.getComment())
.build();
commentRepository.save(savedComment);
return CommentWriteResponse.of(savedComment);
}
}
- 해당 유저가 존재하는지 확인
- 댓글을 작성할 해당 게시글이 존재하는지 확인
- 둘 다 존재한다면 request를 받아서 댓글을 저장한다.
댓글 수정
- 앤드포인트
PUT /posts/{postId}/comments/{id}
- 입력
{
"comment" : "modify comment"
}
- 리턴
{
"resultCode": "SUCCESS",
"result":{
"id": 4,
"comment": "modify comment",
"userName": "test",
"postId": 2,
"createdAt": "2022-12-20T16:15:04.270741",
"lastModifiedAt": "2022-12-23T16:15:04.270741"
}
}
Comment Modify Response
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentModifyResponse {
private Integer id;
private String comment;
private String userName;
private Integer postId;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime createdAt;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime lastModifiedAt;
public static CommentModifyResponse of(Comment comment) {
return CommentModifyResponse.builder()
.id(comment.getId())
.comment(comment.getComment())
.userName(comment.getUser().getUserName())
.postId(comment.getPost().getId())
.createdAt(comment.getCreatedAt())
.lastModifiedAt(comment.getLastModifiedAt())
.build();
}
}
테스트 코드 작성
class CommentControllerTest {
@Test
@DisplayName("댓글 수정 성공")
void success_modify_comment() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
CommentRequest request = new CommentRequest("댓글 수정쓰");
CommentModifyResponse response = CommentModifyResponse.builder()
.id(1)
.comment(request.getComment())
.userName(user.getUserName())
.postId(post.getId())
.createdAt(LocalDateTime.now())
.lastModifiedAt(LocalDateTime.now())
.build();
given(commentService.modifyComment(any(), any(), any(), any())).willReturn(response);
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(post(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").exists())
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.id").exists())
.andExpect(jsonPath("$.result.id").value(1))
.andExpect(jsonPath("$.result.comment").exists())
.andExpect(jsonPath("$.result.comment").value("댓글 수정쓰"))
.andExpect(jsonPath("$.result.postId").exists())
.andExpect(jsonPath("$.result.postId").value(1))
.andExpect(jsonPath("$.result.id").exists())
.andExpect(jsonPath("$.result.id").value(1))
.andExpect(jsonPath("$.result.createdAt").exists())
.andExpect(jsonPath("$.result.lastModifiedAt").exists())
.andDo(print());
}
api 구현
Controller
...
public class CommentController {
...
@ApiOperation(value = "댓글 수정")
@PutMapping("/{postId}/comments/{id}")
public Response<CommentModifyResponse> modifyComment(@PathVariable Integer postId,
@PathVariable Integer id,
@RequestBody CommentRequest request,
Authentication authentication){
String userName = authentication.getName();
CommentModifyResponse response = commentService.modifyComment(postId, id, request, userName);
return Response.success(response);
}
}
Service
...
public class CommentService {
...
public CommentModifyResponse modifyComment(Integer postId, Integer id, CommentRequest request, String userName) {
User user = service.validateGetUserByUserName(userName);
Post post = service.validateGetPostById(postId);
Comment comment = service.validateGetCommentById(id);
service.validateMatchUsers(user, comment);
comment.update(request.getComment());
return CommentModifyResponse.of(commentRepository.save(comment));
}
}
- 해당 유저가 존재하는지 확인
- 댓글을 작성할 해당 게시글이 존재하는지 확인
- 수정할 댓글이 존재하는지 확인
- 댓글의 작성자와 수정 요청하는 요청자가 같은 사람인지 확인
- set기능을 하는 update 메서드를 생성하여 Entity의 값을 수정한 후 DB에 저장한다.
문제 발생
빌더 패턴을 사용하여 수정 기능을 구현하는 도중, Comment 엔티티가 상속한 BaseEntity의 CreatedAt이 Null로 반환되는 현상이 발견되었다.
문제는 Database에는 CreatedAt이 무사히 값이 존재하고 있다는 것이었다.
이것이 왜 문제냐면 Database에 Null로 입력된다면 해당 변수에 어노테이션으로 @Column(updatable = false)
를 추가하면 매우 간단하게 해결되는 문제이기 때문이다.
하지만 Database에는 이상 없이 CreateAt의 값이 존재하고 있었다.
문제 해결을 위한 시도
- Builder 패턴의 문제인가 싶어서 save 후 다시 DB에서 값을 꺼내왔지만 여전히 null로 받아오는 중.
이유 추측
- Dto를 Entity로 변경하는 과정에서 기존의 정보는 넘어가고 있으나
@Column(updatable = false)
이므로 createAt에 대한 정보는 update가 되지 않음. 그래서 null의 정보를 객체가 갖게 된다. - save 후 다시 findById를 통해 정보를 다시 받아와도 왜 null인가? 이미 영속성에 null로 된 Entity가 존재하므로 DB가 아닌 영속성에서 정보를 받아오기 때문에 null을 그대로 가져오는 것 같다.
그렇다면?
- flush를 해주고 find하는 방법
- set 메서드를 사용하여 Entity 추가 생성 없이 내용을 수정하는 방법
선택
Set 관련 메서드를 하나 만들었다. 메서드 명에 Set이 적혀있으면 수정에 대해 오해가 생길 수 있으므로 명확한 메서드 명을 제작하였다.
...
public class Comment extends BaseEntity{
...
public void update(String comment) {
this.comment = comment;
}
}
반응형
'프로젝트 > Archive' 카테고리의 다른 글
[07] Like Api 개발 (0) | 2023.01.05 |
---|---|
[06] Comment Api 개발 - 2 (0) | 2023.01.05 |
[05] 리팩토링 - 2 (0) | 2022.12.29 |
[05] 리팩토링 - 1 (0) | 2022.12.29 |
[04] Post Test 코드 작성 - 2 (Service Test) (0) | 2022.12.27 |