From 389ce9b2ccdebc1ba0cd40c003315f6766d15efc Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 29 May 2025 16:37:56 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor:=20SiteUser=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SiteUser 엔티티의 nickname 컬럼에 unique 제약 조건 추가 --- .../com/example/solidconnection/siteuser/domain/SiteUser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java b/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java index bcfb1f9ac..a7230a737 100644 --- a/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java +++ b/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java @@ -51,7 +51,7 @@ public class SiteUser { private AuthType authType; @Setter - @Column(nullable = false, length = 100) + @Column(nullable = false, length = 100, unique = true) private String nickname; @Setter From 069915225437cdc6f55f9ad4b0d34a9b7ceca3d4 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 29 May 2025 17:50:48 +0900 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20site=5Fuser=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=9D=98=20nickname=20=EC=BB=AC=EB=9F=BC=EC=97=90=20u?= =?UTF-8?q?nique=20=EC=A0=9C=EC=95=BD=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V13__set_unique_constraint_to_nickname.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql diff --git a/src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql b/src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql new file mode 100644 index 000000000..75d290f7b --- /dev/null +++ b/src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql @@ -0,0 +1,3 @@ +ALTER TABLE site_user +ADD CONSTRAINT uk_site_user_nickname +UNIQUE (nickname); From e5a7cc20eeda681108da3b57ff0ba7790f788ab3 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 29 May 2025 18:34:19 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refactor:=20SiteUser=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B4=80=EB=A0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MyPageService에서 중복 닉네임 검증 로직 제거 - 컨트롤러에서 try-catch 문을 통해 예외 처리 --- .../common/exception/ErrorCode.java | 3 +++ .../siteuser/controller/MyPageController.java | 23 +++++++++++++++++-- .../siteuser/service/MyPageService.java | 8 ------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java index 6e932a159..00e600201 100644 --- a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java @@ -96,6 +96,9 @@ public enum ErrorCode { USER_DO_NOT_HAVE_GPA(HttpStatus.BAD_REQUEST.value(), "해당 유저의 학점을 찾을 수 없음"), REJECTED_REASON_REQUIRED(HttpStatus.BAD_REQUEST.value(), "거절 사유가 필요합니다."), + // database + DATA_INTEGRITY_VIOLATION(HttpStatus.CONFLICT.value(), "데이터베이스 무결성 제약조건 위반이 발생했습니다."), + // general JSON_PARSING_FAILED(HttpStatus.BAD_REQUEST.value(), "JSON 파싱을 할 수 없습니다."), JWT_EXCEPTION(HttpStatus.BAD_REQUEST.value(), "JWT 토큰을 처리할 수 없습니다."), diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java index ff36d7baa..27f22b1a9 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java @@ -1,10 +1,13 @@ package com.example.solidconnection.siteuser.controller; +import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.common.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.MyPageResponse; import com.example.solidconnection.siteuser.service.MyPageService; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -13,6 +16,8 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import static com.example.solidconnection.common.exception.ErrorCode.DATA_INTEGRITY_VIOLATION; + @RequiredArgsConstructor @RequestMapping("/my") @RestController @@ -34,7 +39,21 @@ public ResponseEntity updateMyPageInfo( @RequestParam(value = "file", required = false) MultipartFile imageFile, @RequestParam(value = "nickname", required = false) String nickname ) { - myPageService.updateMyPageInfo(siteUser, imageFile, nickname); - return ResponseEntity.ok().build(); + try { + myPageService.updateMyPageInfo(siteUser, imageFile, nickname); + return ResponseEntity.ok().build(); + } catch (DataIntegrityViolationException e) { + throw new CustomException(determineErrorCode(e)); + } + } + + private ErrorCode determineErrorCode(DataIntegrityViolationException e) { + String message = e.getMostSpecificCause().getMessage().toLowerCase(); + + if (message.contains("unique") && message.contains("nickname")) { + return ErrorCode.NICKNAME_ALREADY_EXISTED; + } + + return ErrorCode.DATA_INTEGRITY_VIOLATION; } } diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java index 2c84f0518..d79c2c994 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -20,7 +20,6 @@ import java.util.List; import static com.example.solidconnection.common.exception.ErrorCode.CAN_NOT_CHANGE_NICKNAME_YET; -import static com.example.solidconnection.common.exception.ErrorCode.NICKNAME_ALREADY_EXISTED; @RequiredArgsConstructor @Service @@ -48,7 +47,6 @@ public MyPageResponse getMyPageInfo(SiteUser siteUser) { @Transactional public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { if (nickname != null) { - validateNicknameUnique(nickname); validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); siteUser.setNickname(nickname); siteUser.setNicknameModifiedAt(LocalDateTime.now()); @@ -65,12 +63,6 @@ public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String siteUserRepository.save(siteUser); } - private void validateNicknameUnique(String nickname) { - if (siteUserRepository.existsByNickname(nickname)) { - throw new CustomException(NICKNAME_ALREADY_EXISTED); - } - } - private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { if (lastModifiedAt == null) { return; From 158a5f853bd6e11cbbb8f0ff1473c6539d2971d2 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 29 May 2025 18:47:41 +0900 Subject: [PATCH 04/16] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 변경된 컬럼만 업데이트하는 쿼리 사용 --- .../siteuser/repository/SiteUserRepository.java | 10 ++++++++++ .../siteuser/service/MyPageService.java | 6 ++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java index e0617f046..6b11aabb7 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java @@ -3,11 +3,13 @@ import com.example.solidconnection.siteuser.domain.AuthType; import com.example.solidconnection.siteuser.domain.SiteUser; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -22,4 +24,12 @@ public interface SiteUserRepository extends JpaRepository { @Query("SELECT u FROM SiteUser u WHERE u.quitedAt <= :cutoffDate") List findUsersToBeRemoved(@Param("cutoffDate") LocalDate cutoffDate); + + @Modifying + @Query("UPDATE SiteUser s SET s.nickname = :nickname, s.nicknameModifiedAt = :modifiedAt WHERE s.id = :id") + void updateNickname(@Param("id") Long id, @Param("nickname") String nickname, @Param("modifiedAt") LocalDateTime modifiedAt); + + @Modifying + @Query("UPDATE SiteUser s SET s.profileImageUrl = :profileImageUrl WHERE s.id = :id") + void updateProfileImage(@Param("id") Long id, @Param("profileImageUrl") String profileImageUrl); } diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java index d79c2c994..712280b2e 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -48,8 +48,7 @@ public MyPageResponse getMyPageInfo(SiteUser siteUser) { public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { if (nickname != null) { validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); - siteUser.setNickname(nickname); - siteUser.setNicknameModifiedAt(LocalDateTime.now()); + siteUserRepository.updateNickname(siteUser.getId(), nickname, LocalDateTime.now()); } if (imageFile != null && !imageFile.isEmpty()) { @@ -58,9 +57,8 @@ public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String s3Service.deleteExProfile(siteUser); } String profileImageUrl = uploadedFile.fileUrl(); - siteUser.setProfileImageUrl(profileImageUrl); + siteUserRepository.updateProfileImage(siteUser.getId(), profileImageUrl); } - siteUserRepository.save(siteUser); } private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { From 6f7cf1f2628869876ecb6d1043f36f1ddc6a68d1 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 12 Jun 2025 15:19:12 +0900 Subject: [PATCH 05/16] =?UTF-8?q?refactor:=20sign=20up=20=EC=8B=9C=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - nickname 제약 조건의 이름 명시적으로 지정 --- .../auth/service/SignUpService.java | 29 ++++++++++++++----- .../siteuser/controller/MyPageController.java | 4 +-- .../siteuser/domain/SiteUser.java | 6 +++- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java index 95bd4ecac..5652b5252 100644 --- a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java +++ b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java @@ -3,6 +3,7 @@ import com.example.solidconnection.auth.dto.SignInResponse; import com.example.solidconnection.auth.dto.SignUpRequest; import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.location.country.domain.InterestedCountry; import com.example.solidconnection.location.country.repository.CountryRepository; import com.example.solidconnection.location.country.repository.InterestedCountyRepository; @@ -11,6 +12,7 @@ import com.example.solidconnection.location.region.repository.RegionRepository; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -49,17 +51,20 @@ public SignInResponse signUp(SignUpRequest signUpRequest) { // 검증 validateSignUpToken(signUpRequest); validateUserNotDuplicated(signUpRequest); - validateNicknameDuplicated(signUpRequest.nickname()); - // 사용자 저장 - SiteUser siteUser = siteUserRepository.save(createSiteUser(signUpRequest)); + try { + // 사용자 저장 + SiteUser siteUser = siteUserRepository.save(createSiteUser(signUpRequest)); - // 관심 지역, 국가 저장 - saveInterestedRegion(signUpRequest, siteUser); - saveInterestedCountry(signUpRequest, siteUser); + // 관심 지역, 국가 저장 + saveInterestedRegion(signUpRequest, siteUser); + saveInterestedCountry(signUpRequest, siteUser); - // 로그인 - return signInService.signIn(siteUser); + // 로그인 + return signInService.signIn(siteUser); + } catch (DataIntegrityViolationException e) { + throw new CustomException(determineErrorCode(e)); + } } private void validateNicknameDuplicated(String nickname) { @@ -84,6 +89,14 @@ private void saveInterestedCountry(SignUpRequest signUpRequest, SiteUser savedSi interestedCountyRepository.saveAll(interestedCountries); } + private ErrorCode determineErrorCode(DataIntegrityViolationException e) { + if (e.getMessage().contains("uk_site_user_nickname")) { + return ErrorCode.NICKNAME_ALREADY_EXISTED; + } + + return ErrorCode.DATA_INTEGRITY_VIOLATION; + } + protected abstract void validateSignUpToken(SignUpRequest signUpRequest); protected abstract void validateUserNotDuplicated(SignUpRequest signUpRequest); diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java index 27f22b1a9..aed1d0112 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java @@ -48,9 +48,7 @@ public ResponseEntity updateMyPageInfo( } private ErrorCode determineErrorCode(DataIntegrityViolationException e) { - String message = e.getMostSpecificCause().getMessage().toLowerCase(); - - if (message.contains("unique") && message.contains("nickname")) { + if (e.getMessage().contains("uk_site_user_nickname")) { return ErrorCode.NICKNAME_ALREADY_EXISTED; } diff --git a/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java b/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java index a7230a737..98c18b56a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java +++ b/src/main/java/com/example/solidconnection/siteuser/domain/SiteUser.java @@ -35,6 +35,10 @@ @UniqueConstraint( name = "uk_site_user_email_auth_type", columnNames = {"email", "auth_type"} + ), + @UniqueConstraint( + name = "uk_site_user_nickname", + columnNames = {"nickname"} ) }) public class SiteUser { @@ -51,7 +55,7 @@ public class SiteUser { private AuthType authType; @Setter - @Column(nullable = false, length = 100, unique = true) + @Column(nullable = false, length = 100) private String nickname; @Setter From c8115f3cf8f82295698b174d05d48bcd37e25be9 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 12 Jun 2025 17:18:02 +0900 Subject: [PATCH 06/16] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 게시글_좋아요_동시성_문제를_해결한다: 동일한 닉네임을 사용하지 않도록 변경 - 이메일이_같더라도_인증_유형이_다른_사용자는_정상_저장한다: 동일한 닉네임을 사용하지 않도록 변경 - 닉네임 무결성 테스트는 repository test로 분리 - 새로운_이미지로_성공적으로_업데이트한다: DB에서 사용자 조회 후 URL 비교 --- .../PostLikeCountConcurrencyTest.java | 5 ++- .../solidconnection/e2e/DynamicFixture.java | 4 +- .../repository/SiteUserRepositoryTest.java | 44 ++++++++++++++++--- .../siteuser/service/MyPageServiceTest.java | 14 +----- 4 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java b/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java index 188f717ca..472cb3aca 100644 --- a/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java +++ b/src/test/java/com/example/solidconnection/concurrency/PostLikeCountConcurrencyTest.java @@ -21,7 +21,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static com.example.solidconnection.e2e.DynamicFixture.createSiteUserByEmail; +import static com.example.solidconnection.e2e.DynamicFixture.createSiteUserByEmailAndNickname; import static org.junit.jupiter.api.Assertions.assertEquals; @TestContainerSpringBootTest @@ -92,7 +92,8 @@ private Post createPost(Board board, SiteUser siteUser) { for (int i = 0; i < THREAD_NUMS; i++) { String email = "email" + i; - SiteUser tmpSiteUser = siteUserRepository.save(createSiteUserByEmail(email)); + String nickname = "nickname" + i; + SiteUser tmpSiteUser = siteUserRepository.save(createSiteUserByEmailAndNickname(email, nickname)); executorService.submit(() -> { try { postLikeService.likePost(tmpSiteUser, post.getId()); diff --git a/src/test/java/com/example/solidconnection/e2e/DynamicFixture.java b/src/test/java/com/example/solidconnection/e2e/DynamicFixture.java index 4c90e58dc..5187877d2 100644 --- a/src/test/java/com/example/solidconnection/e2e/DynamicFixture.java +++ b/src/test/java/com/example/solidconnection/e2e/DynamicFixture.java @@ -6,10 +6,10 @@ public class DynamicFixture { // todo: test fixture 개선 작업 이후, 이 클래스의 사용이 대체되면 삭제 필요 - public static SiteUser createSiteUserByEmail(String email) { + public static SiteUser createSiteUserByEmailAndNickname(String email, String nickname) { return new SiteUser( email, - "nickname", + nickname, "profileImage", PreparationStatus.CONSIDERING, Role.MENTEE diff --git a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java index 41806d6cf..34051a797 100644 --- a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java @@ -24,8 +24,8 @@ class 이메일과_인증_유형이_동일한_사용자는_저장할_수_없다 @Test void 이메일과_인증_유형이_동일한_사용자를_저장하면_예외_응답을_반환한다() { // given - SiteUser user1 = createSiteUser("email", AuthType.KAKAO); - SiteUser user2 = createSiteUser("email", AuthType.KAKAO); + SiteUser user1 = createSiteUser("email", "nickname1", AuthType.KAKAO); + SiteUser user2 = createSiteUser("email", "nickname2", AuthType.KAKAO); siteUserRepository.save(user1); // when, then @@ -36,8 +36,8 @@ class 이메일과_인증_유형이_동일한_사용자는_저장할_수_없다 @Test void 이메일이_같더라도_인증_유형이_다른_사용자는_정상_저장한다() { // given - SiteUser user1 = createSiteUser("email", AuthType.KAKAO); - SiteUser user2 = createSiteUser("email", AuthType.APPLE); + SiteUser user1 = createSiteUser("email", "nickname1", AuthType.KAKAO); + SiteUser user2 = createSiteUser("email", "nickname2", AuthType.APPLE); siteUserRepository.save(user1); // when, then @@ -46,10 +46,42 @@ class 이메일과_인증_유형이_동일한_사용자는_저장할_수_없다 } } - private SiteUser createSiteUser(String email, AuthType authType) { + @Nested + class 닉네임은_중복될_수_없다{ + + @Test + void 중복된_닉네임으로_사용자를_저장하면_예외_응답을_반환한다() { + // given + SiteUser user1 = createSiteUser("email1", "nickname", AuthType.KAKAO); + SiteUser user2 = createSiteUser("email2", "nickname", AuthType.KAKAO); + siteUserRepository.save(user1); + + // when, then + assertThatCode(() -> siteUserRepository.save(user2)) + .isInstanceOf(DataIntegrityViolationException.class); + } + + @Test + void 중복된_닉네임으로_변경하면_예외_응답을_반환한다() { + // given + SiteUser user1 = createSiteUser("email1", "nickname1", AuthType.KAKAO); + SiteUser user2 = createSiteUser("email2", "nickname2", AuthType.KAKAO); + siteUserRepository.save(user1); + siteUserRepository.save(user2); + + // when + user2.setNickname("nickname1"); + + // then + assertThatCode(() -> siteUserRepository.saveAndFlush(user2)) + .isInstanceOf(DataIntegrityViolationException.class); + } + } + + private SiteUser createSiteUser(String email, String nickname, AuthType authType) { return new SiteUser( email, - "nickname", + nickname, "profileImageUrl", PreparationStatus.CONSIDERING, Role.MENTEE, diff --git a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java index 1168a0eeb..6c99336ef 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java @@ -118,7 +118,8 @@ class 프로필_이미지_수정_테스트 { myPageService.updateMyPageInfo(user, imageFile, "newNickname"); // then - assertThat(user.getProfileImageUrl()).isEqualTo(expectedUrl); + SiteUser updatedUser = siteUserRepository.findById(user.getId()).get(); + assertThat(updatedUser.getProfileImageUrl()).isEqualTo(expectedUrl); } @Test @@ -175,17 +176,6 @@ void setUp() { assertThat(updatedUser.getNickname()).isEqualTo(newNickname); } - @Test - void 중복된_닉네임으로_변경하면_예외_응답을_반환한다() { - // given - SiteUser existingUser = siteUserFixture.사용자(1, "existing nickname"); - - // when & then - assertThatCode(() -> myPageService.updateMyPageInfo(user, null, existingUser.getNickname())) - .isInstanceOf(CustomException.class) - .hasMessage(NICKNAME_ALREADY_EXISTED.getMessage()); - } - @Test void 최소_대기기간이_지나지_않은_상태에서_변경하면_예외_응답을_반환한다() { // given From afdf86fadae8d49e8469acc88cd90abdac2fcb42 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 10:20:48 +0900 Subject: [PATCH 07/16] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EB=A5=BC=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=EA=B0=80=20=EC=95=84=EB=8B=8C=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EA=B3=84=EC=B8=B5=EC=97=90=EC=84=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/controller/MyPageController.java | 16 ++-------------- .../siteuser/service/MyPageService.java | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java index aed1d0112..ce26fc601 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java @@ -39,19 +39,7 @@ public ResponseEntity updateMyPageInfo( @RequestParam(value = "file", required = false) MultipartFile imageFile, @RequestParam(value = "nickname", required = false) String nickname ) { - try { - myPageService.updateMyPageInfo(siteUser, imageFile, nickname); - return ResponseEntity.ok().build(); - } catch (DataIntegrityViolationException e) { - throw new CustomException(determineErrorCode(e)); - } - } - - private ErrorCode determineErrorCode(DataIntegrityViolationException e) { - if (e.getMessage().contains("uk_site_user_nickname")) { - return ErrorCode.NICKNAME_ALREADY_EXISTED; - } - - return ErrorCode.DATA_INTEGRITY_VIOLATION; + myPageService.updateMyPageInfo(siteUser, imageFile, nickname); + return ResponseEntity.ok().build(); } } diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java index 712280b2e..3d3bee56f 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -1,6 +1,7 @@ package com.example.solidconnection.siteuser.service; import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.s3.domain.ImgType; import com.example.solidconnection.s3.dto.UploadedFileUrlResponse; import com.example.solidconnection.s3.service.S3Service; @@ -11,6 +12,7 @@ import com.example.solidconnection.university.domain.LikedUniversity; import com.example.solidconnection.university.dto.UniversityInfoForApplyPreviewResponse; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -48,7 +50,12 @@ public MyPageResponse getMyPageInfo(SiteUser siteUser) { public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { if (nickname != null) { validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); - siteUserRepository.updateNickname(siteUser.getId(), nickname, LocalDateTime.now()); + + try { + siteUserRepository.updateNickname(siteUser.getId(), nickname, LocalDateTime.now()); + } catch (DataIntegrityViolationException e) { + throw new CustomException(determineErrorCode(e)); + } } if (imageFile != null && !imageFile.isEmpty()) { @@ -77,6 +84,14 @@ private boolean isDefaultProfileImage(String profileImageUrl) { return profileImageUrl == null || !profileImageUrl.startsWith(prefix); } + private ErrorCode determineErrorCode(DataIntegrityViolationException e) { + if (e.getMessage().contains("uk_site_user_nickname")) { + return ErrorCode.NICKNAME_ALREADY_EXISTED; + } + + return ErrorCode.DATA_INTEGRITY_VIOLATION; + } + /* * 관심 대학교 목록을 조회한다. * */ From 074ca5623f00836b22cd6efa35bcc42d0c7fd81c Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 13:45:04 +0900 Subject: [PATCH 08/16] =?UTF-8?q?refactor:=20JPQL=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20dirty=20check=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 중복 닉네임 검증보다 닉네임 수정 시간 검증이 선행되도록 변경 - 준영속 엔티티를 save()하는 과정에서 발생하는 N+1 문제 해결 - DB 레벨, 애플리케이션 레벨 검증 모두 사용하도록 - 위 변경사항에 따른 테스트 코드 수정 --- .../exception/CustomExceptionHandler.java | 12 +++++++ .../siteuser/service/MyPageService.java | 32 +++++++++---------- .../siteuser/service/MyPageServiceTest.java | 5 ++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java b/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java index bc03ca98a..65987c72c 100644 --- a/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java +++ b/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.exc.InvalidFormatException; import io.jsonwebtoken.JwtException; import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -13,6 +14,7 @@ import java.util.ArrayList; import java.util.List; +import static com.example.solidconnection.common.exception.ErrorCode.DATA_INTEGRITY_VIOLATION; import static com.example.solidconnection.common.exception.ErrorCode.INVALID_INPUT; import static com.example.solidconnection.common.exception.ErrorCode.JSON_PARSING_FAILED; import static com.example.solidconnection.common.exception.ErrorCode.JWT_EXCEPTION; @@ -56,6 +58,16 @@ public ResponseEntity handleValidationExceptions(MethodArgumentNo .body(errorResponse); } + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException ex) { + String errorMessage = ex.getMessage(); + log.error("데이터 무결성 제약조건 위반 예외 발생 : {}", errorMessage); + ErrorResponse errorResponse = new ErrorResponse(DATA_INTEGRITY_VIOLATION, errorMessage); + return ResponseEntity + .status(HttpStatus.CONFLICT) + .body(errorResponse); + } + @ExceptionHandler(JwtException.class) public ResponseEntity handleJwtException(JwtException ex) { String errorMessage = ex.getMessage(); diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java index 3d3bee56f..cdc4be794 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -1,7 +1,6 @@ package com.example.solidconnection.siteuser.service; import com.example.solidconnection.common.exception.CustomException; -import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.s3.domain.ImgType; import com.example.solidconnection.s3.dto.UploadedFileUrlResponse; import com.example.solidconnection.s3.service.S3Service; @@ -12,7 +11,6 @@ import com.example.solidconnection.university.domain.LikedUniversity; import com.example.solidconnection.university.dto.UniversityInfoForApplyPreviewResponse; import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -22,6 +20,8 @@ import java.util.List; import static com.example.solidconnection.common.exception.ErrorCode.CAN_NOT_CHANGE_NICKNAME_YET; +import static com.example.solidconnection.common.exception.ErrorCode.NICKNAME_ALREADY_EXISTED; +import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; @RequiredArgsConstructor @Service @@ -48,23 +48,23 @@ public MyPageResponse getMyPageInfo(SiteUser siteUser) { * */ @Transactional public void updateMyPageInfo(SiteUser siteUser, MultipartFile imageFile, String nickname) { - if (nickname != null) { - validateNicknameNotChangedRecently(siteUser.getNicknameModifiedAt()); + SiteUser user = siteUserRepository.findById(siteUser.getId()) + .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); - try { - siteUserRepository.updateNickname(siteUser.getId(), nickname, LocalDateTime.now()); - } catch (DataIntegrityViolationException e) { - throw new CustomException(determineErrorCode(e)); - } + if (nickname != null) { + validateNicknameNotChangedRecently(user.getNicknameModifiedAt()); + validateNicknameUnique(nickname); + user.setNickname(nickname); + user.setNicknameModifiedAt(LocalDateTime.now()); } if (imageFile != null && !imageFile.isEmpty()) { UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(imageFile, ImgType.PROFILE); - if (!isDefaultProfileImage(siteUser.getProfileImageUrl())) { - s3Service.deleteExProfile(siteUser); + if (!isDefaultProfileImage(user.getProfileImageUrl())) { + s3Service.deleteExProfile(user); } String profileImageUrl = uploadedFile.fileUrl(); - siteUserRepository.updateProfileImage(siteUser.getId(), profileImageUrl); + user.setProfileImageUrl(profileImageUrl); } } @@ -84,12 +84,10 @@ private boolean isDefaultProfileImage(String profileImageUrl) { return profileImageUrl == null || !profileImageUrl.startsWith(prefix); } - private ErrorCode determineErrorCode(DataIntegrityViolationException e) { - if (e.getMessage().contains("uk_site_user_nickname")) { - return ErrorCode.NICKNAME_ALREADY_EXISTED; + private void validateNicknameUnique(String nickname) { + if (siteUserRepository.existsByNickname(nickname)) { + throw new CustomException(NICKNAME_ALREADY_EXISTED); } - - return ErrorCode.DATA_INTEGRITY_VIOLATION; } /* diff --git a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java index 6c99336ef..2d7d210b3 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java @@ -34,6 +34,7 @@ import static com.example.solidconnection.siteuser.service.MyPageService.NICKNAME_LAST_CHANGE_DATE_FORMAT; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.BDDMockito.any; import static org.mockito.BDDMockito.eq; import static org.mockito.BDDMockito.given; @@ -148,7 +149,8 @@ class 프로필_이미지_수정_테스트 { myPageService.updateMyPageInfo(커스텀_프로필_사용자, imageFile, "newNickname"); // then - then(s3Service).should().deleteExProfile(커스텀_프로필_사용자); + then(s3Service).should().deleteExProfile(argThat(user -> + user.getId().equals(커스텀_프로필_사용자.getId()))); } } @@ -182,6 +184,7 @@ void setUp() { MockMultipartFile imageFile = createValidImageFile(); LocalDateTime modifiedAt = LocalDateTime.now().minusDays(MIN_DAYS_BETWEEN_NICKNAME_CHANGES - 1); user.setNicknameModifiedAt(modifiedAt); + siteUserRepository.save(user); // when & then assertThatCode(() -> myPageService.updateMyPageInfo(user, imageFile, "nickname12")) From d455281879462891005d39aefd959716ff8acdc9 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 14:00:57 +0900 Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=97=90?= =?UTF-8?q?=EB=A7=8C=20=EC=98=88=EC=99=B8=20=EC=9B=90=EB=AC=B8=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/CustomExceptionHandler.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java b/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java index 65987c72c..57d6f4769 100644 --- a/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java +++ b/src/main/java/com/example/solidconnection/common/exception/CustomExceptionHandler.java @@ -60,11 +60,10 @@ public ResponseEntity handleValidationExceptions(MethodArgumentNo @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException ex) { - String errorMessage = ex.getMessage(); - log.error("데이터 무결성 제약조건 위반 예외 발생 : {}", errorMessage); - ErrorResponse errorResponse = new ErrorResponse(DATA_INTEGRITY_VIOLATION, errorMessage); + log.error("데이터 무결성 제약조건 위반 예외 발생 : {}", ex.getMessage()); + ErrorResponse errorResponse = new ErrorResponse(DATA_INTEGRITY_VIOLATION, "데이터 무결성 제약조건 위반 예외 발생"); return ResponseEntity - .status(HttpStatus.CONFLICT) + .status(DATA_INTEGRITY_VIOLATION.getCode()) .body(errorResponse); } From c88c131f13f6fd4517ce3a7c562ffe9f247ebf35 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 15:55:45 +0900 Subject: [PATCH 10/16] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=95=A0=ED=94=8C=EB=A6=AC=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=A0=88=EB=B2=A8=20=EA=B2=80=EC=A6=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CustomExceptionHandler에 DB 레벨에서 중복 닉네임 검증 추가에 따라 try-catch 문 삭제 --- .../auth/service/SignUpService.java | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java index 5652b5252..f13f11419 100644 --- a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java +++ b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java @@ -51,20 +51,17 @@ public SignInResponse signUp(SignUpRequest signUpRequest) { // 검증 validateSignUpToken(signUpRequest); validateUserNotDuplicated(signUpRequest); + validateNicknameDuplicated(signUpRequest.nickname()); - try { - // 사용자 저장 - SiteUser siteUser = siteUserRepository.save(createSiteUser(signUpRequest)); + // 사용자 저장 + SiteUser siteUser = siteUserRepository.save(createSiteUser(signUpRequest)); - // 관심 지역, 국가 저장 - saveInterestedRegion(signUpRequest, siteUser); - saveInterestedCountry(signUpRequest, siteUser); + // 관심 지역, 국가 저장 + saveInterestedRegion(signUpRequest, siteUser); + saveInterestedCountry(signUpRequest, siteUser); - // 로그인 - return signInService.signIn(siteUser); - } catch (DataIntegrityViolationException e) { - throw new CustomException(determineErrorCode(e)); - } + // 로그인 + return signInService.signIn(siteUser); } private void validateNicknameDuplicated(String nickname) { @@ -89,14 +86,6 @@ private void saveInterestedCountry(SignUpRequest signUpRequest, SiteUser savedSi interestedCountyRepository.saveAll(interestedCountries); } - private ErrorCode determineErrorCode(DataIntegrityViolationException e) { - if (e.getMessage().contains("uk_site_user_nickname")) { - return ErrorCode.NICKNAME_ALREADY_EXISTED; - } - - return ErrorCode.DATA_INTEGRITY_VIOLATION; - } - protected abstract void validateSignUpToken(SignUpRequest signUpRequest); protected abstract void validateUserNotDuplicated(SignUpRequest signUpRequest); From 484bcd302392fcce20b12ba17500c323969874dc Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 16:01:16 +0900 Subject: [PATCH 11/16] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(JPQL=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/repository/SiteUserRepository.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java index 6b11aabb7..58e7c62a9 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java @@ -24,12 +24,4 @@ public interface SiteUserRepository extends JpaRepository { @Query("SELECT u FROM SiteUser u WHERE u.quitedAt <= :cutoffDate") List findUsersToBeRemoved(@Param("cutoffDate") LocalDate cutoffDate); - - @Modifying - @Query("UPDATE SiteUser s SET s.nickname = :nickname, s.nicknameModifiedAt = :modifiedAt WHERE s.id = :id") - void updateNickname(@Param("id") Long id, @Param("nickname") String nickname, @Param("modifiedAt") LocalDateTime modifiedAt); - - @Modifying - @Query("UPDATE SiteUser s SET s.profileImageUrl = :profileImageUrl WHERE s.id = :id") - void updateProfileImage(@Param("id") Long id, @Param("profileImageUrl") String profileImageUrl); } From a2c81ab984659057abfa8873f74146e9a56b52e9 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Sun, 15 Jun 2025 16:01:55 +0900 Subject: [PATCH 12/16] =?UTF-8?q?test:=20=EC=A4=91=EB=B3=B5=EB=90=9C=5F?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=EC=9C=BC=EB=A1=9C=5F=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EB=A5=BC=5F=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=A9=B4=5F=EC=98=88=EC=99=B8=5F=EC=9D=91=EB=8B=B5=EC=9D=84=5F?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=9C=EB=8B=A4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - save -> saveAndFlush --- .../siteuser/repository/SiteUserRepositoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java index 34051a797..a9adc463a 100644 --- a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java @@ -57,7 +57,7 @@ class 닉네임은_중복될_수_없다{ siteUserRepository.save(user1); // when, then - assertThatCode(() -> siteUserRepository.save(user2)) + assertThatCode(() -> siteUserRepository.saveAndFlush(user2)) .isInstanceOf(DataIntegrityViolationException.class); } From 4f7f4f4258eb49f9b08078798677118472be23ff Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 19 Jun 2025 12:18:38 +0900 Subject: [PATCH 13/16] =?UTF-8?q?chore:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20im?= =?UTF-8?q?port=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/solidconnection/auth/service/SignUpService.java | 2 -- .../siteuser/controller/MyPageController.java | 6 +----- .../siteuser/repository/SiteUserRepository.java | 3 +-- .../solidconnection/siteuser/service/MyPageServiceTest.java | 1 - 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java index f13f11419..95bd4ecac 100644 --- a/src/main/java/com/example/solidconnection/auth/service/SignUpService.java +++ b/src/main/java/com/example/solidconnection/auth/service/SignUpService.java @@ -3,7 +3,6 @@ import com.example.solidconnection.auth.dto.SignInResponse; import com.example.solidconnection.auth.dto.SignUpRequest; import com.example.solidconnection.common.exception.CustomException; -import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.location.country.domain.InterestedCountry; import com.example.solidconnection.location.country.repository.CountryRepository; import com.example.solidconnection.location.country.repository.InterestedCountyRepository; @@ -12,7 +11,6 @@ import com.example.solidconnection.location.region.repository.RegionRepository; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.repository.SiteUserRepository; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.transaction.annotation.Transactional; import java.util.List; diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java index ce26fc601..772da0d32 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/MyPageController.java @@ -1,13 +1,11 @@ package com.example.solidconnection.siteuser.controller; -import com.example.solidconnection.common.exception.CustomException; -import com.example.solidconnection.common.exception.ErrorCode; + import com.example.solidconnection.common.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.MyPageResponse; import com.example.solidconnection.siteuser.service.MyPageService; import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -16,8 +14,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import static com.example.solidconnection.common.exception.ErrorCode.DATA_INTEGRITY_VIOLATION; - @RequiredArgsConstructor @RequestMapping("/my") @RestController diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java index 58e7c62a9..51cb410f6 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/SiteUserRepository.java @@ -1,15 +1,14 @@ package com.example.solidconnection.siteuser.repository; + import com.example.solidconnection.siteuser.domain.AuthType; import com.example.solidconnection.siteuser.domain.SiteUser; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; import java.util.Optional; diff --git a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java index 2d7d210b3..a86d80883 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java @@ -29,7 +29,6 @@ import java.util.List; import static com.example.solidconnection.common.exception.ErrorCode.CAN_NOT_CHANGE_NICKNAME_YET; -import static com.example.solidconnection.common.exception.ErrorCode.NICKNAME_ALREADY_EXISTED; import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; import static com.example.solidconnection.siteuser.service.MyPageService.NICKNAME_LAST_CHANGE_DATE_FORMAT; import static org.assertj.core.api.Assertions.assertThat; From b4c9f9818239f1fae36cc0dda74046f16af30b4a Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 19 Jun 2025 12:21:00 +0900 Subject: [PATCH 14/16] =?UTF-8?q?chore:=20=ED=95=A8=EC=88=98=EB=AA=85?= =?UTF-8?q?=EA=B3=BC=20=EC=A4=91=EA=B4=84=ED=98=B8=20=EC=82=AC=EC=9D=B4=20?= =?UTF-8?q?=EB=9D=84=EC=96=B4=EC=93=B0=EA=B8=B0=20=EC=B6=94=EA=B0=80=20(?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EA=B4=80=EB=A0=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/repository/SiteUserRepositoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java index a9adc463a..b8c51c148 100644 --- a/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/repository/SiteUserRepositoryTest.java @@ -47,7 +47,7 @@ class 이메일과_인증_유형이_동일한_사용자는_저장할_수_없다 } @Nested - class 닉네임은_중복될_수_없다{ + class 닉네임은_중복될_수_없다 { @Test void 중복된_닉네임으로_사용자를_저장하면_예외_응답을_반환한다() { From 16e55f78d6a1844bde4185a3631836c23bafdb4a Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Thu, 19 Jun 2025 12:22:36 +0900 Subject: [PATCH 15/16] =?UTF-8?q?chore:=20=ED=98=B8=EC=B6=9C=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=84=A0=EC=96=B8=20=EC=9E=AC=EB=B0=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/service/MyPageService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java index cdc4be794..f5b463c0a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/MyPageService.java @@ -79,17 +79,17 @@ private void validateNicknameNotChangedRecently(LocalDateTime lastModifiedAt) { } } - private boolean isDefaultProfileImage(String profileImageUrl) { - String prefix = "profile/"; - return profileImageUrl == null || !profileImageUrl.startsWith(prefix); - } - private void validateNicknameUnique(String nickname) { if (siteUserRepository.existsByNickname(nickname)) { throw new CustomException(NICKNAME_ALREADY_EXISTED); } } + private boolean isDefaultProfileImage(String profileImageUrl) { + String prefix = "profile/"; + return profileImageUrl == null || !profileImageUrl.startsWith(prefix); + } + /* * 관심 대학교 목록을 조회한다. * */ From 62656848516c383da5d50461344a9da791266aa8 Mon Sep 17 00:00:00 2001 From: seonghyeok Date: Tue, 24 Jun 2025 16:49:38 +0900 Subject: [PATCH 16/16] =?UTF-8?q?chore:=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=ED=8C=8C=EC=9D=BC=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=88=98=EC=A0=95=20V13=20->=20V14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...to_nickname.sql => V14__set_unique_constraint_to_nickname.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V13__set_unique_constraint_to_nickname.sql => V14__set_unique_constraint_to_nickname.sql} (100%) diff --git a/src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql b/src/main/resources/db/migration/V14__set_unique_constraint_to_nickname.sql similarity index 100% rename from src/main/resources/db/migration/V13__set_unique_constraint_to_nickname.sql rename to src/main/resources/db/migration/V14__set_unique_constraint_to_nickname.sql