[Spring] 구인 구직 서비스 5-2 쪽지 삭제 수정

춤인형의 개발일지·2025년 2월 2일

Spring실습

목록 보기
27/40

쪽지 삭제 수정

쪽지 삭제의 문제점은 이러했다.

  • 구현되어 있는 쪽지 삭제는 내가 보낸 쪽지를 삭제하면 받은 사람의 쪽지도 같이 삭제되어버린다. 이게 맞을까? 놉!

그래서 생각을 해본게

첫번째 방법

  • 메세지를 생성할 때 하나의 메세지를 이쪽 저쪽 공유할 수 있는 user_id 칼럼을 만들자
    ? 어떻게,, 만드는거지 ? -> 이론적으로는 뭔지 알겠다. 하나의 쪽지는 공유되어야하는게 맞기 때문에 쪽지1이 나도 가지고 있고, 내가 보낸 A라는 사람도 가지고 있어서 내가 삭제해도 저 사람을 가지고 있는 별개의 쪽지로 보게 한다.

두번째 방법은

  • 삭제했다 치는걸로 만들어서 눈속임 하는 방법
    isDelete를 만들어놔서 삭제할 때 true로 바꾸고 조회할 때는 false인 쪽지들만 보여주게 한다.

두번째 방법
<필요한 것들>
기본적으로 필요한게 뭐가 있는지 그려보자

  • 삭제 여부를 판별해줄 변수
  • 삭제할 때의 삭제여부를 변경해줄 함수

설계가 되었으면 구현을 해보자.

  1. message엔티티 속에 보낸 사람, 받은 사람의 쪽지를 삭제여부 변수를 작성한다.
private boolean deletedBySender = false;

private boolean deletedByReceiver = false;

처음 생성될 때는 삭제가 되지 않아야하니까 기본적으로 false를 넣어둔다.

  1. 삭제할 때 true로 바꿀 함수도 필요하다.
public void deleteBySender() {
        this.deletedBySender = true;
    }

    public void deleteByReceiver() {
        this.deletedByReceiver = true;
    }
  1. Service에 있는 삭제 함수에 삭제할 때 삭제여부 함수를 넣어 구현해보자. 먼저 기존 메세지 삭제 코드는 아래와 같다.
//내가 보낸 메세지 삭제
    public void deleteSendMessage(Long messageId, String senderId) {
        Message message = messageRepository.findById(messageId)
                .orElseThrow(() -> new NoSuchElementException("해당 메세지가 없습니다."));
        if (!message.getSenderId().equals(senderId)) throw new IllegalArgumentException("송신 ID가 일치하지 않습니다.");
        else {
            message.deleteById(messageId);
        }
    }

현재 코드는 그냥 메세지 자체를 삭제하기 때문에 나한테도 삭제되고, 상대방도 상대방의 의지없이 삭제된다.
따라서 삭제여부만 나타내는 함수로 나타내보자

//내가 보낸 메세지 삭제
    public void deleteSendMessage(Long messageId, String senderId) {
        Message message = messageRepository.findById(messageId)
                .orElseThrow(() -> new NoSuchElementException("해당 메세지가 없습니다."));
        if (!message.getSenderId().equals(senderId)) throw new IllegalArgumentException("송신 ID가 일치하지 않습니다.");
        else {
        //여기가 수정된 부분
            message.deleteBySender();
        }
    }

이렇게 하면 메세지는 삭제 되었다고 하면서 조회할 때 보이지않게 하는 것이다. 즉, 조회할 땐 isDelete = true인건 안보이게 하면 된다.

그럼 조회쪽을 다시한번 생각해보면, 보낸 쪽지가 있고, 받은 쪽지가 있을텐데 어쨌거나 보낸 쪽지라면 보낸 쪽지 중에 삭제하지 않은 메세지를 반환해야하고, 받은 쪽지라면 받은 쪽지 중에 삭제하지 않은 메세지를 반환해야한다.

  • 즉 삭제하지 않은 메세지라 하면 isDelete = false인 것만 반환하야한다.

일단, isDelete = false를 알기 위해서는 조건을 거쳐야한다. true인건 다 안보여지게 하는거니까 조건문을 써야될 것 같은데 조회의 코드는 stream.map이 있었다. 그래서 .fliter로 걸려줘야겠다고 생각했다.

private List<MessageSendResponse> messageAdd(List<Message> messageList, boolean isSender) {
        return messageList.stream()
                .filter(message -> {
                    if (내가 보낸 문자) {
                    return !message.isDeletedBySender(); 
                } else {
                    return !message.isDeletedByReceiver();
                }
                })
                .map(message -> new MessageSendResponse(
                        message.getId(),
                        message.getSenderName()
                ))
                .toList();
    }

나는 계속 헷갈렸던 부분이 !message.isDeletedBySender(); 여기다.
하나하나 살펴보자면

  • private boolean deletedBySender = false;
    이 부분은 기본적으로 생성될 때 false인 상태이다.
  • message.isDeletedBySender()
    true → 보낸 사람이 삭제함
    false → 보낸 사람이 삭제 안 함
    이 부분은 false = 즉 아직 보낸 사람이 삭제 안한 상태

  • !message.isDeletedBySender()
    true → 보낸 사람이 삭제 안 함 → 보낸 메시지 목록에 포함 ✅
    false → 보낸 사람이 삭제함 → 보낸 메시지 목록에서 제외 ❌
    이 부분은 true = 보낸 사람이 삭제 안 한 상태

따라서

.filter(message -> {
                    if (내가 보낸 문자) {
                    return !message.isDeletedBySender(); 
                }

이 부분을 보면 "보낸 사람이 삭제하지 않은 메세지를 보여줘라" 라는 의미가 된다.

이렇게 짠 코드를 돌려보니까 테스트코드가 실패했다. 보니까 내가 삭제한게 적용이 안됐다.
이유를 보니, delete를 하고 DB에 반영을 해주지 않았다. 반영을 해주는 방법은 뭐가 있을까

  1. messageRepository.save(message)
  2. @Transactional

❓save()를 하면 자동적으로 DB에 반영이 되나?

  • 아니요!
    save는 엔티티를 DB에 저장하거나 업데이트하는 역할이다. 변경을 위해서는 다른 수단을 활용해야한다.

이걸 좀 더 공부해보니 아래와 같은 정보를 얻을 수 있었다.

🧐 JPA는 데이터를 DB에서 가져오면 임시 저장소(=영속성 컨텍스트(Persistence Context)) 에 넣어두고 임시 저장소에 있는 데이터가 바뀌면 자동으로 바뀐 내용을 DB에 반영한다. 이를 "변경 감지(Dirty Checking)" 라 하는데 여튼, 이 메커니즘을 사용해, 엔티티의 변경 사항을 자동으로 감지하고 DB에 반영한다.

❓그럼 @Transactional를 하면 자동적으로 DB에 반영이 되나?

  • yes!
    왜냐면 Transactional이 변경감지를 하기 때문이다.

💡 @Transactional 의 동작 과정

  1. messageRepository.findById(messageId)
    → 조회된 메세지는 JPA의 영속성 컨텍스트에 저장됨
  2. message.deleteBySender();
    → 엔티티의 deletedBySender 값을 true로 변경
  3. @Transactional를 사용하면 Transactional으로 인해 메서드 종료 시점에 자동으로 DB에 반영됨

🤔 `@Transactional을 사용하지 않는 경우

  • 변경 감지가 작동하지 않음 → save()가 필요함

🤔@Transactional 없이 save()만 사용할 경우

  • 변경 감지가 제대로 동작하지 않을 수 있음
  • 여러 개의 DB 작업이 원자적으로 처리되지 않음
    예를 들어, 여러 개의 메시지를 삭제해야 한다했을 때,
    만약 중간에 어떤 예외가 발생하면, 첫 번째 메시지는 DB에 저장되었지만, 두 번째 메시지는 저장되지 않을 수도 있다. 트랜잭션이 없기 때문에 하나의 작업이 실패해도 이전 작업이 롤백되지 않음

🤔그럼 둘다 사용해야 안전한가?

  • 딱히 그렇진 않다.
    Transactional만 있어도 안전하다.

최종코드는

//내가 보낸 메세지 삭제
    @Transactional
    public void deleteSendMessage(Long messageId, String senderId) {
        Message message = messageRepository.findById(messageId)
                .orElseThrow(() -> new NoSuchElementException("해당 메세지가 없습니다."));
        if (!message.getSenderId().equals(senderId)) throw new IllegalArgumentException("송신 ID가 일치하지 않습니다.");
        else {
            message.deleteBySender();
        }
    }

하나 더 추가로 수정한건 지금 코드는 보내는 사람과 받은 사람의 endpoint가 달랐다.
/messages/receivers/{messageId}
/messages/senders/{messageId}
근데 왜 코드도 receiver인지 senders인지 구분을 하는거 이외에는 코드가 같았다. 그래서 그냥 합쳐보기로 했다.

  • endpoint: /messages/{messageId}
 @Transactional
    public void deleteMessage(Long messageId, String memberId) {
        Message message = messageRepository.findById(messageId)
                .orElseThrow(() -> new NoSuchElementException("해당 쪽지가 없습니다."));

        // 보낸 사람인지 받은 사람인지 확인
        if (message.getSenderId().equals(memberId)) {
            message.deleteBySender();  // 보낸 사람이 삭제한 경우
        } 
        if (message.getReceiverId().equals(memberId)) {
            message.deleteByReceiver();  // 받은 사람이 삭제한 경우
        } 
        else {
            throw new IllegalArgumentException("이 쪽지는 해당 사용자의 권한이 없습니다.");
        }
    }

이렇게 구분만 해주고 삭제시키기!

0개의 댓글