STS 회원가입 : Email 인증
필요 라이브러리

Email 관련 설정을 위해 xml 파일 생성 필요

resources 에서 new -> Spring Bean Configuration File -> email.context.xml

여기서 username 에 google email을 입력하고 value에는 google 계정관리 -> 보안
-> 2단계 인증 -> 맨 밑에 앱 비밀번호 16자리 띄어쓰기 없이 입력
이후 web.xml 에서 경로 설정

// --------------------- 이메일 인증 ---------------------
// 인증번호 발송
// 인증번호를 발송한 이메일 저장
let tempEmail;
sendAuthKeyBtn.addEventListener("click", function(){
authMin = 4;
authSec = 59;
checkObj.authKey = false;
console.log(checkObj.memberEmail)
if(checkObj.memberEmail){ // 중복이 아닌 이메일인 경우
/* fetch() API 방식 ajax */
fetch("/sendEmail/signUp?email="+memberEmail.value)
.then(resp => resp.text())
.then(result => { /* 0 or 1 반환 */
if(result > 0){
console.log("인증 번호가 발송되었습니다.")
tempEmail = memberEmail.value;
}else{
console.log("인증번호 발송 실패")
}
})
.catch(err => {
console.log("이메일 발송 중 에러 발생");
console.log(err);
});
alert("인증번호가 발송 되었습니다.");
authKeyMessage.innerText = "05:00";
authKeyMessage.classList.remove("confirm");
authTimer = window.setInterval(()=>{
// 삼항연산자 : 조건 ? true : false
authKeyMessage.innerText = "0" + authMin + ":" + (authSec < 10 ? "0" + authSec : authSec);
// 남은 시간이 0분 0초인 경우
if(authMin == 0 && authSec == 0){
checkObj.authKey = false;
clearInterval(authTimer);
return;
}
// 0초인 경우
if(authSec == 0){
authSec = 60;
authMin--;
}
authSec--; // 1초 감소
}, 1000)
} else{
alert("중복되지 않은 이메일을 작성해주세요.");
memberEmail.focus();
}
});
중복되지 않는 email일 경우 /sendEmail/signUp 주소에 입력받은 email을 보낸다
@Controller
@RequestMapping("/sendEmail")
public class EmailController {
@Autowired
private EmailService service;
@GetMapping("/signUp")
@ResponseBody
public int signUp(String email) {
return service.signUp(email, "회원 가입");
}
}
js에서 전송한 주소에 맞게 설정하고 email 을 전달받아 service 에 전송
@Service
public class EmailServiceImpl implements EmailService{
@Autowired
private EmailDAO dao;
@Autowired
private JavaMailSender mailSender;
private String fromEmail = "송신자 이메일";
private String fromUsername = "송신자 이름";
// 6자리 난수생성 메서드
public String createAuthKey() {
String key = "";
for(int i=0 ; i< 6 ; i++) {
int sel1 = (int)(Math.random() * 3); // 0:숫자 / 1,2:영어
if(sel1 == 0) {
int num = (int)(Math.random() * 10); // 0~9
key += num;
}else {
char ch = (char)(Math.random() * 26 + 65); // A~Z
int sel2 = (int)(Math.random() * 2); // 0:소문자 / 1:대문자
if(sel2 == 0) {
ch = (char)(ch + ('a' - 'A')); // 대문자로 변경
}
key += ch;
}
}
return key;
}
@Transactional //Transactional 어노테이션이 붙은 메서드에서 예외가 발생하면 자 동으로 rollback
@Override
public int signUp(String email, String title) {
// 6자리 난수 인증번호 생성
String authKey = createAuthKey();
try {
//인증메일 보내기 -> API 사용
MimeMessage mail = mailSender.createMimeMessage();
// 제목
String subject = "[Board Project]"+title+" 인증코드";
// 문자 인코딩
String charset = "UTF-8";
// 메일 내용
String mailContent
= "<p>Board Project "+title+" 인증코드입니다.</p>"
+ "<h3 style='color:blue'>" + authKey + "</h3>";
// 송신자(보내는 사람) 지정
mail.setFrom(new InternetAddress(fromEmail, fromUsername));
// 수신자(받는사람) 지정
mail.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
// 이메일 제목 세팅
mail.setSubject(subject, charset);
// 내용 세팅
mail.setText(mailContent, charset, "html");
//"html" 추가 시 HTML 태그가 해석됨 -> 반드시 필요!
mailSender.send(mail);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
Map<String, String> map = new HashMap<String, String>();
map.put("authKey", authKey);
map.put("email", email);
System.out.println(map);
int result = dao.updateAuthKey(map);
// 인증번호를 이전에 받은 적이 없다면
if(result == 0) {
result = dao.insertAuthKey(map);
}
return result;
}
}
난수 생성 메서드를 통해 6자리 난수를 생성하고 자바 메일 api 를 이용해 입력한 email 주소로
이메일을 발송하고 매개변수로 입력받은 email 과 생성된 난수 authKey를 가지고 map을 생성한 뒤 DAO 매서드에 전달한다
맨 밑부분 result 부분 : 인증번호의 경우 해당 이메일로 이전에 인증번호를 받았을 수도 있고 이번이 처음일 수도 있다. 따라서 해당 이메일로 인증번호를 받은 적이 있으면 update, 없으면 insert를 수행하는 구문이다.
@Repository
public class EmailDAO {
@Autowired
private SqlSessionTemplate sqlSession;
public int updateAuthKey(Map<String, String> map) {
return sqlSession.update("emailMapper.updateAuthKey", map);
}
public int insertAuthKey(Map<String, String> map) {
return sqlSession.insert("emailMapper.insertAuthKey",map);
}
}
update, insert 에 해당하는 메서드로 만약 이전에 인증번호를 받은 적이 없다면 update 에서 0이 반환되어 insert 메서드를 수행할 것이다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace = "emailMapper">
<!-- UPDATE,INSERT 는 반환타입 무조건 INT 라 resultType 필요없음 -->
<update id="updateAuthKey">
UPDATE "AUTH_KEY" SET
CODE = #{authKey},
CREATE_TIME = sysdate
WHERE EMAIL = #{email}
</update>
<insert id="insertAuthKey">
INSERT INTO "AUTH_KEY" VALUES(SEQ_AUTH_KEY_NO.NEXTVAL, #{authKey}, #{email}, DEFAULT)
</insert>
</mapper>
전달받은 map 의 authKey와 email을 가지고 구문을 수행한 뒤 결과를 반환한다.
update,insert의 경우 반환타입이 무조건 _int 이기 때문에 resultType 작성 안해도 된다.
STS 에서 Commit은 자동으로 수행하지만 rollback은 @Transactinal 어노테이션이 필요하다
servlet-context.xml
<!-- @Transactinal 어노테이션 인식, 활성화 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
service
@Transactional //Transactional 어노테이션이 붙은 메서드에서
예외가 발생하면 자동으로 rollback, rollback이 필요한 메서드에 사용