Skip to content

Commit

Permalink
Api: ✏️ 사용자 아이디, 전화번호 수정 API 분리 (#127)
Browse files Browse the repository at this point in the history
* fix: 배포 파이프라인 이미지 빌드 버전 추가

* fix: update_username_and_phone -> update_phone

* fix: 회원가입 dto name, username 정규 표현식 수정

* fix: 사용자 프로필 수정 dto name, username 정규식 수정 및 username_phone_req -> phone_req

* fix: usecase update_username_and_phone -> update_phone

* fix: patch_profile -> patch_phone && 요청 메서드와 함수 이름 불일치하는 메서드 수정

* docs: swagger 문서 수정

* test: 기존 프로필 수정 테스트 변경

* fix: 사용자 아이디 변경 시, 중복 검사 추가

* docs: swagger에 사용자 아이디 중복 검사 예외 응답 추가

* test: 사용자 이름, 아이디 유효성 검사 테스트 예외 메시지 기대값 수정
  • Loading branch information
psychology50 authored Jul 13, 2024
1 parent 00e7450 commit 634f50f
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ public User toUser() {
public record General(
@Schema(description = "아이디", example = "pennyway")
@NotBlank(message = "아이디를 입력해주세요")
@Pattern(regexp = "^[a-z-_.]{5,20}$", message = "5~20자의 영문 소문자, -, _, .사용 가능합니다.")
@Pattern(regexp = "^[a-z0-9-_.]{5,20}$", message = "영문 소문자, 숫자, 특수기호 (-), (_), (.)사용하여, 5~20자의 아이디를 입력해 주세요")
String username,
@Schema(description = "이름", example = "페니웨이")
@NotBlank(message = "이름을 입력해주세요")
@Pattern(regexp = "^[가-힣a-z0-9]{2,8}$", message = "2~8자의 한글, 영문 소문자, 숫자만 사용 가능합니다.")
@Pattern(regexp = "^[가-힣a-zA-Z]{2,8}$", message = "한글과 영문 대, 소문자만 가능해요")
String name,
@Schema(description = "비밀번호", example = "pennyway1234")
@NotBlank(message = "비밀번호를 입력해주세요")
Expand Down Expand Up @@ -115,14 +115,14 @@ public record Oauth(
@Schema(description = "OIDC nonce")
@NotBlank(message = "OIDC nonce는 필수 입력값입니다.")
String nonce,
@Schema(description = "이름", example = "페니웨이")
@NotBlank(message = "이름을 입력해주세요")
@Pattern(regexp = "^[가-힣a-z]{2,8}$", message = "2~8자의 한글, 영문 소문자만 사용 가능합니다.")
String name,
@Schema(description = "아이디", example = "pennyway")
@NotBlank(message = "아이디를 입력해주세요")
@Pattern(regexp = "^[a-z-_.]{5,20}$", message = "5~20자의 영문 소문자, -, _, .사용 가능합니다.")
@Pattern(regexp = "^[a-z0-9-_.]{5,20}$", message = "영문 소문자, 숫자, 특수기호 (-), (_), (.)사용하여, 5~20자의 아이디를 입력해 주세요")
String username,
@Schema(description = "이름", example = "페니웨이")
@NotBlank(message = "이름을 입력해주세요")
@Pattern(regexp = "^[가-힣a-zA-Z]{2,8}$", message = "한글과 영문 대, 소문자만 가능해요")
String name,
@Schema(description = "전화번호", example = "010-1234-5678")
@NotBlank(message = "전화번호를 입력해주세요")
@Pattern(regexp = "^01[01]-\\d{4}-\\d{4}$", message = "전화번호 형식이 올바르지 않습니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,18 @@ public interface UserAccountApi {
ResponseEntity<?> getMyAccount(@AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "사용자 이름 수정")
ResponseEntity<?> putName(@RequestBody @Validated UserProfileUpdateDto.NameReq request, @AuthenticationPrincipal SecurityUserDetails user);
ResponseEntity<?> patchName(@RequestBody @Validated UserProfileUpdateDto.NameReq request, @AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "사용자 아이디 수정")
ResponseEntity<?> putUsername(@RequestBody @Validated UserProfileUpdateDto.UsernameReq request, @AuthenticationPrincipal SecurityUserDetails user);
@ApiResponse(responseCode = "409", content = @Content(mediaType = "application/json", examples = {
@ExampleObject(name = "검증 실패 - 이미 존재하는 아이디", description = "현재 사용하는 아이디로 요청해도 동일한 예외가 발생한다.", value = """
{
"code": "4091",
"message": "이미 존재하는 아이디입니다."
}
""")
}))
ResponseEntity<?> patchUsername(@RequestBody @Validated UserProfileUpdateDto.UsernameReq request, @AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "사용자 비밀번호 검증")
@ApiResponses({
Expand Down Expand Up @@ -117,7 +125,7 @@ ResponseEntity<?> postPasswordVerification(@RequestBody @Validated UserProfileUp
})
ResponseEntity<?> patchPassword(@RequestBody @Validated UserProfileUpdateDto.PasswordReq request, @AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "사용자 프로필 수정")
@Operation(summary = "사용자 전화번호 수정")
@ApiResponses({
@ApiResponse(responseCode = "401", content = @Content(mediaType = "application/json", examples = {
@ExampleObject(name = "검증 실패", value = """
Expand All @@ -136,21 +144,15 @@ ResponseEntity<?> postPasswordVerification(@RequestBody @Validated UserProfileUp
""")
})),
@ApiResponse(responseCode = "409", content = @Content(mediaType = "application/json", examples = {
@ExampleObject(name = "검증 실패 - 이미 존재하는 아이디", value = """
{
"code": "4091",
"message": "이미 존재하는 아이디입니다."
}
"""),
@ExampleObject(name = "검증 실패 - 이미 존재하는 휴대폰 번호", value = """
@ExampleObject(name = "검증 실패 - 이미 존재하는 휴대폰 번호", description = "현재 사용하는 전화번호로 요청해도 동일한 예외가 발생한다.", value = """
{
"code": "4091",
"message": "이미 존재하는 휴대폰 번호입니다."
}
""")
}))
})
ResponseEntity<?> patchProfile(@RequestBody @Validated UserProfileUpdateDto.UsernameAndPhoneReq request, @AuthenticationPrincipal SecurityUserDetails user);
ResponseEntity<?> patchPhone(@RequestBody @Validated UserProfileUpdateDto.PhoneReq request, @AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "사용자 알림 활성화")
@Parameter(name = "type", description = "알림 타입", examples = {
Expand Down Expand Up @@ -254,5 +256,5 @@ ResponseEntity<?> postPasswordVerification(@RequestBody @Validated UserProfileUp
""")
}))
})
ResponseEntity<?> postProfileImage(@RequestBody @Validated UserProfileUpdateDto.ProfileImageReq request, @AuthenticationPrincipal SecurityUserDetails user);
ResponseEntity<?> putProfileImage(@RequestBody @Validated UserProfileUpdateDto.ProfileImageReq request, @AuthenticationPrincipal SecurityUserDetails user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ public ResponseEntity<?> getMyAccount(@AuthenticationPrincipal SecurityUserDetai
@Override
@PatchMapping("/name")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> putName(UserProfileUpdateDto.NameReq request, SecurityUserDetails user) {
public ResponseEntity<?> patchName(UserProfileUpdateDto.NameReq request, SecurityUserDetails user) {
userAccountUseCase.updateName(user.getUserId(), request.name());
return ResponseEntity.ok(SuccessResponse.noContent());
}

@Override
@PatchMapping("/username")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> putUsername(UserProfileUpdateDto.UsernameReq request, SecurityUserDetails user) {
public ResponseEntity<?> patchUsername(UserProfileUpdateDto.UsernameReq request, SecurityUserDetails user) {
userAccountUseCase.updateUsername(user.getUserId(), request.username());
return ResponseEntity.ok(SuccessResponse.noContent());
}
Expand All @@ -78,10 +78,10 @@ public ResponseEntity<?> patchPassword(UserProfileUpdateDto.PasswordReq request,
}

@Override
@PatchMapping("/profile")
@PatchMapping("/phone")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> patchProfile(@RequestBody @Validated UserProfileUpdateDto.UsernameAndPhoneReq request, @AuthenticationPrincipal SecurityUserDetails user) {
userAccountUseCase.updateUsernameAndPhone(user.getUserId(), request);
public ResponseEntity<?> patchPhone(@RequestBody @Validated UserProfileUpdateDto.PhoneReq request, @AuthenticationPrincipal SecurityUserDetails user) {
userAccountUseCase.updatePhone(user.getUserId(), request);
return ResponseEntity.ok(SuccessResponse.noContent());
}

Expand Down Expand Up @@ -110,7 +110,7 @@ public ResponseEntity<?> deleteAccount(@AuthenticationPrincipal SecurityUserDeta
@Override
@PutMapping("/profile-image")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> postProfileImage(@Validated UserProfileUpdateDto.ProfileImageReq request, SecurityUserDetails user) {
public ResponseEntity<?> putProfileImage(@Validated UserProfileUpdateDto.ProfileImageReq request, SecurityUserDetails user) {
userAccountUseCase.updateProfileImage(user.getUserId(), request);
return ResponseEntity.ok(SuccessResponse.noContent());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class UserProfileUpdateDto {
public record NameReq(
@Schema(description = "이름", example = "페니웨이")
@NotBlank(message = "이름을 입력해주세요")
@Pattern(regexp = "^[가-힣a-z0-9]{2,8}$", message = "2~8자의 한글, 영문 소문자, 숫자만 사용 가능합니다.")
@Pattern(regexp = "^[가-힣a-zA-Z]{2,8}$", message = "한글과 영문 대, 소문자만 가능해요")
String name
) {
}
Expand All @@ -20,7 +20,7 @@ public record NameReq(
public record UsernameReq(
@Schema(description = "아이디", example = "pennyway")
@NotBlank(message = "아이디를 입력해주세요")
@Pattern(regexp = "^[a-z-_.]{5,20}$", message = "5~20자의 영문 소문자, -, _, .사용 가능합니다.")
@Pattern(regexp = "^[a-z0-9-_.]{5,20}$", message = "영문 소문자, 숫자, 특수기호 (-), (_), (.)사용하여, 5~20자의 아이디를 입력해 주세요")
String username
) {
}
Expand Down Expand Up @@ -69,11 +69,7 @@ public record ProfileImageReq(
}

@Schema(title = "사용자 아이디, 전화번호 변경 DTO")
public record UsernameAndPhoneReq(
@Schema(description = "변경할 아이디", example = "pennyway")
@NotBlank(message = "아이디를 입력해주세요")
@Pattern(regexp = "^[a-z-_.]{5,20}$", message = "5~20자의 영문 소문자, -, _, . 만 사용 가능합니다.")
String username,
public record PhoneReq(
@Schema(description = "전화번호", example = "010-2629-4624")
@NotBlank(message = "전화번호는 필수입니다.")
@Pattern(regexp = "^01[01]-\\d{4}-\\d{4}$", message = "전화번호 형식이 올바르지 않습니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ public void updateName(Long userId, String newName) {
public void updateUsername(Long userId, String newUsername) {
User user = readUserOrThrow(userId);

if (userService.isExistUsername(newUsername)) {
throw new UserErrorException(UserErrorCode.ALREADY_EXIST_USERNAME);
}

user.updateUsername(newUsername);
}

Expand All @@ -61,27 +65,17 @@ public void updateProfileImage(Long userId, String profileImageUrl) {
}

@Transactional
public void updateUsernameAndPhone(Long userId, String username, String phone, String code) {
public void updatePhone(Long userId, String phone, String code) {
User user = readUserOrThrow(userId);

if (!user.getUsername().equals(username)) {
if (userService.isExistUsername(username)) {
throw new UserErrorException(UserErrorCode.ALREADY_EXIST_USERNAME);
}
phoneVerificationService.isValidCode(PhoneVerificationDto.VerifyCodeReq.of(phone, code), PhoneCodeKeyType.PHONE);
phoneCodeService.delete(phone, PhoneCodeKeyType.PHONE);

user.updateUsername(username);
if (userService.isExistPhone(phone)) {
throw new UserErrorException(UserErrorCode.ALREADY_EXIST_PHONE);
}

if (!user.getPhone().equals(phone)) {
phoneVerificationService.isValidCode(PhoneVerificationDto.VerifyCodeReq.of(phone, code), PhoneCodeKeyType.PHONE);
phoneCodeService.delete(phone, PhoneCodeKeyType.PHONE);

if (userService.isExistPhone(phone)) {
throw new UserErrorException(UserErrorCode.ALREADY_EXIST_PHONE);
}

user.updatePhone(phone);
}
user.updatePhone(phone);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public void updateProfileImage(Long userId, UserProfileUpdateDto.ProfileImageReq
userProfileUpdateService.updateProfileImage(userId, request.profileImageUrl());
}

public void updateUsernameAndPhone(Long userId, UserProfileUpdateDto.UsernameAndPhoneReq request) {
userProfileUpdateService.updateUsernameAndPhone(userId, request.username(), request.phone(), request.code());
public void updatePhone(Long userId, UserProfileUpdateDto.PhoneReq request) {
userProfileUpdateService.updatePhone(userId, request.phone(), request.code());
}

public UserProfileUpdateDto.NotifySettingUpdateRes activateNotification(Long userId, NotifySetting.NotifyType type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void requiredInputError() throws Exception {
.andDo(print());
}

@DisplayName("[2] 아이디는 5~20자의 영문 소문자, -, _, .사용 가능합니다.")
@DisplayName("[2] 아이디는 영문 소문자, 숫자, 특수기호 (-), (_), (.)사용하여, 5~20자의 아이디를 입력해 주세요")
@Test
void idValidError() throws Exception {
// given
Expand All @@ -97,7 +97,7 @@ void idValidError() throws Exception {
// then
resultActions
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.fieldErrors.username").value("5~20자의 영문 소문자, -, _, .사용 가능합니다."))
.andExpect(jsonPath("$.fieldErrors.username").value("영문 소문자, 숫자, 특수기호 (-), (_), (.)사용하여, 5~20자의 아이디를 입력해 주세요"))
.andDo(print());
}

Expand All @@ -118,7 +118,7 @@ void nameValidError() throws Exception {
// then
resultActions
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.fieldErrors.name").value("2~8자의 한글, 영문 소문자, 숫자만 사용 가능합니다."))
.andExpect(jsonPath("$.fieldErrors.name").value("한글과 영문 대, 소문자만 가능해요"))
.andDo(print());
}

Expand Down Expand Up @@ -213,7 +213,7 @@ void someFieldMissingError() throws Exception {
@Test
void signUp() throws Exception {
// given
SignUpReq.General request = new SignUpReq.General("pennyway", "페니웨이123", "pennyway1234",
SignUpReq.General request = new SignUpReq.General("pennyway123", "페니웨이", "pennyway1234",
"010-1234-5678", "123456");
ResponseCookie expectedCookie = ResponseCookie.from("refreshToken", "refreshToken")
.maxAge(Duration.ofDays(7).toSeconds()).httpOnly(true).path("/").build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

@Slf4j
@ExtendWith(MockitoExtension.class)
public class UserProfileUpdateServiceTest {
public class PhoneUpdateServiceTest {
private final Long userId = 1L;
private User user = UserFixture.GENERAL_USER.toUser();
@InjectMocks
Expand All @@ -50,33 +50,7 @@ void setUp() {
}

@Test
@DisplayName("수정 요청한 아이디와 전화번호가 기존 정보와 일치할 경우, 변경이 발생하지 않는다.")
void updateSameUsermameAndPhone() {
// when
userProfileUpdateService.updateUsernameAndPhone(userId, user.getUsername(), user.getPhone(), "000000");

// then
verifyNoInteractions(awsS3Provider, phoneVerificationService, phoneCodeService);
}

@Test
@DisplayName("수정 요청한 아이디만 기존 정보와 다를 경우, 아이디만 변경이 발생한다.")
void updateDifferentUsername() {
// given
String newUsername = "newUsername";
String expectedPhone = user.getPhone();

// when
userProfileUpdateService.updateUsernameAndPhone(userId, newUsername, user.getPhone(), "000000");

// then
assertEquals(newUsername, user.getUsername());
assertEquals(expectedPhone, user.getPhone());
verifyNoInteractions(awsS3Provider, phoneVerificationService, phoneCodeService);
}

@Test
@DisplayName("수정 요청한 전화번호만 기존 정보와 다를 경우, 전화번호만 변경이 발생한다.")
@DisplayName("수정 요청한 전화번호가 DB에 존재하지 않고, 유효한 인증 코드를 가진 경우 수정에 성공한다.")
void updateDifferentPhone() {
// given
String expectedUsername = user.getUsername();
Expand All @@ -85,32 +59,14 @@ void updateDifferentPhone() {
willDoNothing().given(phoneCodeService).delete(newPhone, PhoneCodeKeyType.PHONE);

// when
userProfileUpdateService.updateUsernameAndPhone(userId, user.getUsername(), newPhone, "000000");
userProfileUpdateService.updatePhone(userId, newPhone, "000000");

// then
assertEquals(expectedUsername, user.getUsername());
assertEquals(newPhone, user.getPhone());
verifyNoInteractions(awsS3Provider);
}

@Test
@DisplayName("수정 요청한 아이디가 이미 존재하면, ALREADY_EXIST_USERNAME 에러를 반환한다.")
void updateAlreadyExistUsername() {
// given
String newUsername = "newUsername";
given(userService.isExistUsername(newUsername)).willReturn(true);

// when
UserErrorException exception = assertThrows(UserErrorException.class, () -> userProfileUpdateService.updateUsernameAndPhone(userId, newUsername, user.getPhone(), "000000"));

// then
assertEquals(UserErrorCode.ALREADY_EXIST_USERNAME, exception.getBaseErrorCode());
verifyNoInteractions(awsS3Provider, phoneVerificationService, phoneCodeService);
}

/**
* 트랜잭션이 활성화되지 않아서, username 변경이 되지 않음을 확인할 수는 없다.
*/
@Test
@DisplayName("수정 요청한 전화번호가 이미 존재하면, ALREADY_EXIST_PHONE 에러를 반환한다.")
void updateAlreadyExistPhone() {
Expand All @@ -121,7 +77,7 @@ void updateAlreadyExistPhone() {
willDoNothing().given(phoneCodeService).delete(newPhone, PhoneCodeKeyType.PHONE);

// when
UserErrorException exception = assertThrows(UserErrorException.class, () -> userProfileUpdateService.updateUsernameAndPhone(userId, user.getUsername(), newPhone, "000000"));
UserErrorException exception = assertThrows(UserErrorException.class, () -> userProfileUpdateService.updatePhone(userId, newPhone, "000000"));

// then
assertEquals(UserErrorCode.ALREADY_EXIST_PHONE, exception.getBaseErrorCode());
Expand Down

0 comments on commit 634f50f

Please sign in to comment.