Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,15 @@ Response<UserResponse> updateMyPage(
@UserPrincipal User user,
@RequestBody UpdateUserInfoRequest updateUserInfoRequest
);

@Operation(summary = "사용자 탈퇴", description = "사용자 탈퇴 API - 계정과 관련된 모든 데이터가 삭제됩니다")
@ApiResponse(
responseCode = "200",
description = "사용자 탈퇴 성공"
)
@ApiResponse(
responseCode = "401",
description = "인증되지 않은 사용자"
)
Response<Void> deleteUser(@UserPrincipal User user);
}
7 changes: 7 additions & 0 deletions src/main/java/backend/airo/api/user/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@ public Response<UserResponse> updateMyPage(
);
return Response.success(UserResponse.create(updateUser));
}

@DeleteMapping()
@PreAuthorize("isAuthenticated()")
public Response<Void> deleteUser(@UserPrincipal User user) {
userUseCase.deleteUser(user.getId());
return Response.success(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
import backend.airo.domain.user.User;
import backend.airo.domain.user.command.UpdateExistingUserCommand;
import backend.airo.domain.user.query.GetUserQuery;
import backend.airo.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;

@Component
@Service
@Transactional
@RequiredArgsConstructor
public class UserUseCase {

private final GetUserQuery getUserCommand;
private final UpdateExistingUserCommand updateExistingUserCommand;
private final UserRepository userRepository;

public User getUserById(Long userId) {
return getUserCommand.handle(userId);
Expand All @@ -23,4 +27,12 @@ public User updateUser(Long userId, String name, String nickname, String phoneNu
return updateExistingUserCommand.handle(userId, name, nickname, phoneNumber, birthDate);
}

public void deleteUser(Long userId) {
// User 존재 여부 확인
User user = userRepository.findById(userId);

// User와 연관된 모든 데이터 삭제
userRepository.deleteUserWithRelatedData(userId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface UserRepository extends AggregateSupport<User, Long> {

Optional<User> findByEmail(String email);

// User와 연관된 모든 데이터를 함께 삭제
void deleteUserWithRelatedData(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface CommentJpaRepository extends JpaRepository<CommentEntity, Long>

Long countByPostId(Long postId);

void deleteByUserId(Long userId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public interface ImageJpaRepository extends JpaRepository<ImageEntity, Long> {

// Delete
void deleteByPostId(Long postId);
void deleteByUserId(Long userId);


boolean existsByPostId(Long postId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ INSERT INTO user_point (user_id, point_score)
void upsertIncrement(@Param("userId") Long userId, @Param("delta") long delta);

PointEntity findByUserId(Long userId);

void deleteByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface PointHistoryJpaRepository extends JpaRepository<PointHistoryEnt

@Query("SELECT COALESCE(SUM(p.point), 0) FROM PointHistoryEntity p WHERE p.userId = :userId")
Long getPointScoreByUserId(Long userId);

void deleteByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ Slice<PostEntity> findByStatusAndIdLessThanOrderByIdDesc(

@Query("SELECT CASE WHEN COUNT(p) > 0 THEN true ELSE false END FROM PostEntity p WHERE p.id < :postId AND p.status = 'PUBLISHED'")
boolean existsByIdLessThan(@Param("postId") Long postId);

void deleteByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ INSERT IGNORE INTO post_like(post_id, user_id)

int deleteByPostIdAndUserId(Long postId, Long userId);

void deleteByUserId(Long userId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
import backend.airo.domain.user.User;
import backend.airo.domain.user.enums.ProviderType;
import backend.airo.domain.user.repository.UserRepository;
import backend.airo.persistence.post.repository.PostLikeJpaRepository;
import backend.airo.persistence.user.entity.UserEntity;
import backend.airo.persistence.user.repository.UserJpaRepository;
import backend.airo.persistence.post.repository.PostJpaRepository;

import backend.airo.persistence.comment.repository.CommentJpaRepository;
import backend.airo.persistence.image.repository.ImageJpaRepository;
import backend.airo.persistence.point.repository.PointJpaRepository;
import backend.airo.persistence.point_history.repository.PointHistoryJpaRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
Expand All @@ -20,6 +27,14 @@
public class UserAdapter implements UserRepository {

private final UserJpaRepository userJpaRepository;

// 연관 엔티티들의 JpaRepository 주입
private final PostJpaRepository postJpaRepository;
private final PostLikeJpaRepository postLikeJpaRepository;
private final CommentJpaRepository commentJpaRepository;
private final ImageJpaRepository imageJpaRepository;
private final PointJpaRepository pointJpaRepository;
private final PointHistoryJpaRepository pointHistoryJpaRepository;

@Override
public User save(User aggregate) {
Expand Down Expand Up @@ -65,4 +80,27 @@ public Optional<User> findByEmail(String email) {

}

@Override
public void deleteUserWithRelatedData(Long userId) {
// 1. 연관된 이미지 삭제
imageJpaRepository.deleteByUserId(userId);

// 2. 연관된 댓글 삭제
commentJpaRepository.deleteByUserId(userId);

// 3. 연관된 게시글 좋아요 삭제
postLikeJpaRepository.deleteByUserId(userId);

// 4. 연관된 포인트 히스토리 삭제
pointHistoryJpaRepository.deleteByUserId(userId);

// 5. 연관된 포인트 삭제
pointJpaRepository.deleteByUserId(userId);

// 6. 연관된 게시글 삭제
postJpaRepository.deleteByUserId(userId);

// 7. 마지막으로 User 삭제
userJpaRepository.deleteById(userId);
}
}