일정 관리 앱 develop : 트러블 슈팅 TIL

coldrice99·2024년 10월 17일
0

일정과 댓글 기능 구현 중 발생한 문제 해결

이번 프로젝트에서 일정과 댓글 기능을 구현하면서 발생한 여러 문제들을 해결하는 과정을 기록하였다. 주요 문제는 비밀번호 검증 로직, 요청 데이터 바인딩, 그리고 JPA 연관 관계 설정과 DTO 변환에서 발생한 혼란 등이었다. 이를 해결한 방법과 함께, 각 문제를 정리해보겠다.

1. JPA 연관 관계 설정의 어려움

문제: 처음 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;
  • 결과: JPA 연관 관계를 명확하게 설정하여, 일정 삭제 시 댓글도 함께 삭제되는 기능을 구현할 수 있었다.

2. 비밀번호 검증 로직 문제

문제: 일정 수정 및 삭제 시 비밀번호 검증이 제대로 작동하지 않았다. 잘못된 비밀번호로도 일정이 수정되거나 삭제되는 문제가 있었다.

원인: 비밀번호 검증을 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); // 검증 후 저장
}
  • 결과: 비밀번호 검증을 먼저 수행하여, 검증에 실패한 경우 DB에 반영되지 않도록 했다.

3. RequestBody 누락으로 인한 Null 값 문제

문제: 댓글 삭제 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 어노테이션을 추가하여, 요청 데이터를 제대로 받아서 처리할 수 있게 되었다.

4. 잘못된 멤버 조회 순서 문제

문제: 일정 수정 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);
}
  • 결과: 검증 후에 멤버를 조회하도록 순서를 수정하여 문제를 해결하였다.

5. DTO 변환 로직에서의 혼란

문제: 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
    );
}
  • 결과: 댓글 개수를 포함하는 DTO 변환 메서드를 추가하여, 두 가지 변환 방식을 모두 사용할 수 있게 되었다.

트러블 슈팅의 교훈

  • 검증은 항상 먼저 해야 한다: DB에 중요한 변경을 하기 전에, 검증을 먼저 수행해 올바른 데이터만 반영되도록 해야 한다. 특히 비밀번호와 같이 민감한 정보는 우선 검증해야 실수를 방지할 수 있다.
  • 어노테이션의 중요성: @RequestBody와 같은 어노테이션이 누락되면, 클라이언트와의 데이터 주고받기가 엉킬 수 있다. 기본적인 어노테이션을 놓치지 않도록 꼼꼼히 체크하는 것이 중요하다.
  • 코드의 순서도 중요하다: 비즈니스 로직에서 검증과 데이터 조작 순서는 예상치 못한 버그를 일으킬 수 있다. 적절한 순서대로 로직을 작성하는 습관을 들여야 한다.

https://github.com/coldrice99/ScheduleMangerApp_Develop.git

profile
서두르지 않으나 쉬지 않고

0개의 댓글