Google SMTP 사용
추가로 공부하세요~! 안타깝지만 제 블로그 글이 아님..ㅎ
- SMTP, POP3, IMAP 차이
https://m.blog.naver.com/ijoos/221742035684
- SSL, TSL 차이
https://ko.gadget-info.com/difference-between-ssl
# application.yaml에 작성
spring:
mail:
default-encoding: UTF-8
host: smtp.gmail.com
port: 587
username: [------@gmail.com] <자기 구글 E-mail주소>
password: [-------] <구글 비밀번호=>앱 비밀번호로 변경>
properties:
mail:
smtp:
auth: true
starttls:
enable: true
protocol: smtp
----------------------------------------------------------------------------------------------------------------------
org.springframework.mail.MailAuthenticationException: Authentication failed;
nested exception is javax.mail.AuthenticationFailedException
위 에러가 발생하는 경우 보안 수준이 낮은 앱의 액세스를 허용해 주어야 합니다.
라고 하는데 어차피 2022년 5월 기준 끝나기도 하고, 나의 경우 해결이 안됐었다
해결방법
구글(Gmail)을 기준으로 작성하였다
1) 구글에 로그인 한 후 오른쪽 위 동그란 프로필을 눌러서 Google 계정 관리에 들어가자
2) 왼쪽의 보안 탭으로 들어가자
3) 2단계 인증을 활성화한다(앱 비밀번호를 받기 위함)
4) 앱 비밀번호를 클릭해서 - 메일 / - Windows 컴퓨터를 선택한다
5) 생성을 누르고 받아온 기기용 앱 비밀번호를 yaml 파일의 pass로 바꿔준다
사용 방법들
1. Springboot-starter-mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
참고로 template엔진은 thymeleaf, freemarker, velocity, mustache 등등 다양하다.
텍스트 혹은 객체를 html 또는 각각의 파일들에 매핑시켜주는 역할을 한다
# Controller
import com.example.testproject.dto.MailDto;
import com.example.testproject.service.MailService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@Controller
public class MailController {
private final MailService mailService;
@GetMapping("/mail")
public String dispMail() {
return "mail";
}
@PostMapping("/mail")
public void execMail(MailDto mailDto) {
mailService.mailSimpleSend(mailDto);
// mailService.mailSend(mailDto);
}
}
# Service
import com.example.testproject.dto.MailDto;
import com.example.testproject.handler.MailHandler;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class MailService {
private JavaMailSender mailSender;
private static final String FROM_ADDRESS = "no_repy@boki.com";
@Async
public void mailSimpleSend(MailDto mailDto) {
SimpleMailMessage message = new SimpleMailMessage();
String msg = mailDto.getMessage() + "\n인증번호는 " + RandomStringUtils.randomAlphanumeric(6) + "입니다";
message.setTo(mailDto.getAddress());
// message.setFrom(MailService.FROM_ADDRESS); // 구글 정책 변경으로 설정한 gmail로 가게됨
message.setSubject(mailDto.getTitle());
message.setText(msg);
mailSender.send(message);
}
@Async
public void justSend(MailDto mailDto) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(mailDto.getTitle());
message.setText(mailDto.getMessage());
mailSender.send(message);
}
}
비동기를 달아줬다. Configration을 따로 하는 객체 혹은 SpringBootApplication위에 @EnableAsync를 달아줘야한다
비동기는 Config해주지 않으면 스레드가 Integer.MAX_VALUE개만큼 생성되므로 컨피그가 필요하다
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
public class AsyncConfig extends AsyncConfigurerSupport {
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
//executor.setThreadNamePrefix("hanumoka-async-");
executor.initialize();
return executor;
}
}
나중에 제대로 포스팅하겠다!
# DTO
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class MailDto {
private String address;
private String title;
private String message;
}
# resources / templates / mail.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>메일 발송</title>
</head>
<body>
<h1>메일 발송</h1>
<form th:action="@{/mail}" method="post">
<input name="address" placeholder="이메일 주소"> <br>
<input name="title" placeholder="제목"> <br>
<textarea name="message" placeholder="메일 내용을 입력해주세요." cols="60" rows="20"></textarea>
<button>발송</button>
</form>
</body>
</html>
# 테스트
2. javax.mail:mail or com.sun.mail:javax.mail
implementation 'javax.mail:mail:1.4.7'
둘 중에 하나
implementation 'com.sun.mail:javax.mail:1.6.2'
그냥 static main 메서드에서 작성을 했다
import org.apache.commons.lang.RandomStringUtils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
public class VerifyEmail {
public static void main(String[] args) {
// String recipient = "받는사람이메일@co.kr";
String recipient = "[이메일]@naver.com";
// String code = "abc";
String msg = "인증번호는 " + RandomStringUtils.randomAlphanumeric(6) + "입니다";
// 1. 발신자의 메일 계정과 비밀번호 설정
// final String user = "전송자이메일@co.kr";
final String user = "[구글 이메일 주소]";
// final String user = "no_repy@boki.com"; // 구글 정책 변경으로 이거로 전송 안됨
// final String password = "********";
final String password = "[발급받은 앱 기기용 비밀번호]";
// 2. Property에 SMTP 서버 정보 설정
Properties prop = new Properties();
prop.put("mail.smtp.host", "smtp.gmail.com");
prop.put("mail.smtp.port", 465);
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.trust", "smtp.gmail.com");
// 3. SMTP 서버정보와 사용자 정보를 기반으로 Session 클래스의 인스턴스 생성
Session session = Session.getDefaultInstance(prop, new javax.mail.Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
});
// 4. Message 클래스의 객체를 사용하여 수신자와 내용, 제목의 메시지를 작성한다.
// 5. Transport 클래스를 사용하여 작성한 메세지를 전달한다.
MimeMessage message = new MimeMessage(session);
try {
message.setFrom(new InternetAddress(user));
// 수신자 메일 주소
message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
// Subject
message.setSubject("임시비밀번호 발송메일");
// Text
message.setText(msg);
Transport.send(message); // send message
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
3. WebClient
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.6.4'
non-block 방식 이용
// 호출하는 쪽
@Builder
@Getter
static class MailDto {
private String address;
private String title;
private String message;
}
@PostMapping("/mail")
public ResponseEntity<Object> mail() {
final String MAIL_API = "localhost:8090";
String msg = "인증번호: " + RandomStringUtils.randomAlphanumeric(6);
MailDto mailDto = MailDto.builder().address("[받는 쪽 이메일 입력]").message(msg).title("인증번호 발송").build();
ObjectMapper objectMapper = new ObjectMapper();
MultiValueMap<String, String> multiValueMap = convert(objectMapper, mailDto);
webClient.method(HttpMethod.POST)
.uri(MAIL_API + "/mail")
.bodyValue(multiValueMap)
.retrieve()
.bodyToMono(Void.class)
.subscribe();
return ResponseEntity.ok("메일발송 SUCCESS");
}
// @Slf4j
// @NoArgsConstructor(access = AccessLevel.PRIVATE)
// public abstract class MultiValueMapConverter {
// public static MultiValueMap<String, String> convert(ObjectMapper objectMapper, Object dto) { // (2)
public MultiValueMap<String, String> convert(ObjectMapper objectMapper, Object dto) { // (2)
try {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
Map<String, String> map = objectMapper.convertValue(dto, new TypeReference<Map<String, String>>() {}); // (3)
params.setAll(map); // (4)
return params;
} catch (Exception e) {
log.error("Url Parameter 변환중 오류가 발생했습니다. requestDto={}", dto, e);
throw new IllegalStateException("Url Parameter 변환중 오류가 발생했습니다.");
}
// }
}
// 호출 당하는 쪽
import com.example.testproject.dto.MailDto;
import com.example.testproject.service.MailService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@Controller
public class MailController {
private final MailService mailService;
@GetMapping("/mail")
public String dispMail() {
return "mail";
}
@PostMapping("/mail")
public void execMail(MailDto mailDto) {
// mailService.mailSimpleSend(mailDto);
mailService.justSend(mailDto);
// mailService.mailSend(mailDto);
}
}
추가로 이벤트방식으로 코드를 작성할 수도 있다
실질적으로는 실서비스를 할때는 AWS SES(Simple Email Service)를 이용하는 등 다른 방법을 찾아봐야하겠다!
참고:
https://jojoldu.tistory.com/478
https://victorydntmd.tistory.com/342
'Backend > Java - Spring' 카테고리의 다른 글
Blocking/Non-Blocking & Sync/Async가 뭔데? (1) | 2022.03.23 |
---|---|
DTO instantiate (feat. Lombok, GET/POST) (4) | 2022.03.10 |
Springboot local 환경에서 https 적용하기 (0) | 2022.03.03 |
댓글