아이디(email) 찾기 & 비밀번호 찾기
- API
- 이메일: api/password/email
- 비밀번호: api/password/email/phone
- email을 입력받아서 존재하는 아이디인지 확인한다.
- 새로운 Input 클래스를 만든다.
- 컨트롤러에서 @RequsetBody를 적용하기 위해서 만든다.
<hide/>
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FindPasswordMemberInput {
private String email;
private String phone;
}
- Impl 클래스
- findMemberByEmail() 메서드를 findEmail(), findPhone() 에 모두 적용한다.
<hide/>
private Member findMemberByEmail(String email) {
Optional<Member> optionalMember = memberRepository.findByEmail(email);
if (!optionalMember.isPresent()) {
throw new RuntimeException("존재하지 않는 아이디(이메일)입니다. 이메일을 정확하게 입력해주세요");
}
return optionalMember.get();
}
@Override
public String findEmail(String email) {
findMemberByEmail(email);
return email;
}
@Override
public String findPhone(String email, String phone) {
Member optionalMember = findMemberByEmail(email);
if (!optionalMember.getPhone().equals(phone)) {
throw new RuntimeException("올바른 연락처(번호)가 아닙니다. 다시 입력해주세요");
}
return optionalMember.getPhone();
}
- ctrl
- @RequestBody를 이용하면 입력받는 인풋 클래스를 컨트롤러 매서드의 매개 변수로 넣어서 적용할 수 있다.
<hide/>
@PostMapping("/password/email")
public ResponseEntity<?> checkEmail(
@RequestBody FindPasswordMemberInput findPasswordMemberInput) {
String findEmail = memberService.findEmail(findPasswordMemberInput.getEmail());
return new ResponseEntity(findEmail, HttpStatus.OK);
}
@PostMapping("/password/email/phone")
public ResponseEntity<?> checkPhone(
@RequestBody FindPasswordMemberInput findPasswordMemberInput) {
String findPhone = memberService.findPhone(findPasswordMemberInput.getEmail(),
findPasswordMemberInput.getPhone());
return new ResponseEntity(findPhone, HttpStatus.OK);
}
Ex) API 테스트
- test 클래스
- 이메일 찾기
- anyString()은 어떤 값을 넣어도 상관없는건가?
- 멤버를 만들어서 findByEmail()을 했을 때, member 객체를 반환할 것이다. 라고 조건을 준다.?
- 이메일 찾기니까 email을 설정해서
<hide/>
@Test
@DisplayName("아이디 찾기 - 이메일 확인")
void findEmail() {
// given
Member member = Member.builder()
.email("egg@naver.com")
.address("서울특별시")
.phone("010-2222-0000")
.birth(LocalDate.from(LocalDate.of(2000, 9, 30)))
.gender("남")
.nickName("원빈")
.password("abc!@#12")
.build();
// 삭제 memberRepository.save(member);
given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));
// 만든 멤버를 반환한다고 가정한다.
// when
String email = "egg@naver.com";
String findEmail = memberService.findEmail(email);
// then
Assertions.assertEquals(email, findEmail);
}
@Test
@DisplayName("비밀번호 찾기 - 이메일, 휴대폰 일치 확인")
void findPhone() {
// GIVEN
Member member = Member.builder()
.email("egg@naver.com")
.address("서울특별시")
.phone("010-2222-0000")
.birth(LocalDate.from(LocalDate.of(2000, 9, 30)))
.gender("남")
.nickName("강동원")
.password("abc!@#12")
.build();
given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));
// WHEN
String email = "egg@naver.com";
String phone = "010-2222-0000";
String findPhone = memberService.findPhone(email, phone);
//THEN
Assertions.assertEquals(findPhone, phone);
}
- 컨트롤러 테스트
- ArgumentCaptor<>
- capture했을 때의 결과가 join은 JoinMemberInput?
- findEmail(), findPhone()의 반환형은 String이고 join() 의 반환형은 void 라서 그런가?
- findPhone() 할 때는 이메일을 anyString()으로 바꾼다.
- ArgumentCaptor<>
<hide/>
@WebMvcTest(MemberController.class)
@DisplayName("MemberController 테스트")
@AutoConfigureMockMvc(addFilters = false)
class MemberControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
ObjectMapper mapper;
@MockBean
private MemberServiceImpl memberServiceImpl;
@MockBean
private MemberRepository memberRepository;
@Test
void signUp() throws Exception {
// given
JoinMemberInput memberInput = JoinMemberInput.builder()
.email("egg@naver.com")
.address("서울특별시")
.phone("010-2222-0000")
.birth(LocalDate.from(LocalDate.of(2000, 9, 30)))
.gender("남")
.nickName("원빈")
.password("1")
.build();
String memberInputJson = mapper.writeValueAsString(memberInput);
//when
mockMvc.perform(
post("/api/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(memberInputJson))
.andExpect(status().isOk())
.andDo(print());
ArgumentCaptor<JoinMemberInput> captor = ArgumentCaptor.forClass(JoinMemberInput.class);
//then
Mockito.verify(memberServiceImpl, times(1)).join(captor.capture());
assertEquals(captor.getValue().getEmail(), memberInput.getEmail());
}
@Test
void checkEmail() throws Exception {
// given
FindPasswordMemberInput memberInput = FindPasswordMemberInput.builder()
.email("egg@naver.com")
.build();
String inputToJson = mapper.writeValueAsString(memberInput);
// when
mockMvc.perform(
post("/api/password/email")
.contentType(MediaType.APPLICATION_JSON)
.content(inputToJson))
.andExpect(status().isOk())
.andDo(print());
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// then
Mockito.verify(memberServiceImpl, times(1)).findEmail(captor.capture());
assertEquals(captor.getValue(), memberInput.getEmail());
}
@Test
void checkPhone() throws Exception {
// given
FindPasswordMemberInput memberInput = FindPasswordMemberInput.builder()
.email("egg@naver.com")
.phone("010-2345-1234")
.build();
String inputToJson = mapper.writeValueAsString(memberInput);
//when
mockMvc.perform(
post("/api/password/email/phone")
.contentType(MediaType.APPLICATION_JSON)
.content(inputToJson))
.andExpect(status().isOk())
.andDo(print());
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
//then
Mockito.verify(memberServiceImpl, times(1)).findPhone(anyString(), captor.capture());
assertEquals(captor.getValue(), memberInput.getPhone());
}
}
궁금한 점
- @RequestParam, @RequestBody 차이점은?
- 둘다 컨트롤러의 매개변수에 주는 어노테이션이다.
- @RequestParam은 url에 매개변수명이 노출된다. 각 변수별로 데이터를 저장 가능하다. 객체 생성 불가능
- @RequestBody는 별도의 Input 클래스를 만들어서 사용할 수 있다. 객체 생성이 가능하다. 변수변로 데이터 저장이 불가능하다.
- ArgumentCaptor<>란?
- 매개 변수(parameter)에 실제 넣는 값을 argument라고 한다.
- 특정 메서드에 사용되는 argument(인자)를 capture(저장)했다가 나중에 다시 getValue() 해서 사용 가능하다.
'Spring Projcect > [팀플] In & Out 가계부' 카테고리의 다른 글
회원 가입 API (0) | 2022.10.27 |
---|---|
회원 로그아웃 API (0) | 2022.10.26 |
회원 예외 처리 MemberErrorCode, ExceptionHandler (0) | 2022.10.25 |
회원 로그인 API (0) | 2022.10.25 |
회원 정보 조회 및 수정 API (0) | 2022.10.21 |