본문 바로가기

Dot Programming/Spring Clone

[스프링 웹앱 프로젝트 #17] 가입 확인 이메일 재전송

17. 가입 확인 이메일 재전송 기능

구현한 로직
 > 가입 확인 이메일을 재전송할 수 있는 기능 제공
 > 하지만, 너무 자주 이메일을 전송할 경우 리소스를 낭비할 수 있다는 문제가 있음
 > 보완책으로, 1시간에 한번만 인증 메일을 전송할 수 있도록 제한한다.

GET "/check-email"
 > 가입 확인 이메일을 전송한 이메일 주소 ( == 가입할 때 입력한 이메일 주소)를 화면에 보여줌
 > 재전송 버튼 보여주기
 > 재전송 버튼 클릭하면 GET "/resend-confirm-email" 요청 전송

GET "/resend-confirm-email"
 > 인증 메일을 다시 전송할 수 있는지 확인한 뒤에
 > 보낼 수 있으면 전송하고, 첫 페이지로 리다이렉트
 > 보낼 수 없으면 에러 메시지를 모델에 담아주고 이메일 확인 페이지 다시 보여주기

 

이메일 인증되었는지 확인하기

AccountController.java

  /**
     * 이메일 인증되었는지 확인
     */
    @GetMapping("/check-email")
    public String checkEmail(@CurrentUser Account account, Model model){
        model.addAttribute("email", account.getEmail());
        return "account/check-email";
    }
    
    
 /**
     * 인증 이메일 다시 보내기
     * 10분에 1번만 재전송 가능
     */
    @GetMapping("/resend-confirm-email")
    public String resendConfirmEmail(@CurrentUser Account account, Model model){
        if(!account.canSendConfirmEmail()){
            model.addAttribute("error", "인증 이메일은 1시간에 한번만 전송할 수 있습니다.");
            model.addAttribute("email", account.getEmail());
            return "account/check-email";
        }

        accountService.sendSignUpConfirmEmail(account);
        return "redirect:/";
    }

 

"/resned-confirm-email" 리턴값을 Get메소드임에도 불구하고 리다이렉트로 설정한 이유는 해당 화면에서 refresh할 때마다 인증 이메일을 무한 재전송하는 일이 벌어질 수 있기 때문이다.

(POST : sign-up도 폼 재전송이 일어나지 않도록 마찬가지로 리다이렉트 설정)

 

인증 이메일 재전송이 계속 가능하게 냅두면 악의적으로 트래픽 증가를 시킬 수도 있고 SMTP서버에서 스팸으로 차단을 당할 우려 같은 것들이 있기 때문에 제한을 걸어두는 것이 좋다.

 

Account.java


// 10초에 한 번 전송 가능
    public boolean canSendConfirmEmail() {
        return this.emailCheckTokenGenerateAt.isBefore(LocalDateTime.now().minusSeconds(10));

    }

//  전송할 때 마다 시간과 토큰값 재설정
    public void resetGeneratedTokenAndTime() {
        this.emailCheckToken = UUID.randomUUID().toString();
        this.emailCheckTokenGenerateAt = LocalDateTime.now();
    }

 

reset은 entity값이 바뀌는 것이기 때문에 이메일을 전송해주는 service단에서 처리했다

 

AccountService.java

    public void sendSignUpConfirmEmail(Account newAccount) {
        //이메일 전송
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("닷 스터디, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token="+ newAccount.getEmailCheckToken() +
                "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);

        //이메일 토큰 내용과 시간 다시 생성
        newAccount.generateEmailCheckToken();
    }

 

 

뷰 작성

check-email.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace ="fragments.html :: head"></head>

<body class="bg-light">
<nav th:replace="fragments.html :: main-nav"></nav>



    <div class = "container">

<!--       에러메시지 화면-->
        <div class = "py-5 text-center" th:if="${error != null}">
            <p class="lead">닷스터디 가입</p>
            <div class ="alert alert-danger" role="alert" th:text="${error}"></div>
            <p class="lead" th:text="${email}">example@email.com</p>
        </div>

<!--        일반 화면-->
        <div class = "py-5 text-center" th:if="${error == null}">
            <p class="lead">닷스터디 가입</p>
            <h2>닷스터디 서비스를 사용하려면 인증 이메일을 확인하세요.</h2>

            <div>
            <p class="lead" th:text="${email}">example@email.com</p>
            <a class="btn btn-outline-info" th:href="@{/resend-confirm-email}">
                인증 이메일 다시 보내기
            </a>
            </div>
        </div>
    </div>




    </body>
</html>

 

 

결과화면

 

인증 이메일 재전송하는 뷰 

 

버튼을 클릭할 때 인증이 10초안에 두 번 이상 진행할 시 에러 발생

 

10초 이상 간격으로 이벤트를 발생시켜야 에러없이 작동한다 (시간은 자유롭게 설정 가능)

 


참고

인프런 강의 - 스프링과 JPA 기반 웹 애플리케이션 개발