Skip to content

Commit

Permalink
feat: #23 전화번호로 유저 이메일 찾기 API 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Wo-ogie committed Feb 22, 2024
1 parent 9c911a1 commit 1ee7a4e
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ public class SecurityConfig {
};

private static final Map<String, HttpMethod> AUTH_WHITE_LIST = Map.of(
"/v*/auth/login", POST,
"/v*/auth/kakao/login", POST,
"/v*/users", POST,
"/v*/users/existence", GET,
"/v*/auth/login", POST,
"/v*/auth/kakao/login", POST
"/v*/users/email", GET
);

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum CustomExceptionType {
USER_PHONE_DUPLICATION(2203, "이미 사용 중인 전화번호입니다."),
USER_KAKAO_UID_DUPLICATION(2204, "이미 가입한 계정입니다."),
USER_NOT_FOUND_BY_KAKAO_UID(2205, "일치하는 회원을 찾을 수 없습니다."),
USER_NOT_FOUND_BY_PHONE(2206, "일치하는 회원을 찾을 수 없습니다."),

KAKAO_CLIENT(10000, "카카오 서버와의 통신 중 오류가 발생했습니다."),
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ajou.hertz.common.validator.PhoneNumber;
import com.ajou.hertz.domain.user.dto.UserDto;
import com.ajou.hertz.domain.user.dto.request.SignUpRequest;
import com.ajou.hertz.domain.user.dto.response.UserEmailResponse;
import com.ajou.hertz.domain.user.dto.response.UserExistenceResponse;
import com.ajou.hertz.domain.user.dto.response.UserResponse;
import com.ajou.hertz.domain.user.service.UserCommandService;
Expand All @@ -28,6 +30,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;

@Tag(name = "유저 관련 API")
Expand Down Expand Up @@ -55,6 +58,25 @@ public UserExistenceResponse getExistenceOfUserByEmailV1_1(
return new UserExistenceResponse(existence);
}

@Operation(
summary = "전화번호로 유저 이메일 찾기",
description = "특정 유저를 식별할 수 있는 정보(전화번호)를 받아 일치하는 유저의 이메일을 조회합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "404", description = "[2206] 전화번호에 해당하는 유저를 찾을 수 없는 경우", content = @Content)
})
@GetMapping(value = "/email", headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1)
public UserEmailResponse getUserEmailByPhoneV1_1(
@Parameter(
description = "이메일을 찾고자 하는 유저의 전화번호",
example = "01012345678"
) @RequestParam @NotBlank @PhoneNumber String phone
) {
UserDto userDto = userQueryService.getDtoByPhone(phone);
return new UserEmailResponse(userDto.getEmail());
}

@Operation(
summary = "회원 등록",
description = "회원을 등록합니다."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ajou.hertz.domain.user.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
public class UserEmailResponse {

@Schema(description = "이메일", example = "[email protected]")
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ajou.hertz.domain.user.exception;

import com.ajou.hertz.common.exception.NotFoundException;
import com.ajou.hertz.common.exception.constant.CustomExceptionType;

public class UserNotFoundByPhoneException extends NotFoundException {
public UserNotFoundByPhoneException(String phone) {
super(CustomExceptionType.USER_NOT_FOUND_BY_PHONE, phone);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByKakaoUid(String kakaoUid);

Optional<User> findByPhone(String phone);

boolean existsByEmail(String email);

boolean existsByKakaoUid(String kakaoUid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.ajou.hertz.domain.user.entity.User;
import com.ajou.hertz.domain.user.exception.UserNotFoundByEmailException;
import com.ajou.hertz.domain.user.exception.UserNotFoundByIdException;
import com.ajou.hertz.domain.user.exception.UserNotFoundByPhoneException;
import com.ajou.hertz.domain.user.repository.UserRepository;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -68,6 +69,18 @@ public UserDto getDtoByEmail(String email) {
return UserDto.from(getByEmail(email));
}

/**
* 전화번호로 user 정보를 조회한다.
*
* @param phone 조회하고자 하는 user의 전화번호
* @return 조회한 유저 정보가 담긴 dto
* @throws UserNotFoundByPhoneException 일치하는 유저를 찾지 못한 경우
*/
public UserDto getDtoByPhone(String phone) {
User user = userRepository.findByPhone(phone).orElseThrow(() -> new UserNotFoundByPhoneException(phone));
return UserDto.from(user);
}

/**
* 전달된 email을 사용 중인 회원의 존재 여부를 조회한다.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,40 @@ public void securitySetUp() throws Exception {
verifyEveryMocksShouldHaveNoMoreInteractions();
}

@Test
void 전화번호가_주어지고_주어진_전화번호를_사용_중인_회원의_이메일을_조회한다() throws Exception {
// given
String phone = "01012345678";
UserDto expectedResult = createUserDto();
given(userQueryService.getDtoByPhone(phone)).willReturn(expectedResult);

// when & then
mvc.perform(
get("/v1/users/email")
.header(API_MINOR_VERSION_HEADER_NAME, 1)
.queryParam("phone", phone)
)
.andExpect(status().isOk())
.andExpect(jsonPath("email").value(expectedResult.getEmail()));
then(userQueryService).should().getDtoByPhone(phone);
verifyEveryMocksShouldHaveNoMoreInteractions();
}

@Test
void 전화번호가_주어지고_주어진_전화번호를_사용_중인_회원의_이메일을_조회한다_전달된_전화번호가_잘못된_형식인_경우_에러가_발생한다() throws Exception {
// given
String phone = "12345";

// when & then
mvc.perform(
get("/v1/users/email")
.header(API_MINOR_VERSION_HEADER_NAME, 1)
.queryParam("phone", phone)
)
.andExpect(status().isUnprocessableEntity());
verifyEveryMocksShouldHaveNoMoreInteractions();
}

@Test
void 주어진_회원_정보로_신규_회원을_등록한다() throws Exception {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.ajou.hertz.domain.user.entity.User;
import com.ajou.hertz.domain.user.exception.UserNotFoundByEmailException;
import com.ajou.hertz.domain.user.exception.UserNotFoundByIdException;
import com.ajou.hertz.domain.user.exception.UserNotFoundByPhoneException;
import com.ajou.hertz.domain.user.repository.UserRepository;
import com.ajou.hertz.domain.user.service.UserQueryService;

Expand All @@ -34,6 +35,25 @@ class UserQueryServiceTest {
@Mock
private UserRepository userRepository;

@Test
void 카카오_유저_ID가_주어지고_주어진_카카오_유저_ID로_유저를_조회하면_조회된_유저_정보가_담긴_Optional_DTO가_반환된다() throws Exception {
// given
String kakaoUid = "12345";
User expectedResult = createUser(1L, "[email protected]", kakaoUid, "01012345678");
given(userRepository.findByKakaoUid(kakaoUid)).willReturn(Optional.of(expectedResult));

// when
Optional<UserDto> actualResult = sut.findDtoByKakaoUid(kakaoUid);

// then
then(userRepository).should().findByKakaoUid(kakaoUid);
verifyEveryMocksShouldHaveNoMoreInteractions();
assertThat(actualResult).isNotEmpty();
assertThat(actualResult.get())
.hasFieldOrPropertyWithValue("id", expectedResult.getId())
.hasFieldOrPropertyWithValue("kakaoUid", expectedResult.getKakaoUid());
}

@Test
void 유저_id가_주어지고_주어진_id로_유저를_조회하면_조회된_유저_정보가_반환된다() throws Exception {
// given
Expand Down Expand Up @@ -70,7 +90,7 @@ class UserQueryServiceTest {
void 이메일이_주어지고_주어진_이메일로_유저를_조회하면_조회된_유저_정보가_반환된다() throws Exception {
// given
String email = "[email protected]";
User expectedResult = createUser(1L, email, "1234");
User expectedResult = createUser(1L, email, "1234", "01012345678");
given(userRepository.findByEmail(email)).willReturn(Optional.of(expectedResult));

// when
Expand Down Expand Up @@ -100,22 +120,36 @@ class UserQueryServiceTest {
}

@Test
void 카카오_유저_ID가_주어지고_주어진_카카오_유저_ID로_유저를_조회하면_조회된_Optional_유저_정보가_반환된다() throws Exception {
void 전화번호가_주어지고_주어진_전화번호로_유저를_조회하면_조회된_유저_정보가_반환된다() throws Exception {
// given
String kakaoUid = "12345";
User expectedResult = createUser(1L, "[email protected]", kakaoUid);
given(userRepository.findByKakaoUid(kakaoUid)).willReturn(Optional.of(expectedResult));
String phone = "01012345678";
User expectedResult = createUser(1L, "[email protected]", "1234", phone);
given(userRepository.findByPhone(phone)).willReturn(Optional.of(expectedResult));

// when
Optional<UserDto> actualResult = sut.findDtoByKakaoUid(kakaoUid);
UserDto actualResult = sut.getDtoByPhone(phone);

// then
then(userRepository).should().findByKakaoUid(kakaoUid);
then(userRepository).should().findByPhone(phone);
verifyEveryMocksShouldHaveNoMoreInteractions();
assertThat(actualResult).isNotEmpty();
assertThat(actualResult.get())
assertThat(actualResult)
.hasFieldOrPropertyWithValue("id", expectedResult.getId())
.hasFieldOrPropertyWithValue("kakaoUid", expectedResult.getKakaoUid());
.hasFieldOrPropertyWithValue("phone", expectedResult.getPhone());
}

@Test
void 전화번호가_주어지고_주어진_전화번호로_유저를_조회한다_만약_일치하는_유저가_없다면_예외가_발생한다() {
// given
String phone = "01012345678";
given(userRepository.findByPhone(phone)).willReturn(Optional.empty());

// when
Throwable t = catchThrowable(() -> sut.getDtoByPhone(phone));

// then
then(userRepository).should().findByPhone(phone);
verifyEveryMocksShouldHaveNoMoreInteractions();
assertThat(t).isInstanceOf(UserNotFoundByPhoneException.class);
}

@Test
Expand Down Expand Up @@ -170,7 +204,7 @@ private void verifyEveryMocksShouldHaveNoMoreInteractions() {
then(userRepository).shouldHaveNoMoreInteractions();
}

private User createUser(Long id, String email, String kakaoUid) throws Exception {
private User createUser(Long id, String email, String kakaoUid, String phone) throws Exception {
Constructor<User> userConstructor = User.class.getDeclaredConstructor(
Long.class, Set.class, String.class, String.class, String.class,
String.class, LocalDate.class, Gender.class, String.class, String.class
Expand All @@ -185,12 +219,12 @@ private User createUser(Long id, String email, String kakaoUid) throws Exception
"https://user-default-profile-image-url",
LocalDate.of(2024, 1, 1),
Gender.ETC,
"01012345678",
phone,
null
);
}

private User createUser(Long id) throws Exception {
return createUser(id, "[email protected]", "12345");
return createUser(id, "[email protected]", "12345", "01012345678");
}
}

0 comments on commit 1ee7a4e

Please sign in to comment.