today_is
[ mail ] 이메일 보내기 본문
오늘의 목표
프로젝트에서 사용해봤던 mail 보내기 과정을 정리해보겠다
먼저 설정해주어야할 작업
1) 프로젝트 내에서 바로 id, password 를 임의로 작성해줄 것이기 때문에
편의성을 위해서는 2차인증을 "해제" 해두자.
2) 네이버 메일 -> 환경설정 -> POP3/IMAP 설정에서,
POP3/SMTP 설정 , IMAP/SMTP 설정 둘다 사용함으로 체크해둔 후, "저장"해두자.
꼭, 네이버가 아니더라도
2번에서 언급한 환경설정만 해둔다면 메일 보내기 가능하다
3) 프로젝트에 필요한 설정들 해주기
pom.xml
: 의존성 추가
( ... 중략 ...)
<!-- 스프링에서 웹소켓을 처리할 수 있도록 하는 라이브러리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 스프링에서 STOMP 처리를 위한 라이브러리 -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stomp</artifactId>
<version>5.4.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework-version}</version>
</dependency>
</dependencies>
servlet-context.xml
: web-socket message-broker prefix 설정 해주기
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<view-controller path="/" view-name="home" />
<context:component-scan base-package="com.itbank.controller" />
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/endpoint">
<websocket:sockjs websocket-enabled="true" />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/broker" />
</websocket:message-broker>
</beans:beans>
root-context.xml
: component 를 사용할 것이기 때문에 스프링 빈 등록해주기
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:component-scan base-package="com.itbank.component" />
</beans>
그밖에도, 기본적으로 설정되어있어야할 것들을 지정해주어야함
ex03.jsp
인증번호가 담겨있는 메일을 보내고
사용자에게 인증번호입력을 받아서
일치하는지 확인하기 실습
인증번호 보내기 버튼을 누르면
숨어있던 인증번호 입력 form 이 나타난다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<h3>AJAX로 메일보내기</h3>
<div class="mailSend">
<form>
<h3>인증번호 발송</h3>
<p>
<input type="email" name="address" placeholder="email">
<button>인증번호 보내기</button>
</p>
<p class="message"></p>
</form>
</div>
<div class="auth box hidden">
<form>
<h3>인증번호 확인</h3>
<p>
<input type="text" name="authNumber" placeholder="인증번호 입력">
<button>인증 확인</button>
</p>
<p class="message"></p>
</form>
</div>
<script>
const mailSendForm = document.forms[0] // form은 배열로 바로 불러올 수 있음
const authForm = document.forms[1]
mailSendForm.onsubmit = async function(event) {
event.preventDefault()
const url = '${cpath}/ajax/sendMail'
const opt = {
method: 'POST',
body : JSON.stringify({
address: event.target.querySelector('input').value
}),
headers: {
'Content-Type' : 'application/json;charset=utf-8'
}
}
const result = await fetch(url, opt).then(resp => resp.text())
const message = event.target.querySelector('p.message')
if(result == 1) {
message.innerText = '메일을 전송했습니다'
message.style.color = 'blue'
document.querySelector('.auth').classList.remove('hidden')
}
else {
message.innerText = '메일을 보낼 수 없습니다'
message.style.color = 'red'
}
}
authForm.onsubmit = async function(event) {
event.preventDefault()
const inputNumber = event.target.querySelector('input').value
const url = '${cpath}/ajax/authNumber/' + inputNumber // /를 꼭 붙여야 pathVariable 사용할 수 있음
const result = await fetch(url).then(resp => resp.text())
const message = event.target.querySelector('p.message')
if(result == 1) {
message.innerText = '인증 성공'
message.style.color = 'blue'
}
else {
message.innerText = '인증 실패'
message.style.color = 'red'
}
}
</script>
</body>
</html>
AjaxController
package com.itbank.controller;
import java.util.HashMap;
import java.util.Random;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.itbank.component.MailComponent;
// AJAX 니까 그냥 controller 말고 Restcontroller
// 문자열 하나만 받는다면, 굳이 ajax 필요없음
// 객체로 묶어주기 위해서 ajax 로 처리하자
@RestController
@RequestMapping("/ajax")
public class AjaxController {
@Autowired private MailComponent component;
private Random ran = new Random();
@PostMapping("/sendMail")
public int sendMail(@RequestBody HashMap<String, String> param, HttpSession session) {
System.out.println("address : " + param.get("address"));
int num = ran.nextInt(999999);
String authNumber = String.format("%06d", num);
System.out.println("authNumber : " + authNumber);
session.setAttribute("authNumber", authNumber);
session.setMaxInactiveInterval(180);
param.put("subject", "인증번호");
param.put("content", authNumber);
int row = component.sendMimeMessage(param);
System.out.println(row != 0 ? "전송 성공" : "전송 실패");
return row;
}
@GetMapping("/authNumber/{inputNumber}")
public int authNumber(@PathVariable("inputNumber") String inputNumber, HttpSession session) {
// 만약, 세션이 만료되었다면 (== 180초)
// authNumber 의 값은 1이다
// 두개의 문자열의 일치를 비교할때 A.equals(B) 형태로 비교한다
// null 일 가능성이 있는 문자열을 뒤에 배치하여 Nullpointer 에러를 방지함
// 만약, 세션이 만료되었을 때 예외를 발생시켜서 다른 반환값을 전달하려면,
// ExceptionHandler 를 사용하거나,
// @RestControllerAdvice 클래스를 작성하여 처리할 수도 있다
String authNumber = (String) session.getAttribute("authNumber");
int row = 0;
if(inputNumber.equals(authNumber)) {
row = 1;
}
return row;
}
}
mailComponent
package com.itbank.component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Properties;
import java.util.Scanner;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.PasswordAuthentication;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
@Component
public class MailComponent {
private final String host = "smtp.naver.com";
private final int port = 465;
private String serverId = "로그인할 아이디"; // 이 계정으로 로그인해서 메일을 보낼것임
private String serverPw = "비번";
private Properties props;
// @Autowired 가 자동으로 스프링 빈 연결하듯이
// @Value는 자동으로 자원(파일)을 연결한다
// org.springframework.core.io.Resource
// classpath : "src/main/java" or "src/main/resources
@Value("classpath:mailForm.html")
private Resource mailForm;
@PostConstruct
private void init() {
props = new Properties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.prot", port);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.true", host);
}
// 단순 텍스트 메일 보내기 (ex01)
public int sendSimpleMessage(String address, String content, String subject) {
// 1) 메일 서버 인증 (접속에 필요하다)
Session mailSession = Session.getDefaultInstance(props, new Authenticator() {
String un = serverId;
String pw = serverPw;
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(un, pw);
}
});
mailSession.setDebug(true); // 메일 전송 과정을 콘솔창에 출력한다
// 2) 보낼 메세지 작성
Message message = new MimeMessage(mailSession);
try {
message.setFrom(new InternetAddress(serverId + "@naver.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(address)); // 받는사람
message.setSubject(subject); // 제목
message.setText(content); // 내용
Transport.send(message); // 3) 준비가 끝난 메시지를 발송한다
return 1;
} catch (MessagingException e) {
e.printStackTrace();
return 0;
}
}
// html 포함한 메일(ex02)
public int sendMimeMessage(HashMap<String, String> param) {
// 1) 메일 서버 인증 (접속에 필요하다)
Session mailSession = Session.getDefaultInstance(props, new Authenticator() {
String un = serverId;
String pw = serverPw;
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(un, pw);
}
});
mailSession.setDebug(true); // 메일 전송 과정을 콘솔창에 출력한다
// 2) 보낼 메세지 작성
Message message = new MimeMessage(mailSession);
String address = param.get("address");
String subject = param.get("subject");
String content = param.get("content");
try {
message.setFrom(new InternetAddress(serverId + "@naver.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(address)); // 받는사람
message.setSubject(subject); // 제목
String tag = "";
Scanner sc = new Scanner(mailForm.getFile());
while(sc.hasNextLine()) {
tag += sc.nextLine();
}
sc.close();
content = String.format(tag, content);
message.setContent(content, "text/html; charset=utf-8"); // 태그 포함 내용
Transport.send(message);
return 1;
} catch (MessagingException | IOException e) {
e.printStackTrace();
return 0;
}
}
}
결과 확인
네이버든, 구글이든 메일을 보내려면 POP/IMAP 을 활성화해야하고
사이트별로 port가 다르기 때문에 유의하자!
네이버 포트 465
구글 포트 587
구글 계정으로도 메일 보내기를 연습했다
'spring' 카테고리의 다른 글
[ websocket ] 채팅 (0) | 2024.03.04 |
---|---|
[ websocket ] 메모장 (0) | 2024.03.03 |
[ spring ] 다른 ip와 DB 내용 공유하기 (0) | 2024.03.03 |
[ json ] HashMap 을 이용한 json 데이터 mapping (0) | 2024.02.04 |
[ Exception ] 예외처리, 예외전가 (0) | 2024.01.30 |