Today I Learned 1010

doyeon kim·2023년 10월 10일

Spring Framework

목록 보기
5/11

STS 회원가입 : Email 인증

필요 라이브러리

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

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


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

이후 web.xml 에서 경로 설정

인증번호 보내기 (JS code)

// --------------------- 이메일 인증 ---------------------

// 인증번호 발송
// 인증번호를 발송한 이메일 저장
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

@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

@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를 수행하는 구문이다.

DAO

@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 메서드를 수행할 것이다.

sql

<?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 작성 안해도 된다.

**Transactional : Rollback

STS 에서 Commit은 자동으로 수행하지만 rollback은 @Transactinal 어노테이션이 필요하다

servlet-context.xml

<!-- @Transactinal 어노테이션 인식, 활성화 -->
	<tx:annotation-driven  transaction-manager="transactionManager"/>

service

 @Transactional  //Transactional 어노테이션이 붙은 메서드에서 
 예외가 발생하면 자동으로 rollback, rollback이 필요한 메서드에 사용

0개의 댓글