- MailComponent
- JavaMailSender를 이용하기 위한 Component 클래스를 만든다.
- @Component 애너테이션을 붙이지 않으면 아래와 같은 오류가 난다. 따로 클래스를 만들고 빈 등록을 꼭 해줘야한다.
<hide/>
Parameter 3 of constructor in com.rezero.inandout.member.service.MemberServiceImpl required a bean of type 'com.rezero.inandout.member.model.MailComponent' that could not be found.
- mail
- Mime: 단순 텍스트 뿐만 아니라 다른 여러 바이너리 파일을 메일에 첨부할 때 쓰인다.
- MimeMessage는 디테일하게 메일 전송 가능 <=> SimpleMailMessage는 단순 텍스트만 전송 가능하다.
- MimeMessagePreparator는 메시지 준비를 위해서 필요한 콜백 인터페이스를 제공한다.
- MimeMessageHelper: 메시지 생성을 위해 쓰이는 클래스 (이미지, 파일, 텍스트 등을 html 형식으로 제공)
- mail 전송에 실패할 수도 있으니 예외처리를 한다.
- html 형태로 텍스트를 구성하기 위해서 setText()에 true를 꼭 넣어줘야한다.
- 안 넣어주면 그냥 쌩 텍스트로 이메일이 전송된다.
<hide/>
@Component
@RequiredArgsConstructor
public class MailComponent {
private final JavaMailSender javaMailSender;
public void send(String to, String subject, String text) {
MimeMessagePreparator msg = new MimeMessagePreparator() {
@Override
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true,
"UTF-8");
mimeMessageHelper.setTo(to);
mimeMessageHelper.setSubject(subject);
mimeMessageHelper.setText(text, true);
}
};
try {
javaMailSender.send(msg);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
- service
- ex) http://localhost:8080/api/signup/sending?id=fe471ac7-911c-4267-923f-aa9699bd7418
<hide/>
@Override
public void join(JoinMemberInput input) {
validateInput(input);
String password = input.getPassword();
String encPassword = bCryptPasswordEncoder.encode(password);
String uuid = UUID.randomUUID().toString();
System.out.println("uuid " + uuid);
Member member = Member.builder().email(input.getEmail()).address(input.getAddress())
.birth(input.getBirth()).gender(input.getGender()).password(encPassword)
.nickName(input.getNickName()).phone(input.getPhone()).status(MemberStatus.REQ)
.emailAuthKey(uuid).build();
memberRepository.save(member);
String subject = "In and Out 회원 가입을 축하드립니다.";
String text = "<p>안녕하세요. In And Out 입니다.</p><p>아래 링크를 누르시면 회원 가입이 완료됩니다.</p>"
+ "<div><a href='http://localhost:8080/api/signup/sending?id=" + uuid
+ "'>가입 완료</a></div>";
mailComponent.send(input.getEmail(), subject, text);
}
@Override
public void emailAuth(String uuid) {
Optional<Member> optionalMember = memberRepository.findByEmailAuthKey(uuid);
if (!optionalMember.isPresent()) {
throw new MemberException(EMAIL_AUTH_KEY_NOT_EXIST);
}
Member member = optionalMember.get();
member.setStatus(MemberStatus.ING);
memberRepository.save(member);
}
- controller
- 링크 ex) http://localhost:8080/api/signup/sending?id=3cf81017-e796-4e65-a7ef-f9c820803873
- url에서 파라미터인 uuid를 가져와서 emailAuth()에 적용한다.
- 생각해보니까 이메일 인증 URL을 띄우면 MemberRepository에서 해당 멤버의 MemberStatus가 "REQ" => "ING"로 활성화된다.
- 따라서, 이는 클라이언트 => 서버에 데이터를 전송해주는 것에 가깝고 이는 GetMapping 보다는 PostMapping에 가깝다.
- GetMapping과 PostMapping의 차이는?
- Get: SELECT 기능에서 우수함
- Post:UPDATE, CREATE, DELETE 기능으로 사용한다.
<hide/>
@PostMapping("/signup")
@ApiOperation(value = "회원 가입 API", notes = "이메일을 아이디로 사용하여 가입할 수 있다.")
public ResponseEntity<?> signUp(
@ApiParam(value = "회원 가입 정보 입력") @RequestBody JoinMemberInput memberInput) {
memberService.join(memberInput);
String message = "이메일 인증을 하시면 회원가입이 완료됩니다.";
return new ResponseEntity(message, HttpStatus.OK);
}
@PostMapping("/signup/sending")
@ApiOperation(value = "회원 가입을 위한 이메일 인증 API", notes = "이메일을 인증하여 회원 가입 가능하다.")
public ResponseEntity<?> emailAuth(HttpServletRequest request) {
String uuid = request.getParameter("id");
memberService.emailAuth(uuid);
String message = "회원가입이 완료되었습니다.";
return new ResponseEntity(message, HttpStatus.OK);
}
- test
<hide/>
@Test
@DisplayName("회원 가입을 위한 이메일 인증")
void emailAuth() throws Exception {
// given
String uuid = UUID.randomUUID().toString();
// when
mockMvc.perform(
post("/api/signup/sending?id=" + uuid)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(print());
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// then
Mockito.verify(memberServiceImpl, times(1)).emailAuth(captor.capture());
assertEquals(uuid, captor.getValue());
}
- test
- uuid가 존재하지 않는 경우 잘못된 url이므로 예외처리한다.
<hide/>
@Test
@DisplayName("회원가입을 위한 이메일 인증 - 성공")
void emailAuth() {
// given
String uuid = UUID.randomUUID().toString();
Member member = Member.builder()
.email("egg@naver.com")
.password("abc123!@")
.emailAuthKey(uuid)
.status(MemberStatus.REQ)
.build();
given(memberRepository.findByEmailAuthKey(anyString())).willReturn(Optional.of(member));
// when
memberService.emailAuth(uuid);
// then
assertEquals(MemberStatus.ING, member.getStatus());
}
@Test
@DisplayName("회원가입을 위한 이메일 인증 - 실패")
void emailAuth_fail() {
// given
String uuid = UUID.randomUUID().toString();
given(memberRepository.findByEmailAuthKey(anyString())).willReturn(Optional.empty());
// when
MemberException exception = assertThrows(MemberException.class,
() -> memberService.emailAuth(uuid));
// then
assertEquals(MemberErrorCode.EMAIL_AUTH_KEY_NOT_EXIST, exception.getErrorCode());
}
'Spring Projcect > [팀플] In & Out 가계부' 카테고리의 다른 글
회원 비밀번호 초기화 API (0) | 2022.10.31 |
---|---|
회원 탈퇴 API (0) | 2022.10.30 |
회원 가입 API (0) | 2022.10.27 |
회원 로그아웃 API (0) | 2022.10.26 |
회원 예외 처리 MemberErrorCode, ExceptionHandler (0) | 2022.10.25 |