diff --git a/clokey-api/src/main/java/org/clokey/domain/member/controller/MemberController.java b/clokey-api/src/main/java/org/clokey/domain/member/controller/MemberController.java index 32226907..9918fdfd 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/controller/MemberController.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/controller/MemberController.java @@ -6,7 +6,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.clokey.code.GlobalBaseSuccessCode; -import org.clokey.domain.member.dto.request.DuplicatedIdCheckRequest; +import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest; import org.clokey.domain.member.dto.request.ProfileUpdateRequest; import org.clokey.domain.member.dto.response.*; import org.clokey.domain.member.service.MemberService; @@ -42,7 +42,7 @@ public BaseResponse updateProfile(@Valid @RequestBody ProfileUpdateRequest summary = "닉네임 중복확인", description = "닉네임 중복을 확인합니다.") public BaseResponse checkDuplicateNickname( - @Valid @RequestBody DuplicatedIdCheckRequest request) { + @Valid @RequestBody DuplicatedNicknameCheckRequest request) { DuplicatedIdCheckResponse response = memberService.checkDuplicateNickname(request); return BaseResponse.onSuccess(GlobalBaseSuccessCode.OK, response); diff --git a/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedIdCheckRequest.java b/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedIdCheckRequest.java deleted file mode 100644 index 5760cb71..00000000 --- a/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedIdCheckRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.clokey.domain.member.dto.request; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; - -public record DuplicatedIdCheckRequest( - @NotNull(message = "닉네임은 비워둘 수 없습니다.") - @Pattern( - regexp = "^[a-z0-9._]+$", - message = "닉네임은 영어 소문자, 숫자, 언더바(_), 점(.)만 허용됩니다.") - @Schema(description = "중복을 확인할 닉네임", example = "clokey11") - String nickname) {} diff --git a/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedNicknameCheckRequest.java b/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedNicknameCheckRequest.java new file mode 100644 index 00000000..5c6f3a7d --- /dev/null +++ b/clokey-api/src/main/java/org/clokey/domain/member/dto/request/DuplicatedNicknameCheckRequest.java @@ -0,0 +1,15 @@ +package org.clokey.domain.member.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +public record DuplicatedNicknameCheckRequest( + @NotBlank(message = "닉네임은 비워둘 수 없습니다.") + @Size(max = 20, message = "닉네임은 20자 이하여야 합니다.") + @Pattern( + regexp = "^[a-z가-힣._]+$", + message = "닉네임은 영어 소문자, 한글, 언더바(_), 점(.)만 허용됩니다.") + @Schema(description = "중복을 확인할 닉네임", example = "clokey.홍길동") + String nickname) {} diff --git a/clokey-api/src/main/java/org/clokey/domain/member/service/MemberService.java b/clokey-api/src/main/java/org/clokey/domain/member/service/MemberService.java index ed55ddab..2d57148b 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/service/MemberService.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/service/MemberService.java @@ -1,6 +1,6 @@ package org.clokey.domain.member.service; -import org.clokey.domain.member.dto.request.DuplicatedIdCheckRequest; +import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest; import org.clokey.domain.member.dto.request.ProfileUpdateRequest; import org.clokey.domain.member.dto.response.*; import org.clokey.global.paging.SortDirection; @@ -10,7 +10,7 @@ public interface MemberService { void updateProfile(ProfileUpdateRequest request); - DuplicatedIdCheckResponse checkDuplicateNickname(DuplicatedIdCheckRequest request); + DuplicatedIdCheckResponse checkDuplicateNickname(DuplicatedNicknameCheckRequest request); void toggleFollow(Long userId); diff --git a/clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java b/clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java index 1fa39c04..27825525 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java @@ -4,7 +4,7 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; import org.clokey.domain.history.repository.HistoryRepository; -import org.clokey.domain.member.dto.request.DuplicatedIdCheckRequest; +import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest; import org.clokey.domain.member.dto.request.ProfileUpdateRequest; import org.clokey.domain.member.dto.response.*; import org.clokey.domain.member.event.NewFollowerEvent; @@ -70,7 +70,8 @@ public void updateProfile(ProfileUpdateRequest request) { } @Override - public DuplicatedIdCheckResponse checkDuplicateNickname(DuplicatedIdCheckRequest request) { + public DuplicatedIdCheckResponse checkDuplicateNickname( + DuplicatedNicknameCheckRequest request) { final Member currentMember = memberUtil.getCurrentMember(); boolean duplicated = diff --git a/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java b/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java index fd34a6e3..4df72cc4 100644 --- a/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java +++ b/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java @@ -1,5 +1,6 @@ package org.clokey.domain.member.controller; +import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; @@ -9,7 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; -import org.clokey.domain.member.dto.request.DuplicatedIdCheckRequest; +import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest; import org.clokey.domain.member.dto.request.ProfileUpdateRequest; import org.clokey.domain.member.dto.response.*; import org.clokey.domain.member.service.MemberService; @@ -94,7 +95,9 @@ class 프로필_수정_요청_시 { .andExpect(jsonPath("$.isSuccess").value(false)) .andExpect(jsonPath("$.code").value("COMMON400")) .andExpect(jsonPath("$.message").value("잘못된 요청입니다.")) - .andExpect(jsonPath("$.result.nickname").value("닉네임은 비워둘 수 없습니다.")); + .andExpect( + jsonPath("$.result.nickname") + .value(containsString("닉네임은 비워둘 수 없습니다."))); } @ParameterizedTest @@ -121,7 +124,9 @@ class 프로필_수정_요청_시 { .andExpect(jsonPath("$.isSuccess").value(false)) .andExpect(jsonPath("$.code").value("COMMON400")) .andExpect(jsonPath("$.message").value("잘못된 요청입니다.")) - .andExpect(jsonPath("$.result.nickname").value("닉네임은 비워둘 수 없습니다.")); + .andExpect( + jsonPath("$.result.nickname") + .value(containsString("닉네임은 비워둘 수 없습니다."))); } @Test @@ -157,7 +162,8 @@ class 아이디_중복확인_요청_시 { @Test void 유효한_요청이면_중복_여부를_반환한다() throws Exception { // given - DuplicatedIdCheckRequest request = new DuplicatedIdCheckRequest("test_clokey_id"); + DuplicatedNicknameCheckRequest request = + new DuplicatedNicknameCheckRequest("clokey.홍길동"); DuplicatedIdCheckResponse response = new DuplicatedIdCheckResponse(true); given(memberService.checkDuplicateNickname(request)).willReturn(response); @@ -180,7 +186,7 @@ class 아이디_중복확인_요청_시 { @Test void 닉네임이_null이면_예외가_발생한다() throws Exception { // given - DuplicatedIdCheckRequest request = new DuplicatedIdCheckRequest(null); + DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(null); // when ResultActions perform = @@ -197,12 +203,37 @@ class 아이디_중복확인_요청_시 { .andExpect(jsonPath("$.result.nickname").value("닉네임은 비워둘 수 없습니다.")); } - // 허용 종류 : 영문(소문자) , 숫자, 언더바(_), 점(.) @ParameterizedTest - @ValueSource(strings = {"clokey clokey", "CLOKEY", "클로키", "clokey-user", "clokey,,user^^"}) + @NullAndEmptySource + @ValueSource(strings = {" "}) + void 닉네임이_비어있으면_예외가_발생한다(String nickname) throws Exception { + // given + DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname); + + // when + ResultActions perform = + mockMvc.perform( + post("/users/check-duplicate-nickname") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + // then + perform.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.isSuccess").value(false)) + .andExpect(jsonPath("$.code").value("COMMON400")) + .andExpect(jsonPath("$.message").value("잘못된 요청입니다.")) + .andExpect( + jsonPath("$.result.nickname") + .value(containsString("닉네임은 비워둘 수 없습니다."))); + } + + // 허용 종류 : 영어 소문자, 한글, 언더바(_), 점(.) + @ParameterizedTest + @ValueSource( + strings = {"clokey clokey", "CLOKEY", "clokey-user", "clokey,,user^^", "clokey1"}) void 닉네임_제약조건을_위배하면_예외가_발생한다(String nickname) throws Exception { // given - DuplicatedIdCheckRequest request = new DuplicatedIdCheckRequest(nickname); + DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname); // when ResultActions perform = @@ -218,7 +249,51 @@ class 아이디_중복확인_요청_시 { .andExpect(jsonPath("$.message").value("잘못된 요청입니다.")) .andExpect( jsonPath("$.result.nickname") - .value("닉네임은 영어 소문자, 숫자, 언더바(_), 점(.)만 허용됩니다.")); + .value("닉네임은 영어 소문자, 한글, 언더바(_), 점(.)만 허용됩니다.")); + } + + @Test + void 닉네임이_20자를_초과하면_예외가_발생한다() throws Exception { + // given + DuplicatedNicknameCheckRequest request = + new DuplicatedNicknameCheckRequest("abcdefghijklmnopqrstu"); + + // when + ResultActions perform = + mockMvc.perform( + post("/users/check-duplicate-nickname") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + // then + perform.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.isSuccess").value(false)) + .andExpect(jsonPath("$.code").value("COMMON400")) + .andExpect(jsonPath("$.message").value("잘못된 요청입니다.")) + .andExpect(jsonPath("$.result.nickname").value("닉네임은 20자 이하여야 합니다.")); + } + + @ParameterizedTest + @ValueSource(strings = {"clokey", "홍길동", "clokey.홍길동", "abc_def"}) + void 닉네임_제약조건을_만족하면_중복_여부를_반환한다(String nickname) throws Exception { + // given + DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname); + DuplicatedIdCheckResponse response = new DuplicatedIdCheckResponse(false); + given(memberService.checkDuplicateNickname(request)).willReturn(response); + + // when + ResultActions perform = + mockMvc.perform( + post("/users/check-duplicate-nickname") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + // then + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$.isSuccess").value(true)) + .andExpect(jsonPath("$.code").value("COMMON200")) + .andExpect(jsonPath("$.message").value("성공입니다.")) + .andExpect(jsonPath("$.result.duplicated").value(false)); } } diff --git a/clokey-api/src/test/java/org/clokey/domain/member/service/MemberServiceTest.java b/clokey-api/src/test/java/org/clokey/domain/member/service/MemberServiceTest.java index c6f22465..5fdedf9e 100644 --- a/clokey-api/src/test/java/org/clokey/domain/member/service/MemberServiceTest.java +++ b/clokey-api/src/test/java/org/clokey/domain/member/service/MemberServiceTest.java @@ -7,7 +7,7 @@ import java.util.Optional; import org.clokey.IntegrationTest; import org.clokey.TransactionUtil; -import org.clokey.domain.member.dto.request.DuplicatedIdCheckRequest; +import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest; import org.clokey.domain.member.dto.request.ProfileUpdateRequest; import org.clokey.domain.member.dto.response.BlockedMemberResponse; import org.clokey.domain.member.dto.response.FollowMemberResponse; @@ -135,7 +135,7 @@ void setUp() { @ValueSource(strings = {"testNickname1", "distinctId1", "distinctId2"}) void 현재_닉네임_또는_중복되지_않는_닉네임을_입력하면_false를_반환한다(String nickname) { // given - DuplicatedIdCheckRequest request = new DuplicatedIdCheckRequest(nickname); + DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname); // when& then assertThat(memberService.checkDuplicateNickname(request).duplicated()).isFalse(); @@ -144,7 +144,8 @@ void setUp() { @Test void 중복되는_닉네임을_입력한_경우_true를_반환한다() { // given - DuplicatedIdCheckRequest request = new DuplicatedIdCheckRequest("testNickname2"); + DuplicatedNicknameCheckRequest request = + new DuplicatedNicknameCheckRequest("testNickname2"); // when& then assertThat(memberService.checkDuplicateNickname(request).duplicated()).isTrue();