인증 이메일을 보낼 이메일(Google, Naver ...)마다 다르겠지만 추가적인 이메일 설정이 필요합니다. 저는 Gmail을 사용했습니다. Gmail 설정은 이 링크를 참고하세요!
Gradle
implementation'org.springframework.boot:spring-boot-starter-mail'
Mailconfig
@Configuration
public class MailConfig {
@Value("${email.id}")
private String fromId;
@Value("${email.password}")
private String password;
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(465);
mailSender.setUsername(fromId);
mailSender.setPassword(password);
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.debug", "true");
return mailSender;
}
}
- @Value로 설정되어 있는 값은 yml에 설정하시면 됩니다
- smtp 프로트콜 이용 (465 포트번호)
- JavaMailSender 객체를 생성하여. 이 객체를 사용해서 이메일을 보내는 것입니다.
MailController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/mail")
@PreAuthorize("isAuthenticated()")
public class MailController {
private final AuthMailService authMailService;
/**
* 인증번호 보내기
*/
@PostMapping
public ResponseForm<AuthNumberResponse> sendEmail(@Valid @RequestBody SendMailRequest request, @AuthenticationPrincipal AuthorityUserDTO authorityUserDTO) {
return new ResponseForm<>(authMailService.sendCodeEmail(request.getEmail(), authorityUserDTO.getProviderId()));
}
}
참고) AuthorityUserDTO는 Security Context Holder에 담겨져 있는 인증객체(API 요청한 유저)입니다
SendMailRequest
@Data
@NoArgsConstructor
public class SendMailRequest {
@NotBlank
private String email; //인증 번호 받을 이메일
}
AuthMailService
@Service
@RequiredArgsConstructor
public class AuthMailService {
private static final Long EXPIRATION = 1800000L; //한시간
private final RandomGenerator randomGenerator;
private final SendMailService sendMailService;
private final RedisUtil redisUtil;
@Transactional
public AuthNumberResponse sendCodeEmail(String email, String key) {
int authNumber = randomGenerator.makeRandomNumber();
String title = "Flint 회원 가입 인증 이메일 입니다."; // 이메일 제목
String content =
"홈페이지를 방문해주셔서 감사합니다." + //html 형식으로 작성
"<br><br>" +
"인증 번호는 " + authNumber + "입니다." +
"<br>" +
"해당 인증번호를 인증번호 확인란에 기입하여 주세요."; //이메일 내용 삽입
sendMailService.sendEmail(email, title, content);
redisUtil.saveAuthNumber(key, String.valueOf(authNumber), EXPIRATION); //인증번호 검증을 위해 레디스에 저장
return new AuthNumberResponse(authNumber);
}
}
- 이메일 보낼 내용은는 String, html, file 형식으로 보낼 수 있습니다.
- saveAuthNumber(): 인증번호를 Redis에 담는 메소드. Key는 유저 ProviderId입니다.
- 인증번호를 Redis에 담은 이유: 관계형 데이터베이스에 담는 것보다 인증번호 검증을 더 빠른 속도로 처리할 수 있습니다.
RandomGenerator
@Component
public class RandomGenerator {
private static final Random random = new Random();
public int makeRandomNumber() {
// 난수의 범위 111111 ~ 999999 (6자리 난수)
return random.nextInt(888888) + 111111;
}
}
SendMailService
@Service
@RequiredArgsConstructor
public class SendMailService {
private final JavaMailSender mailSender;
@Value("${email.id}")
private String fromId;
@Async("mailExecutor")
public void sendEmail(String to, String subject, String content) {
MimeMessagePreparator messagePreparator =
mimeMessage -> {
//true는 멀티파트 메세지를 사용하겠다는 의미
final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(fromId);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
};
mailSender.send(messagePreparator);
}
}
- @Async: 비동기화 처리를 위한
- JavaMailSender로 이메일을 보낼 때 시간이 10초 이상 소요됐습니다. 이는 사용자 경험에 불쾌감을 줄 수 있기 때문에 비동기로 처리했습니다.
AsyncConfig
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "mailExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("Async MailExecutor-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}
AuthNumberResponse
@Data
@AllArgsConstructor
public class AuthNumberResponse {
private int authNumber;
}
결과
'스프링' 카테고리의 다른 글
Intellij에서 gradle 버전이 안 보일 때 해결법 (0) | 2024.04.06 |
---|---|
mysql, gradle 종속성 관리 변경된 점 (0) | 2024.01.31 |
[인가 설정 리팩토링] Security Config -> @PreAuthorize (0) | 2024.01.25 |
JWT Token Service 개발 중 고민, Tip (0) | 2024.01.24 |
[쿼리문 개선] 댓글/대댓글 조회 dto로 받기 (1) | 2023.11.27 |