이번 프로젝트에서 일정과 댓글 기능을 구현하면서 발생한 여러 문제들을 해결하는 과정을 기록하였다. 주요 문제는 비밀번호 검증 로직, 요청 데이터 바인딩, 그리고 JPA 연관 관계 설정과 DTO 변환에서 발생한 혼란 등이었다. 이를 해결한 방법과 함께, 각 문제를 정리해보겠다.
문제: 처음 JPA 연관 관계 설정 시 @OneToMany
, @ManyToOne
, @ManyToMany
등의 어노테이션을 사용하는 과정에서, 관계 매핑을 잘못 설정하여 관계가 올바르게 작동하지 않았다. 특히 유저와 일정, 댓글 간의 관계를 설정할 때 다대다(N:M) 관계와 중간 테이블(@JoinTable
)을 처음 적용하는 과정에서 어려움을 겪었다.
해결: 유저와 일정은 @ManyToMany
관계로 설정하고, 중간 테이블을 사용하여 관계를 연결하였다. 댓글과 일정은 @OneToMany
, @ManyToOne
관계로 설정하고, 일정 삭제 시 댓글도 함께 삭제되도록 orphanRemoval = true
를 적용하였다.
수정 전 코드:
@OneToMany(mappedBy = "todo")
private List<Comment> comments;
수정 후 코드:
@OneToMany(mappedBy = "todo", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;
문제: 일정 수정 및 삭제 시 비밀번호 검증이 제대로 작동하지 않았다. 잘못된 비밀번호로도 일정이 수정되거나 삭제되는 문제가 있었다.
원인: 비밀번호 검증을 DB 업데이트 이후에 실행하면서, 잘못된 비밀번호로도 변경이 이루어졌다.
수정 전 코드:
@Transactional
public void updateTodo(Long todoId, TodoRequestDto requestDto) {
Todo todo = findTodo(todoId);
Member member = findMember(requestDto.getMemberId());
todo.init(requestDto, member); // 업데이트 후 저장
passwordValidation(todo.getPassword(), requestDto.getPassword()); // 비밀번호 검증이 뒤에 있음
}
수정 후 코드:
@Transactional
public void updateTodo(Long todoId, TodoRequestDto requestDto) {
Todo todo = findTodo(todoId);
passwordValidation(todo.getPassword(), requestDto.getPassword()); // 비밀번호 검증을 먼저 수행
todo.init(requestDto, member); // 검증 후 저장
}
문제: 댓글 삭제 API에서 requestDto.getMemberId()
값이 null로 들어오는 문제가 있었다.
원인: 컨트롤러 메서드에 @RequestBody
어노테이션이 누락되어, 클라이언트에서 전달한 JSON 데이터가 제대로 바인딩되지 않았다.
수정 전 코드:
@DeleteMapping("/{commentId}")
public ResponseEntity<Void> deleteComment(
@PathVariable Long commentId,
CommentRequestDto requestDto // @RequestBody 누락
) {
commentService.deleteComment(commentId, requestDto);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
수정 후 코드:
@DeleteMapping("/{commentId}")
public ResponseEntity<Void> deleteComment(
@PathVariable Long commentId,
@RequestBody CommentRequestDto requestDto // @RequestBody 추가
) {
commentService.deleteComment(commentId, requestDto);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@RequestBody
어노테이션을 추가하여, 요청 데이터를 제대로 받아서 처리할 수 있게 되었다.문제: 일정 수정 API에서 작성자 검증 전에 Member
객체를 먼저 조회하여 로직이 꼬이는 문제가 발생했다.
원인: 검증 전에 findMember()
메서드를 실행하면서, 수정 작업 중 혼란이 발생했다.
수정 전 코드:
@Transactional
public void updateTodo(Long todoId, TodoRequestDto requestDto) {
Todo todo = findTodo(todoId);
Member member = findMember(requestDto.getMemberId()); // 먼저 멤버 조회
memberValidation(todo.getMember().getId(), requestDto.getMemberId()); // 이후 작성자 검증
todo.init(requestDto, member);
}
수정 후 코드:
@Transactional
public void updateTodo(Long todoId, TodoRequestDto requestDto) {
Todo todo = findTodo(todoId);
memberValidation(todo.getMember().getId(), requestDto.getMemberId()); // 검증 먼저 실행
Member member = findMember(requestDto.getMemberId()); // 이후 멤버 조회
todo.init(requestDto, member);
}
문제: commentCount
필드를 추가한 후 TodoResponseDto
에서 발생한 문제로, to()
메서드를 오버로딩하려 했으나 인자 순서나 메서드 오버로딩에서 오류가 발생했다.
해결: to()
메서드를 오버로딩하여 댓글 개수 없이 변환할 때와 댓글 개수를 포함할 때 두 가지 경우를 모두 처리할 수 있게 하였다.
수정 전 코드:
public TodoResponseDto to() {
return new TodoResponseDto(
this.id, this.member.getId(), this.title, this.description, this.getCreatedAt(), this.getUpdatedAt()
);
}
수정 후 코드:
public TodoResponseDto to(long commentCount) {
return new TodoResponseDto(
this.id, this.member.getId(), this.title, this.description, this.getCreatedAt(), this.getUpdatedAt(), commentCount
);
}
@RequestBody
와 같은 어노테이션이 누락되면, 클라이언트와의 데이터 주고받기가 엉킬 수 있다. 기본적인 어노테이션을 놓치지 않도록 꼼꼼히 체크하는 것이 중요하다.