diff --git a/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountPersistenceAdapter.java b/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountPersistenceAdapter.java index 430d5ab..9ebc128 100644 --- a/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountPersistenceAdapter.java +++ b/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountPersistenceAdapter.java @@ -7,6 +7,7 @@ import com.promsearch.auth.domain.enums.SocialProvider; import com.promsearch.auth.domain.exception.AuthDomainException; import com.promsearch.auth.domain.exception.AuthErrorCode; +import com.promsearch.user.application.port.out.social.DeleteUserSocialAccountsPort; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; @@ -14,7 +15,10 @@ @Component @RequiredArgsConstructor -public class SocialAccountPersistenceAdapter implements LoadSocialAccountPort, SaveSocialAccountPort { +public class SocialAccountPersistenceAdapter implements + LoadSocialAccountPort, + SaveSocialAccountPort, + DeleteUserSocialAccountsPort { private final SocialAccountRepository socialAccountRepository; @@ -36,4 +40,9 @@ public Optional findByProviderAndProviderUserId(SocialProvider pr return socialAccountRepository.findByProviderAndProviderUserId(provider, providerUserId) .map(SocialAccountJpaEntity::toDomain); } + + @Override + public void deleteByUserId(Long userId) { + socialAccountRepository.deleteByUserId(userId); + } } diff --git a/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountRepository.java b/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountRepository.java index 4a8640f..2499b98 100644 --- a/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountRepository.java +++ b/src/main/java/com/promsearch/auth/infrastructure/persistence/SocialAccountRepository.java @@ -10,4 +10,6 @@ public interface SocialAccountRepository extends JpaRepository findByProviderAndProviderUserId(SocialProvider provider, String providerUserId); boolean existsByProviderAndProviderUserId(SocialProvider provider, String providerUserId); + + void deleteByUserId(Long userId); } diff --git a/src/main/java/com/promsearch/user/application/port/out/social/DeleteUserSocialAccountsPort.java b/src/main/java/com/promsearch/user/application/port/out/social/DeleteUserSocialAccountsPort.java new file mode 100644 index 0000000..7eb3b37 --- /dev/null +++ b/src/main/java/com/promsearch/user/application/port/out/social/DeleteUserSocialAccountsPort.java @@ -0,0 +1,13 @@ +package com.promsearch.user.application.port.out.social; + +/** + * 탈퇴한 사용자의 외부 로그인 연결을 제거하는 출력 포트. + * + *

소셜 제공자 측 연결은 유지하되, 우리 서비스의 연결 정보만 제거하여 + * 동일한 소셜 계정으로 다시 가입할 수 있게 한다.

+ */ +public interface DeleteUserSocialAccountsPort { + + /** 사용자의 모든 내부 소셜 로그인 연결을 제거한다. */ + void deleteByUserId(Long userId); +} diff --git a/src/main/java/com/promsearch/user/application/service/command/UserCommandService.java b/src/main/java/com/promsearch/user/application/service/command/UserCommandService.java index f403b3c..feda219 100644 --- a/src/main/java/com/promsearch/user/application/service/command/UserCommandService.java +++ b/src/main/java/com/promsearch/user/application/service/command/UserCommandService.java @@ -8,6 +8,7 @@ import com.promsearch.user.application.port.out.user.SaveUserPort; import com.promsearch.user.application.port.out.profileimage.ProfileImageDeliveryPort; import com.promsearch.user.application.port.out.profileimage.ScheduleProfileImageDeletionPort; +import com.promsearch.user.application.port.out.social.DeleteUserSocialAccountsPort; import com.promsearch.user.application.usecase.BootstrapAdminAccountUseCase; import com.promsearch.user.application.usecase.ChangePasswordUseCase; import com.promsearch.user.application.usecase.DeleteUserUseCase; @@ -67,6 +68,7 @@ public class UserCommandService implements private final PasswordEncoder passwordEncoder; private final ProfileImageDeliveryPort profileImageDeliveryPort; private final ScheduleProfileImageDeletionPort profileImageDeletionPort; + private final DeleteUserSocialAccountsPort deleteUserSocialAccountsPort; @Override public SignupInfo signup(SignupCommand command) { @@ -148,6 +150,7 @@ public void changePassword(ChangePasswordCommand command) { public void delete(Long userId) { User user = loadUserPort.getById(userId); saveUserPort.update(user.delete()); + deleteUserSocialAccountsPort.deleteByUserId(userId); profileImageDeletionPort.afterCommit(user.getProfileImageObjectKey()); log.info("user_deleted userId={}", userId); } diff --git a/src/test/java/com/promsearch/user/application/service/command/UserCommandServiceTest.java b/src/test/java/com/promsearch/user/application/service/command/UserCommandServiceTest.java index cb5e210..1141901 100644 --- a/src/test/java/com/promsearch/user/application/service/command/UserCommandServiceTest.java +++ b/src/test/java/com/promsearch/user/application/service/command/UserCommandServiceTest.java @@ -33,6 +33,7 @@ class UserCommandServiceTest { private FakeUserRepository userRepository; private FakeSaveUserInterestTagPort saveUserInterestTagPort; + private Long deletedSocialAccountUserId; private UserCommandService userCommandService; @BeforeEach @@ -50,7 +51,8 @@ void setUp() { new TestPasswordEncoder(), (externalUrl, objectKey) -> objectKey == null ? externalUrl : "signed:" + objectKey, objectKey -> { - } + }, + userId -> deletedSocialAccountUserId = userId ); } @@ -190,6 +192,7 @@ void deleteMarksUserAsDeletedAndAnonymizesUniqueInformation() { assertThat(deletedUser.getNickname()).startsWith("deleted_1_").endsWith("_user"); assertThat(deletedUser.getName()).isEqualTo("Deleted User"); assertThat(deletedUser.getProfileImageUrl()).isNull(); + assertThat(deletedSocialAccountUserId).isEqualTo(1L); } @Test