이메일 전송
의존성 추가
- Java Mail Sender 의존성 추가

- 의존성 추가 확인

** SMTP(Simple Mail Transfer Protocol)
JavaMailSenderImpl
- Spring의 클래스
- JavaMailSender의 구현체
- 이메일 전송에 필요한 여러 설정을 할 수 있다
- JavaMailSenderImpl의 메소드 중 일부
void | send(MimeMessage mimeMessage) | Mime Message 전송 |
void | send(SimpleMailMessage simpleMessage) | Simple Message 전송 |
void | setHost(String host) | host 이름 설정 |
void | setPort(int port) | port 번호 설정 |
void | setUserName(String username) | 사용자 아이디 설정 |
void | setPassWord(String password) | 사용자 비밀번호 설정 |
void | setJavaMailProperties(Properties properties) | Session에 대한 JavaMail의 속성 설정 |
SimpleMailMessage
- Spring의 클래스
- 보낸 사람(from), 받는 사람(to), 참조(cc), 제목(subject) 및 내용(text)를 포함하는 간단한 메시지를 전송
- SimpleMailMessage의 메소드 중 일부
void | setFrom(String from) | 보낸 사람(from) 설정 |
void | setTo(String to) | 받는 사람(to) 설정 |
void | setCc(String cc) | 참조(cc) 설정 |
void | setBcc(String bcc) | 숨은 참조(bcc) 설정 - 수신자에게 보이지 않는 참조 |
void | setSubject(String subject) | 제목(subject) 설정 |
void | setText(String text) | 내용(text) 설정 |
Gmail을 이용한 이메일 전송
EmailTest1
- src/test/java에 테스트용 클래스 생성
- Spring의 SimpleMailMessage 클래스를 이용한 단문 이메일 전송
1) JavaMailSenderImpl의 인스턴스 생성 및 기본 설정
2) Properties의 인스턴스 생성 및 설정
3) JavaMailSenderImpl의 추가 설정 - Properties 이용
4) SimpleMailMessage의 인스턴스 생성 및 받는 사람, 제목, 내용 등 설정
5) JavaMailSenderImpl의 send 메소드 실행
@SpringBootTest
public class EmailTest1 {
@Test
public void test() {
// 이메일 전송을 위한 기본 설정
// - JavaMailSenderImpl의 인스턴스 생성 및 설정
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("smtp.gmail.com"); // Host 이름 설정 - Gmail로 설정
sender.setPort(587); // Port 번호 설정 - Gmail은 587번 포트 사용
sender.setUsername("Gmail 아이디"); // 사용자 아이디 설정 - Gmail 아이디
sender.setPassword("Gmail 앱비밀번호"); // 사용자 비밀번호 설정 - Gmail의 앱 비밀번호
// - 추가 설정을 위한 Properties의 인스턴스 생성 및 설정 (Properties는 Map<String, String> 형태)
Properties props = new Properties();
props.setProperty("mail.smtp.auth", "true"); // 인증 여부 설정(필수)
props.setProperty("mail.smtp.debug", "true"); // 디버깅 사용 여부 설정(선택)
props.setProperty("mail.smtp.starttls.enable", "true"); // TLS 사용 여부(필수)
props.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); // TLS 버전 설정(필수)
props.setProperty("mail.smtp.ssl.trust", "smtp.gmail.com"); // 신뢰할 수 있는 대상으로 추가(필수)
// 설정된 Properties의 인스턴스를 JavaMailSenderImpl의 Properties로 설정
sender.setJavaMailProperties(props);
// Simple message 작성
SimpleMailMessage message = new SimpleMailMessage();
message.setTo("받는사람 이메일"); // 받는 사람(to) 설정
message.setSubject("테스트 이메일"); // 제목(subject) 설정
message.setText("메롱"); // 내용(text) 설정
// 설정된 SimpleMailMessage의 인스턴스를 이용하여 단문 이메일 전송
sender.send(message);
}
}
이메일 전송을 위한 인스턴스 생성 및 설정을 모듈화
EmailConfiguration
- @Confgiruation 어노테이션을 붙여서 configuration으로 등록
- @Bean 어노테이션을 붙여서 JavaMailSender를 반환하는 메소드를 bean으로 등록
@Configuration
public class EmailConfiguration {
@Bean // JavaMailSender를 반환하는 메소드를 bean으로 등록
public JavaMailSender javaMailSender() {
// 이메일 전송을 위한 기본 설정
// - JavaMailSenderImpl의 인스턴스 생성 및 설정
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("smtp.gmail.com"); // Host 이름 설정 - Gmail로 설정
sender.setPort(587); // Port 번호 설정 - Gmail은 587번 포트 사용
sender.setUsername("Gmail 아이디"); // 사용자 아이디 설정 - Gmail 아이디
sender.setPassword("Gmail 앱 비밀번호"); // 사용자 비밀번호 설정 - Gmail의 앱 비밀번호
// - 추가 설정을 위한 Properties의 인스턴스 생성 및 설정 (Properties는 Map<String, String> 형태)
Properties props = new Properties();
props.setProperty("mail.smtp.auth", "true"); // 인증 여부 설정(필수)
props.setProperty("mail.smtp.debug", "true"); // 디버깅 사용 여부 설정(선택)
props.setProperty("mail.smtp.starttls.enable", "true"); // TLS 사용 여부(필수)
props.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); // TLS 버전 설정(필수)
props.setProperty("mail.smtp.ssl.trust", "smtp.gmail.com"); // 신뢰할 수 있는 대상으로 추가(필수)
// 설정된 Properties의 인스턴스를 JavaMailSenderImpl의 Properties로 설정
sender.setJavaMailProperties(props);
// 설정이 끝난 JavaMailSenderImpl의 인스턴스를 반환
// - 반환시 JavaMailSenderImpl 형태로 반환되지만 반환형은 JavaMailSender이 된다(업 캐스팅)
return sender;
}
}
EmailProperties를 이용한 EmailConfiguration 개선
** 기존 방식의 문제점
- 협업을 하는 경우 이메일 아이디와 비밀번호가 타인에게 노출될 수 있다
** 해결책
- .gitignore에 .properties를 추가하여 Git Hub에 application.properties 파일이 업로드되지 않도록 한다
- application.properties에 spring에 없는 custom 설정으로 이메일과 관련된 개인 정보를 설정한 후
@ConfigurationProperties 어노테이션을 이용해 필요할 때마다 설정값을 Java 클래스에 바인딩하는 방식으로 작업한다
src/main/resources - application.properties
- sping에 없는 custom 설정을 만들어서 이메일과 관련된 개인 정보 설정

EmailProperties
- application.properties의 값들을 바인딩하여 사용할 수 있도록 하는 Component
@Data
@Component
@ConfigurationProperties(prefix = "custom.email")
public class EmailProperties {
private String host; // Host 이름
private int port; // Port 번호
private String username; // 사용자 아이디
private String password; // 사용자 비밀번호
}
@Component
- Component로 등록하는 어노테이션
@ConfigurationProperties
- *.properties, *.yml 파일에 있는 property들을 Java 클래스의 값으로 가져와서(바인딩) 사용하게 해주는 어노테이션
- prefix 속성을 이용하면 해당 접두사를 가지고있는 모든 property의 값을 자동으로 바인딩해준다
ex) custom.email.port의 값인 587이 EmailProperties의 port 필드의 값으로 바인딩된다
EmailProperties를 이용한 EmailConfiguration 개선
@Configuration
public class EmailConfiguration {
// EmailProperties에 대한 의존성 주입
@Autowired
private EmailProperties emailProperties;
@Bean // JavaMailSender를 반환하는 메소드를 bean으로 등록
public JavaMailSender javaMailSender() {
// 이메일 전송을 위한 기본 설정 (EmailProperties의 값으로 설정하도록 변경)
// - JavaMailSenderImpl의 인스턴스 생성 및 설정
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(emailProperties.getHost()); // Host 이름 설정
sender.setPort(emailProperties.getPort()); // Port 번호 설정
sender.setUsername(emailProperties.getUsername()); // 사용자 아이디 설정
sender.setPassword(emailProperties.getPassword()); // 사용자 비밀번호 설정
// - 추가 설정을 위한 Properties의 인스턴스 생성 및 설정 (Properties는 Map<String, String> 형태)
Properties props = new Properties();
props.setProperty("mail.smtp.auth", "true"); // 인증 여부 설정(필수)
props.setProperty("mail.smtp.debug", "true"); // 디버깅 사용 여부 설정(선택)
props.setProperty("mail.smtp.starttls.enable", "true"); // TLS 사용 여부(필수)
props.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); // TLS 버전 설정(필수)
props.setProperty("mail.smtp.ssl.trust", "smtp.gmail.com"); // 신뢰할 수 있는 대상으로 추가(필수)
// 설정된 Properties의 인스턴스를 JavaMailSenderImpl의 Properties로 설정
sender.setJavaMailProperties(props);
// 설정이 끝난 JavaMailSenderImpl의 인스턴스를 반환
// - 반환시 JavaMailSenderImpl 형태로 반환되지만 반환형은 JavaMailSender이 된다(업 캐스팅)
return sender;
}
}
이메일 전송을 이용한 이메일 인증 구현
플로우 차트
- 인증번호가 포함된 이메일을 사용자에게 전송한다
- 동시에 해당 인증번호에 대한 발급 정보(누구에게, 인증번호, 언제)를 DB에 등록한다
- 사용자가 인증번호를 입력하면 DB에서 조회하여 인증번호 일치 여부를 검사한다

DB 테이블 생성
- 인증번호 테이블(CERT) 생성
** who를 PK로 하면 한 사람에게 무조건 하나의 인증번호만 발급할 수 있다
** who와 serial을 PK(복합키)로 하면 한 사람에게 여러 개의 서로 다른 인증번호를 발급할 수 있다
create table cert (
who varchar2(60) not null primary key,
serial char(6) not null,
when date default sysdate not null
);
CertDto
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CertDto {
private String who; // 누구에게
private String serial; // 인증번호
private String when; // 언제
}
1. 인증번호 발급 및 인증메일 전송
cert-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cert">
<!-- 인증번호 등록 -->
<insert id = "insert" parameterType = "CertDto">
insert into cert(who, serial) values(#{who}, #{serial})
</insert>
<!-- 인증번호 삭제 -->
<delete id = "delete" parameterType = "String">
delete cert where who = #{who}
</delete>
</mapper>
CertDao
public interface CertDao {
// 추상 메소드 - 인증번호 등록
void insert(CertDto certDto);
// 추상 메소드 - 인증번호 삭제
boolean delete(String who);
}
CertDaoImpl
@Repository
public class CertDaoImpl implements CertDao {
// 의존성 주입
@Autowired
private SqlSession sqlSession;
// 추상 메소드 - 인증번호 등록
@Override
public void insert(CertDto certDto) {
sqlSession.insert("cert.insert", certDto);
}
// 추상 메소드 - 인증번호 삭제
@Override
public boolean delete(String who) {
int count = sqlSession.delete("cert.delete", who);
return count > 0;
}
}
CreateCertTest
@SpringBootTest
public class CreateCertTest {
// 이메일 전송을 위한 의존성 주입
@Autowired
private JavaMailSender javaMailSender;
// DB 접근을 위한 의존성 주입
@Autowired
private CertDao certDao;
// 테스트용 이메일
String email = "abcd@gmail.com";
@Test
public void test() {
// 1. 6자리 랜덤인증번호 생성
// - 인증번호(난수) 생성 범위 - 10의 size 제곱
int range = (int)Math.pow(10, size);
// - 해당 범위에서 인증번호 생성
int number = r.nextInt(range);
System.out.println("number = " + number);
// - 0이 나열된 문자열 생성 (인증번호 자릿수 조정을 위함)
StringBuffer buffer = new StringBuffer();
for(int i = 0 ; i < size ; i ++) {
buffer.append("0");
}
// - 0이 나열된 문자열을 pattern으로 하는 Format 클래스의 인스턴스 생성
Format f = new DecimalFormat(buffer.toString());
// - 인증번호를 지정된 pattern 형태의 문자열 변환
String serial = f.format(number);
// 2. 이메일 발송
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("[KH정보교육원] 이메일 인증번호입니다");
message.setText("인증번호 : " + serial);
javaMailSender.send(message);
// 3. 데이터베이스 등록
// - 이전에 해당 이메일로 보냈던 인증번호 정보 삭제
certDao.delete(email);
// - 새로 발급된 인증번호 정보 등록
CertDto certDto = CertDto.builder().who(email).serial(serial).build();
certDao.insert(certDto);
}
}
2. 인증번호 검사
cert-mapper.xml
- oracle에서 날짜 계산의 기본 단위는 1일이다 (날짜 계산에서 1은 1일을 의미)
** 1을 24로 나누면 1시간
** 1을 24로 나눈 후 60으로 나누면 1분
** 5분을 나타내는 값은 5/24/60이 된다
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cert">
<!-- 인증번호 조회 -->
<select id = "check" parameterType = "CertDto" resultType = "CertDto">
select * from cert
where
who = #{who}
and
serial = #{serial}
and
when >= sysdate - 5/24/60
</select>
</mapper>
CertDao
package com.kh.spring18.repository;
import com.kh.spring18.entity.CertDto;
public interface CertDao {
// 추상 메소드 - 인증번호 조회(5분 이내 발급된 인증번호인지)
boolean check(CertDto certDto);
}
CertDaoImpl
@Repository
public class CertDaoImpl implements CertDao {
// 의존성 주입
@Autowired
private SqlSession sqlSession;
// 추상 메소드 - 인증번호 조회(5분 이내 발급된 인증번호인지)
@Override
public boolean check(CertDto certDto) {
CertDto result = sqlSession.selectOne("cert.check", certDto);
return result != null;
}
}
CheckCertTest
- 테스트용 데이터에서 해당 이메일로 5분이내 발급된 해당 인증번호가 존재(유효)하는지를 단일조회
- 해당 인증번호가 있다면 인증 성공 및 해당 인증번호 삭제
@SpringBootTest
public class CheckCertTest {
// DB 접근을 위한 의존성 주입
@Autowired
private CertDao certDao;
// 테스트를 위한 인증번호 정보
String email = "abcd@gmail.com";
String serial = "849892";
@Test
public void test() {
// 주어진 정보(email, serial)로 certDto 설정
CertDto certDto = CertDto.builder().who(email).serial(serial).build();
// 설정된 certDto로 인증번호 조회(5분 이내 발급된 인증번호인지)
boolean result = certDao.check(certDto);
// 5분 이내 발급된 인증번호인지 여부 판정
if(result) { // 5분 이내 발급된 인증번호라면(인증 성공)
System.out.println("인증 성공");
certDao.delete(email);
}
else { // 그렇지 않다면(인증 실패)
System.out.println("인증 실패");
}
}
}
** 인증번호 생성의 Component화
- 인증번호의 자릿수를 유동적으로 조정하기 위해 추상화 구조(Dao, DaoImpl)로 클래스를 작성한다
- StringBuffer로 자릿수만큼 0을 붙인 문자열을 반환하여 Format 클래스의 생성자에 들어갈 pattern으로 사용한다
- 생성된 인증번호를 Format 클래스의 pattern 형태의 문자열로 반환한다
RandomGenerator
public interface RandomGenerator {
// 추상 메소드 - 자릿수에 해당하는 숫자를 받아 인증번호를 생성
String generateSerial(int size);
}
RandomGeneratorImpl
@Component
public class RandomGeneratorImpl implements RandomGenerator {
// Random 클래스의 인스턴스 생성
private Random r = new Random();
// 추상 메소드 오버라이딩 - 자릿수에 해당하는 숫자를 받아 인증번호를 생성
@Override
public String generateSerial(int size) {
// 인증번호(난수) 생성 범위 - 10의 size 제곱
int range = (int)Math.pow(10, size);
// 해당 범위에서 인증번호 생성
int number = r.nextInt(range);
System.out.println("number = " + number);
// 0이 나열된 문자열 생성 (인증번호 자릿수 조정을 위함)
StringBuffer buffer = new StringBuffer();
for(int i = 0 ; i < size ; i ++) {
buffer.append("0");
}
// 0이 나열된 문자열을 pattern으로 하는 Format 클래스의 인스턴스 생성
Format f = new DecimalFormat(buffer.toString());
// 인증번호를 지정된 pattern 형태의 문자열 변환
String serial = f.format(number);
// 생성된 인증번호 문자열 반환
return serial;
}
}
1. 인증번호 발송 및 등록의 Service화
- 인증번호 생성, 인증메일 발송, 이전에 발급된 인증번호 삭제, 새로 발급된 인증번호 등록을 한번에 처리
EmailService
public interface EmailService {
// 추상 메소드 - 인증번호 발급 및 등록
void sendCertMail(String email);
}
GmailService
@Service
public class GmailService implements EmailService {
// 의존성 주입 - 인증번호 생성
@Autowired
private RandomGenerator randomGenerator;
// 의존성 주입 - 인증메일 전송
@Autowired
private JavaMailSender javaMailSender;
// 의존성 주입 - 발급한 인증번호 등록/삭제/조회
@Autowired
private CertDao certDao;
// 추상 메소드 오버라이딩 - 인증번호 발급 및 등록
@Override
public void sendCertMail(String email) {
// 1) 6자리 랜덤인증번호 생성
String serial = randomGenerator.generateSerial(6);
// 2) 이메일 발송
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("[KH정보교육원] 이메일 인증번호입니다");
message.setText("인증번호 : " + serial);
javaMailSender.send(message);
// 3) 데이터베이스 등록
// - 이전에 해당 이메일로 보냈던 인증번호 정보 삭제
certDao.delete(email);
// - 새로 발급된 인증번호 정보 등록
CertDto certDto = CertDto.builder().who(email).serial(serial).build();
certDao.insert(certDto);
}
}
2. 인증번호 검사의 Service화
- 입력된 인증번호 정보로 단일 조회를 하여 인증 성공 / 실패에 따라 다른 값을 반환
EmailService
public interface EmailService {
// 추상 메소드 - 인증번호 검사
boolean checkCert(CertDto certDto);
}
GmailService
@Service
public class GmailService implements EmailService {
// 의존성 주입 - 발급한 인증번호 등록/삭제/조회
@Autowired
private CertDao certDao;
// 추상 메소드 오버라이딩 - 인증번호 검사
@Override
public boolean checkCert(CertDto certDto) {
if(certDao.check(certDto)) { // 5분이내 발급된 인증번호가 존재하면(인증 성공)
certDao.delete(certDto.getWho()); // 인증번호 삭제
return true;
}
return false;
}
}
TestController
- 인증 메일 전송 페이지(test1.jsp)에서 <form>으로 이메일 정보(who)를 입력받는다
- 인증번호 입력 페이지(test2.jsp)에서 <form>으로 이메일 정보(who)와 인증번호(serial)을 입력받는다
(단, 이메일 정보는 hidden 형태로 하여 페이지에서 보이지 않도록 한다)
- 인증번호 입력 페이지(test2.jsp)에서 입력받은 인증번호 정보(certDto)로 단일조회하여 인증성공 여부를 판정
@Controller
public class TestController {
// 의존성 주입 - 이메일 전송
@Autowired
private EmailService emailService;
// 이메일 인증 메일 발송 Mapping
@RequestMapping("/test1")
public String test1() {
// 인증 메일 전송 페이지(test1.jsp)로 연결
return "test1";
}
// 인증메일 전송 및 DB 등록 Mapping
@RequestMapping("/test2")
public String test2(@RequestParam String who, Model model) {
// 인증메일 전송 및 해당 인증번호에 대한 정보 등록
emailService.sendCertMail(who);
// 이메일을 Model에 첨부
model.addAttribute("who", who);
// 인증번호 입력 페이지(test2.jsp)로 연결
return "test2";
}
// 이메일 인증번호 등록 및 인증확인 Mapping
@RequestMapping("/test3")
public String test3(@ModelAttribute CertDto certDto, RedirectAttributes attr) {
// 해당 인증번호가 존재하며 5분 이내 인증번호인지 여부
boolean result = emailService.checkCert(certDto);
if(result) { // 인증 성공시
// 인증 성공 Mapping으로 강제 이동(redirect)
return "redirect:test4";
}
else { // 인증 실패시
// 인증번호 입력 Mapping으로 강제 이동(redirect)
attr.addAttribute("who", certDto.getWho());
return "redirect:test2?error";
}
}
// 이메일 인증 성공 Mapping
@RequestMapping("/test4")
public String test4() {
// 인증 성공 페이지(test4.jsp)로 연결
return "test4";
}
}
test1.jsp
- 인증메일 전송 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>이메일 인증</h1>
<form action = "test2" method = "get">
이메일 : <input type = "text" name = "who" required>
<button type = "submit">확인</button>
</form>
test2.jsp
- 인증번호 입력 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>인증번호 입력</h1>
<form action = "test3" method = "get">
<input type = "hidden" name = "who" value = "${who}">
인증번호 : <input type = "text" name = "serial" required>
<button type = "submit">확인</button>
</form>
test4.jsp
- 인증 성공 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>인증 완료!</h1>
비동기 통신을 이용한 단일 페이지 내에서의 이메일 인증 구현
AsyncController
@Controller
public class AsyncController {
// 의존성 주입 - 이메일 전송
@Autowired
private EmailService emailService;
// 인증 페이지 Mapping
@RequestMapping("/async1")
public String async1() {
// 인증 페이지(async.jsp)로 연결
return "async1";
}
// 인증메일 전송 및 DB 등록 Mapping
@PostMapping("/async2")
@ResponseBody
public void async2(@RequestParam String who) {
// 인증메일 전송 및 해당 인증번호에 대한 정보 등록
emailService.sendCertMail(who);
}
// 인증번호 검사
@PostMapping("/async3")
@ResponseBody
public boolean async3(@ModelAttribute CertDto certDto) {
// 해당 인증번호가 존재하며 5분 이내 인증번호인지 검사 및 결과 반환
return emailService.checkCert(certDto);
}
}
async1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script src = "http://code.jquery.com/jquery-3.6.1.min.js"></script>
<script>
$(function(){
// 판정 객체 - 인증이 성공할 경우에만 true로 변경
var judge = {
emailValid : false,
};
// 확인 버튼에 대한 click 이벤트 설정
$(".send-btn").click(function(){
// memberEmail이라는 name을 가진 요소의 value를 변수 email로 지정
var email = $("[name=memberEmail]").val();
// email의 길이가 0이면(입력되지 않았으면) return
if(email.length == 0) return;
// 확인 버튼을 변수 btn으로 지정
var btn = $(this);
// 확인 버튼을 누르면 중복입력을 방지하기 위해 비활성화 설정
btn.prop("disabled", true);
// 화인 버튼을 클릭할 때 발급된 인증 번호를 등록하기 위한 비동기 통신
$.ajax({
url:"${pageContext.request.contextPath}/async2",
method : "post",
data : {who : email},
success : function(){
// 성공했다면 메일은 전송되었다고 볼 수 있다
btn.prop("disabled", false);
// 인증번호 입력창 태그 재구성을 위한 변수 지정
// <div> 태그를 변수 div로 지정
var div = $("<div>");
// <input> 태그를 변수 input으로 지정
var input = $("<input>");
// <button> 태그를 변수 button으로 지정
// - type을 "button"으로, 태그 사이의 text를 "검사"로 지정
var button = $("<button>").attr("type", "button").text("검사");
// div에 input와 button 추가
div.append(input).append(button);
// 클래스명이 cert인 <div> 태그 안에 html(innerHtml)로 태그 생성
$(".cert").html(div);
// 검사 버튼에 대한 click 이벤트 설정
button.click(function(){
// input 태그에 입력되어있는 값을 변수 serial로 지정
var serial = input.val();
// 만약 serial의 길이가 6이 아니라면(비정상 입력) return
if(serial.length != 6) return;
// 인증번호 확인에 대한 ajax
$.ajax({
url:"${pageContext.request.contextPath}/async3",
method:"post",
data : {who : email, serial : serial},
success:function(resp){
// checkCert(CertDto certDto)의 결과를
// judge의 emailValid 값으로 저장
judge.emailValid = resp;
// 더이상 인증메일을 보내지 못하도록
// 확인 버튼(인증메일 전송 버튼)을 비활성화
btn.prop("disabled", true);
}
});
});
}
});
});
// form이 전송될 때 판정 객체(judge)의 상태가 어떤지 출력
$(".join-form").submit(function(){
console.log(judge);
return false;
});
});
</script>
<h1>회원가입</h1>
<form class = "join-form">
이메일 : <input type = "text" name = "memberEmail">
<button class = "send-btn">확인</button>
<div class = "cert"></div>
<button type = "submit">가입</button>
</form>
MIME(Multipurpose Internet Message Extensions) 메시지 전송
MimeMessageHelper
- MimeMessageHelper의 생성자 중 일부
MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding) |
** multipart 여부, encoding 방식을 추가로 설정할 수 있다
- MimeMessageHelper의 메소드 중 일부
void | setFrom(String from) | 보낸 사람(from) 설정 |
void | setTo(String to) | 받는 사람(to) 설정 |
void | setSubject(String subject) | 제목(subject) 설정 |
void | setText(String text) | 내용(text) 설정 |
void | setText(String text, boolean html) | 내용(text) 설정 + html 태그 인식 여부 |
void | addAttachment(String attachmentFileName, File file) | 해당 파일명으로 첨부파일 추가 |
MimeMessageTest1
- setText 메소드의 boolean html이 true이면 html 태그를 자동으로 인식하여 내용에 태그를 적용한다
@SpringBootTest
public class MineMessageTest1 {
// 의존성 주입 - 이메일 전송
@Autowired
private JavaMailSender javaMailSender;
@Test
public void test() throws MessagingException {
// 1) MimeMessage의 인스턴스 생성
MimeMessage message = javaMailSender.createMimeMessage();
// 2) MimeMessageHelper의 인스턴스 생성
// - 생성자의 매개변수는 순서대로 MimeMessage,
MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8");
// 3) 정보 설정
helper.setTo("eomhyunyoung@gmail.com");
helper.setSubject("마임메세지 테스트");
// - html 태그를 적용하지 않을 경우
// helper.setText("<h1>안녕</h1>");
// - html 태그를 적용할 경우
helper.setText("<h1>안녕</h1>", true);
// 4) Mime 메시지 전송
javaMailSender.send(message);
}
}
MimeMessageTest2
- Mime 메시지에 첨부파일 추가
@SpringBootTest
public class MineMessageTest2 {
// 의존성 주입 - 이메일 전송
@Autowired
private JavaMailSender javaMailSender;
@Test
public void test() throws MessagingException {
// 1) MimeMessage의 인스턴스 생성
MimeMessage message = javaMailSender.createMimeMessage();
// 2) MimeMessageHelper의 인스턴스 생성
// - 파일 첨부시 multipart를 true로 한다
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
// 3) 정보 설정
helper.setTo("eomhyunyoung@gmail.com");
helper.setSubject("첨부파일 테스트");
helper.setText("첨부파일을 확인해주세요");
// ** 첨부파일 설정
// - File 클래스의 인스턴스 생성
File target = new File("D:\\", "attachmentTest.jpg");
// - File 클래스의 인스턴스로 FileDataSource의 인스턴스 생성 후 DataSource로 업 캐스팅
DataSource source = new FileDataSource(target);
// - MimeMessageHelper에 해당 DataSource를 원본 파일명으로 첨부파일에 추가
helper.addAttachment(target.getName(), source);
// 4) Mime 메시지 전송
javaMailSender.send(message);
}
}
MimeMessageTest4
- Spring 프로젝트 내 resource로 저장된 html 파일 전송
- 특정 위치에 입력되어야할 값을 문자열 대체(replace)로 해결하는 방법
@SpringBootTest
public class MineMessageTest4 {
// 의존성 주입 - 이메일 전송
@Autowired
private JavaMailSender javaMailSender;
@Test
public void test() throws MessagingException, FileNotFoundException, IOException {
// 1) MimeMessage의 인스턴스 생성
MimeMessage message = javaMailSender.createMimeMessage();
// 2) MimeMessageHelper의 인스턴스 생성
MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8");
// 3) 정보 설정
helper.setTo("eomhyunyoung@gmail.com");
helper.setSubject("HTML 템플릿 테스트");
// ** Spring 프로젝트 내 Resource 파일 읽기
ClassPathResource resource = new ClassPathResource("email/template2.html");
// ** 해당 Resource 파일의 내용을 StringBuffer로 연결
StringBuffer buffer = new StringBuffer();
try(Scanner sc = new Scanner(resource.getFile())){
while(sc.hasNextLine()) {
buffer.append(sc.nextLine());
}
}
String text = buffer.toString();
// MimeMessageHelper의 text로 설정하기 전 {{name}}과 {{address}}의 값 대체
text = text.replace("{{name}}", "eomhyunyoung");
text = text.replace("{{address}}", "http://localhost:8888/async1");
// 내용(text)에 들어갈 값 및 html 적용여부 설정
helper.setText(text, true);
// 4) Mime 메시지 전송
javaMailSender.send(message);
}
}
MimeMessageTest5
- Spring 프로젝트 내 resource로 저장된 html 파일 전송
- Jsoup을 이용하여 문자열을 대체하는 방법
** Jsoup 의존성 추가 - https://mvnrepository.com/artifact/org.jsoup/jsoup/1.15.3
@SpringBootTest
public class MineMessageTest5 {
// 의존성 주입 - 이메일 전송
@Autowired
private JavaMailSender javaMailSender;
@Test
public void test() throws MessagingException, FileNotFoundException, IOException {
// 1) MimeMessage의 인스턴스 생성
MimeMessage message = javaMailSender.createMimeMessage();
// 2) MimeMessageHelper의 인스턴스 생성
MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8");
// 3) 정보 설정
helper.setTo("eomhyunyoung@gmail.com");
helper.setSubject("Jsoup 변환 테스트");
// ** Spring 프로젝트 내 Resource 파일 읽기
ClassPathResource resource = new ClassPathResource("email/template3.html");
// ** 해당 Resource 파일의 내용을 StringBuffer로 연결
StringBuffer buffer = new StringBuffer();
try(Scanner sc = new Scanner(resource.getFile())){
while(sc.hasNextLine()) {
buffer.append(sc.nextLine());
}
}
String text = buffer.toString();
// StringBuffer로 생성한 문자열을 HTML로 분석(parse)
Document doc = Jsoup.parse(text);
// doc에서 'user-name'이라는 id를 가진 요소를 선택
Element element = doc.getElementById("user-name");
// 선택한 요소에 해당 문자열을 설정(대체)
element.text("eomhyunyoung");
// doc에서 'user-url'이라는 id를 가진 요소를 선택
Element link = doc.getElementById("return-url");
// 선택한 요소에 해당 이름의 속성(attribute)과 값을 설정
link.attr("href", "http://localhost:8888/async1");
// 내용(text)에 들어갈 값 및 html 적용여부 설정
helper.setText(doc.toString(), true);
// 4) Mime 메시지 전송
javaMailSender.send(message);
}
}
MimeMessageTest6
- Spring 프로젝트 내 resource로 저장된 html 파일 전송
- Jsoup을 이용하여 문자열을 대체하는 방법
- 주소를 자동으로 생성해주는 ServletUriComponentsBuilder 클래스 이용
@SpringBootTest
public class MineMessageTest6 {
// 의존성 주입 - 이메일 전송
@Autowired
private JavaMailSender javaMailSender;
@Test
public void test() throws MessagingException, FileNotFoundException, IOException {
// 1) MimeMessage의 인스턴스 생성
MimeMessage message = javaMailSender.createMimeMessage();
// 2) MimeMessageHelper의 인스턴스 생성
MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8");
// 3) 정보 설정
helper.setTo("eomhyunyoung@gmail.com");
helper.setSubject("Jsoup 변환 테스트");
// ** Spring 프로젝트 내 Resource 파일 읽기
ClassPathResource resource = new ClassPathResource("email/template3.html");
// ** 해당 Resource 파일의 내용을 StringBuffer로 연결
StringBuffer buffer = new StringBuffer();
try(Scanner sc = new Scanner(resource.getFile())){ // scanner 자동 close
while(sc.hasNextLine()) {
buffer.append(sc.nextLine());
}
}
String text = buffer.toString();
// StringBuffer로 생성한 문자열을 HTML로 분석(parse)
Document doc = Jsoup.parse(text);
// doc에서 'user-name'이라는 id를 가진 요소를 선택
Element element = doc.getElementById("user-name");
// 선택한 요소에 해당 문자열을 설정(대체)
element.text("eomhyunyoung");
// doc에서 'user-url'이라는 id를 가진 요소를 선택
Element link = doc.getElementById("return-url");
// http로 시작하는 전체 주소 자동 생성 도구
// (주의) 서버에서 실행될 때만 정상적으로 구동(테스트에서는 80번 포트로 인식, 실제로는 프로젝트가 실행되는 포트 번호로 인식됨)
String url = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/async1")
.toUriString();
// 선택한 요소에 해당 이름의 속성(attribute)과 값을 설정
link.attr("href", url);
// 내용(text)에 들어갈 값 및 html 적용여부 설정
helper.setText(doc.toString(), true);
// 4) Mime 메시지 전송
javaMailSender.send(message);
}
}
Scheduler를 이용하여 기간이 만료된 인증번호 삭제
Schedular
- 일장 시간간격마다 또는 일정 시각(설정 가능)에 특정 작업을 반복하도록 하는 설정
** Cron 표현식
- Scheduler 시간을 설정할 때 사용
- 총 7개의 필드로 구성되어 있으며 년도는 생략 가능
<초> <분> <시> <일> <월> <요일> <년도>
필드 | 허용되는 값 | 허용되는 특수 문자 |
초 | 0-59 | , - * / |
분 | 0-59 | , - * / |
시 | 0-23 | , - * / |
일 | 1-31 | , - * / L W |
월 | 1-12 또는 JAN ~ DEC | , - * / |
요일 | 0-6 또는 SUN ~ SAT | , - * / L # |
년도 | 1970 ~ 2099 | , - * / |
- 특수 문자
* | 모든 값 |
? | 특정 값이 없음 |
- | 범위 지정 |
, | 여러 값을 지정 |
/ | 증가하는 값을 지정 |
L | 마지막 값을 지정 |
W | 가까운 평일 |
# | 몇 번째 무슨 요일인지 지정 |
- 예시
* * * * * * | 매 초마다 |
*/1 * * * * * | 매 1초마다 |
*/2 * * * * * | 매 2초마다 |
10,20 * * * * * | 매 분 10초, 20초마다 |
0 * * * * * | 매 분마다 |
0 */1 * * * * | 매 1분마다 |
0 */5 * * * * | 매 5분마다 |
0 0 * * * * | 매 시간마다 |
0 0 */2 * * * | 매 2시간마다 |
0 0 6 * * W | 매 주 수요일 아침 6시 정각마다 |
0 0 6 L * ? | 매 월 마지막날 아침 6시 정각마다 |
0 0 6 ? * 4L | 매 월 마지막 수요일 아침 6시 정각마다 |
0 0 6 ? * 4#3 | 매 월 3주차 수요일 아침 6시 정각마다 |
cert-mapper.xml
- 5분이 지난 인증 정보 삭제
- XML에서 '<'는 태그 시작을 나타내므로 문자열로 인식하도록 하기 위해서는 <![CDATA[ ]]> 를 사용한다
** <![CDATA[ ]]> : (Unparsed) Character Data, 즉, 파싱하지 않는 문자 데이터
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cert">
<!-- 5분 이상 지난 인증정보 삭제 -->
<delete id = "clear">
<![CDATA[delete cert where when < sysdate - 5/24/60]]>
</delete>
</mapper>
CertDao
- 5분이 지난 인증 정보 삭제
public interface CertDao {
// 추상 메소드 - 만료된 인증정보 삭제
void clear();
}
CertDaoImpl
- 5분이 지난 인증 정보 삭제
@Repository
public class CertDaoImpl implements CertDao {
// 의존성 주입
@Autowired
private SqlSession sqlSession;
// 추상 메소드 오버라이딩 - 만료된 인증정보 삭제
@Override
public void clear() {
sqlSession.delete("cert.clear");
}
}
Spring18emailApplication.java
- @EnableScheduling : Scheduler를 사용하기 위한 설정
@EnableScheduling // 스케쥴러 사용 설정
@SpringBootApplication
public class Spring18emailApplication {
public static void main(String[] args) {
SpringApplication.run(Spring18emailApplication.class, args);
}
}
** 프로젝트명 + Application.java 파일의 위치

SchedulerService
- 만료된 인증정보 삭제
public interface SchedulerService {
// 추상 메소드 - 만료된 인증정보 삭제
void clearCert();
}
SchedulerServiceImpl
- 만료된 인증정보 삭제
- @Scheduled에 설정된 시간마다 자동으로 지정된 메소드를 실행
@Service
public class SchedularServiceImpl implements SchedulerService {
// 의존성 주입
@Autowired
private CertDao certDao;
// 추상 메소드 오버라이딩 - 만료된 인증정보 삭제
@Scheduled(cron = "* * * * * *") // 매초 매분 매시 매일 매월 매요일
@Override
public void clearCert() {
certDao.clear();
}
}
'국비교육 > 국비교육' 카테고리의 다른 글
day75 - 1114 (0) | 2022.11.14 |
---|---|
day73 - 1110 (0) | 2022.11.12 |
day71 - 1108 (0) | 2022.11.08 |
day70 - 1107 (0) | 2022.11.07 |
day45 - 0928 (0) | 2022.09.28 |