Spring/[P] AI 챗봇 기반 맞춤형 레시피 서비스

[SpringBoot/Querydsl] QnA 대댓글 (4) 삭제

alsruds 2024. 3. 11. 23:48

 

Querydsl 을 이용하여, 작성했던 QnA 를 삭제해봅시다 ~

 


 

[ QnA 삭제하기 ]

 

0. QnA 작성하기

2024.03.04 - [Spring/[Project] AI 챗봇 기반 맞춤형 레시피 서비스] - [SpringBoot/Querydsl] QnA 대댓글 (2) 작성

 

[SpringBoot/Querydsl] QnA 대댓글 (2) 작성

대댓글 형식의 QnA 를 작성해봅시다 ~ [ QnA 작성하기 ] 0. Querydsl 설정 2024.03.01 - [Spring/[Project] AI 챗봇 기반 맞춤형 레시피 서비스] - [SpringBoot/Querydsl] 대댓글 Querydsl (1) 프로젝트 설정 [SpringBoot/Querydsl]

alsrudalsrudalsrud.tistory.com

 

1. Controller

@RestController
@RequestMapping("/post")
@RequiredArgsConstructor
public class QnaController {

    ...

    // Qna 삭제
    @DeleteMapping("/qna/{qnaId}")
    public ApiResponse<QnaResponseDTO.deleteQnaDTO> deleteQna(@PathVariable("qnaId") Long qnaId) {
        qnaService.deleteQna(qnaId);
        return ApiResponse.onSuccess(QnaConverter.deleteQnaResult());
    }
}

 

· 삭제할 글 id 넘겨주기

 

2. Service

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class QnaServiceImpl implements QnaService {

    ...

    // Qna 삭제
    @Override
    public void deleteQna(Long qnaId) {

        // 부모 댓글과 함께 조회
        Qna qna = qnaCustomRepository.findQnaByIdWithParent(qnaId).orElseThrow(() -> new GeneralException(ErrorStatus.POST_QNA_NOT_FOUND));

        if (!qna.getChildren().isEmpty()) { // 자식이 있을 때
            // isDeleted 상태만 변경
            qna.changeIsDeleted(true);

            // S3 사진 삭제
            if (qna.getImageUrl() != null) {
                s3Service.deleteImage(qna.getFileName(), "images");
            }

        } else { // 자식 댓글이 없을 때

            // 삭제 가능한 조상 댓글 삭제
            qnaRepository.delete(getDeletableAncestorQna(qna));

            // S3 사진 삭제
            if (qna.getImageUrl() != null) {
                s3Service.deleteImage(qna.getFileName(), "images");
            }
        }
    }

    private Qna getDeletableAncestorQna(Qna qna) {

        // 현재 댓글의 부모
        Qna parent = qna.getParent();

        // 재귀 : 부모 댓글이 있음 & 부모의 자식 댓글이 1개 & 부모의 삭제 상태가 True
        if (parent != null && parent.getChildren().size() == 1 && parent.getIsDeleted()) {
            return getDeletableAncestorQna(parent);
        }

        // 삭제해야 하는 댓글 반환
        return qna;
    }
}

 

· 자식 댓글이 있으면 isDeleted 상태만 변경하기

· 모든 부모 댓글을 함께 조회하여, 맨 마지막 댓글이 삭제됐을 때 삭제되지 않은 부모 댓글 직전까지 모두 함께 삭제하기

 

3. Repository

@Repository
@RequiredArgsConstructor
public class QnaCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;

    ...

    // Qna 댓글 삭제
    public Optional<Qna> findQnaByIdWithParent(Long qnaId) {

        Qna selectedQna = jpaQueryFactory.select(qna)
                .from(qna)
                .leftJoin(qna.parent).fetchJoin()
                .where(qna.id.eq(qnaId))
                .fetchOne();

        return Optional.ofNullable(selectedQna);
    }
}

 

· 삭제하려는 qna id 가 자식 댓글인, 모든 부모 댓글 함께 조회하기

 

4. 대댓글을 마무리하며..

🐳 항상 JpaRepository 를 상속받아 쿼리를 사용하다가, Querydsl 을 사용하니 새로웠다

결과적으로는, 프론트 측에서 children 리스트를 처리하기가 어렵다고 판단해 다 갈아엎었다

나도 처음 해봐서 시간을 꽤나 들였는데.. 없어져서 아쉬웠다ㅎ (울면서 지웠음)

 

📌 참고한 블로그

https://velog.io/@korea3611/Spring-Boot-대댓글-기능-만들기

 

[Spring Boot] 대댓글 기능 만들기

사이드 프로젝트는 간단한 커뮤니티였고 기획에는 대댓글 기능이 있었습니다. 이번글은 spring data jpa와 querydsl을 활용하여 대댓글기능을 어떻게 구현했는지 작성해보겠습니다. 전체코드는 github

velog.io

https://velog.io/@tjddnths0223/스프링부트JPAqueryDsl-대댓글계층형-구현

 

[스프링부트+JPA+queryDsl] 대댓글(계층형) 구현

요구사항: 무한 대댓글 기능을 구현하고, 게시글 및 부모 댓글 삭제시 자식 댓글들 모두 삭제하기.Post(게시글) Entity@OneToMany(fetch = FetchType.LAZY, mappedBy = "post", cascade = CascadeType.ALL,

velog.io