내일배움캠프/TIL

[Spring]_뉴스피드 팀프로젝트 트러블 슈팅

cork-7 2025. 2. 20. 13:34

 

⌘ 발단

 - 유저 삭제 과정 오류

 

⌘ 원인

- 프로필에서 다양한 엔티티의 연관을 지어 메소드를 사용하고 있다. 그 상태에서 유저를 삭제하면 남아있는 연관관계에

   오류가 발생해 500에러 발생

@Transactional
public void deleteUserById(Long id, String oldPassword) {
    User user = userRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("해당 유저가 존재 하지 않습니다"));
    
    // 기존 비밀번호를 틀리게 작성할 경우 에러 발생
    if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
        throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다.");
    }
    
    // 1. 유저가 남긴 댓글 삭제
    List<Comment> comments = commentRepository.findByUserId(id);
    if (!comments.isEmpty()) {
        commentRepository.deleteByUserId(id);
    }

    // 2. 유저가 누른 좋아요 삭제
    List<Like> likes = likeRepository.findByUserId(id);
    if (!likes.isEmpty()) {
        likeRepository.deleteByUserId(id);
    }
    
    // 3. 유저가 작성한 게시물 삭제
    List<Post> posts = postRepository.findByUserId(id);
    if (!posts.isEmpty()) {
        postRepository.deleteAll(posts);
    }
    
    // 4. 유저 계정 삭제
    userRepository.deleteById(id);
}

- 해당 로직은 유저의 것만 지운다

 

⌘ 해결

// 3. 유저가 작성한 게시물 삭제(게시글에 작성한 댓글도 삭제)
        List<Post> posts = postRepository.findByUserId(id);
        if(!posts.isEmpty()){
            for (Post post : posts) {
                List<Like> postLikes = likeRepository.findByPost_PostId(post.getPostId());
                if(!postLikes.isEmpty()) {
                    likeRepository.deleteAll(postLikes);
                }
            }
            for (Post post : posts) {
                List<Comment> postComments = commentRepository.findByPost_PostId(post.getPostId());
                if(!postComments.isEmpty()){
                    commentRepository.deleteAll(postComments);
                }
            }
            postRepository.deleteAll(posts);
        }

- 유저의 게시글에 작성된 댓글과 좋아요를 모두 삭제하게 코드를 작성 

- 이를 위해 좋아요 레포지토리에 메소드 작성

    // 유저가 누른 좋아요 삭제
    List<Like> deleteByUserId(Long userId);
    // 게시글에 달린 좋아요 삭제
    List<Like> findByPost_PostId(Long postPostId);
    // 댓글에 달린 좋아요 삭제
    List<Like> findByComment_id(Long commentId);

 - 유저와 친구 관계인 사람에서도 유저를 삭제

// 4. 유저의 친구 삭제(친구 관계인 사람에서도 삭제)
List<Friend> friends = friendRepository.findAcceptedFriend(id);
if(!friends.isEmpty()){
  for (Friend friend : friends) {
       friendRepository.delete(friend);
       friendRepository.deleteByUserIdAndFriendId(
                        friend.getRequester().getId(), 
                        friend.getReceiver().getId());
            }
            friendRepository.deleteAll(friends);

 

 // 삭제하는 유저와 친구인 사람 삭제(관계를 삭제)
@Modifying
@Query("""
DELETE FROM Friend f WHERE (
      f.requester.id = :userId AND f.receiver.id = :friendId) OR (
      f.requester.id = :friendId AND f.receiver.id = :userId)
""")
void deleteByUserIdAndFriendId(
     @Param("userId") Long userId, 
     @Param("friendId") Long friendId);

-  친구 레포지토리에 쿼리를 통해 삭제하는 로직 구현

 

<전체 로직>

package com.example.newspeed.profile.service;

import com.example.newspeed.comment.entity.Comment;
import com.example.newspeed.comment.repository.CommentRepository;
import com.example.newspeed.common.config.PasswordEncoder;
import com.example.newspeed.friend.entity.Friend;
import com.example.newspeed.friend.repository.FriendRepository;
import com.example.newspeed.like.entity.Like;
import com.example.newspeed.like.repository.LikeRepository;
import com.example.newspeed.post.dto.response.PostResponse;
import com.example.newspeed.post.entity.Post;
import com.example.newspeed.post.repository.PostRepository;
import com.example.newspeed.post.service.PostService;
import com.example.newspeed.user.dto.request.DeletePassword;
import com.example.newspeed.user.dto.response.UserResponse;
import com.example.newspeed.user.dto.response.UserResponseDto;
import com.example.newspeed.user.entity.User;
import com.example.newspeed.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.aspectj.weaver.ast.Literal;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;

@Service
@RequiredArgsConstructor
public class ProfileService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final PostService postService;
    private final PostRepository postRepository;
    private final CommentRepository commentRepository;
    private final LikeRepository likeRepository;
    private final FriendRepository friendRepository;


    @Transactional
    public UserResponse updatePassword(Long id, String oldPassword, String newPassword) {
        User user=userRepository.findById(id)
                .orElseThrow(()->new IllegalArgumentException("해당 유저가 존재하지 않습니다."));

        //기존 비밀번호를 틀리게 작성할 경우 에러 발생
        if(!passwordEncoder.matches(oldPassword,user.getPassword())) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"기존 비밀번호가 일치하지 않습니다.");
        }

        String encodedPassword = passwordEncoder.encode(newPassword);
        //변경된 비밀번호로 저장
        user.updatePassword(encodedPassword);

        return new UserResponse(
                user.getId(),
                user.getUserName(),
                user.getEmail()
        );
    }

    @Transactional
    public UserResponse updateUserName(Long id, String newUserName) {
        User user=userRepository.findById(id)
                .orElseThrow(()->new IllegalArgumentException("해당 유저가 존재하지 않습니다."));

        //변경된 유저 이름 저장
        user.updateUserName(newUserName);

        return new UserResponse(
                user.getId(),
                user.getUserName(),
                user.getEmail()
        );
    }

    // 유저가 작성한 게시물 조회
    @Transactional
    public List<PostResponse> getUserPosts(Long userId){
        return postService.getPostByUserId(userId);
    }

    // 유저 계정 삭제
    @Transactional
    public void deleteUserById(Long id, DeletePassword deleteDto) {
        User user = userRepository.findById(id)
                .orElseThrow(()-> new IllegalArgumentException("해당 유저가 존재 하지 않습니다"));
        System.out.println(deleteDto);
        //기존 비밀번호를 틀리게 작성할 경우 에러 발생
        if(!passwordEncoder.matches(deleteDto.getPassword(), user.getPassword())) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"비밀번호가 일치하지 않습니다.");
        }

        // 1. 유저가 남긴 댓글 삭제(댓글에 달린 좋아요도 삭제)
        List<Comment> comments = commentRepository.findByUserId(id);
        if(!comments.isEmpty()){
            for (Comment comment : comments) {
                List<Like> commentLikes = likeRepository.findByComment_id(comment.getId());
                if(!commentLikes.isEmpty()){
                    likeRepository.deleteAll(commentLikes);
                }
            }
            commentRepository.deleteAll(comments);
        }

        // 2. 유저가 누른 좋아요 삭제
        List<Like> likes = likeRepository.deleteByUserId(id);
        if (!likes.isEmpty()) {
            likeRepository.deleteAll(likes);
        }

        // 3. 유저가 작성한 게시물 삭제(게시글에 작성한 댓글도 삭제)
        List<Post> posts = postRepository.findByUserId(id);
        if(!posts.isEmpty()){
            for (Post post : posts) {
                List<Like> postLikes = likeRepository.findByPost_PostId(post.getPostId());
                if(!postLikes.isEmpty()) {
                    likeRepository.deleteAll(postLikes);
                }
            }
            for (Post post : posts) {
                List<Comment> postComments = commentRepository.findByPost_PostId(post.getPostId());
                if(!postComments.isEmpty()){
                    commentRepository.deleteAll(postComments);
                }
            }
            postRepository.deleteAll(posts);
        }

        // 4. 유저의 친구 삭제(친구 관계인 사람에서도 삭제)
        List<Friend> friends = friendRepository.findAcceptedFriend(id);
        if(!friends.isEmpty()){
            for (Friend friend : friends) {
                friendRepository.delete(friend);
                friendRepository.deleteByUserIdAndFriendId(friend.getRequester().getId(), friend.getReceiver().getId());
            }
            friendRepository.deleteAll(friends);
        }
        
        // 5. 유저 계정 삭제
        userRepository.deleteById(id);
    }
}

 

⌘ 결과

- 해당 메소드를 작성하며 연관관계 맺는 것 뿐만아니라 삭제하는 것을 배웠고, 쿼리를 통해 복잡한 조건의 삭제를 진행하였다.

 

⌘ 후기

- Spring 어려워~~~ Git도 너무 어려워~~ 팀플하면서 git오류가 너무 많이 발생했다....