본문 바로가기
스프링과 JPA 기반 웹 어플리케이션 개발/1부 (개발환경, 회원가입, 로그인, 계정설정)

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

by Backchus 2020. 4. 20.

구현한 로직

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

GET "/check-email"

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

GET "/resend-confirm-email"

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

 

SecurityConfig 설정 수정

package me.weekbelt.studyolle.config;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // "check-email" 제거 (로그인 한 사용자에만 접근해야 하기 때문)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email-token",
                        "/email-login", "/check-email-login", "/login-link").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();
        ;
    }
    
    // 기존 코드 .......
}

 

AccountController에 checkEmail메소드 추가

ㅁpackage me.weekbelt.studyolle.account;

@RequiredArgsConstructor
@Controller
public class AccountController {


    // 기존 코드 ..........

    // 추가
    @GetMapping("/check-email")
    public String checkEmail(@CurrentUser Account account, Model model) {
        model.addAttribute("email", account.getEmail());
        return "account/check-email";
    }
}

 

이메일 인증을 보내기 위한 페이지인 /account/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">
<!--네비게이션 바-->
<div th:replace="fragments.html::main-nav"></div>

<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}">your@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}">your@email.com</p>
            <a class="btn btn-outline-info" th:href="@{/resend-confirm-email}">인증 이메일 다시 보내기</a>
        </div>
    </div>
</div>

<!-- footer -->
<div th:replace="fragments.html::footer"></div>
</body>
</html>

 

이메일 인증 요청을 처리하는 핸들러 메소드를 AccountController에 구현

package me.weekbelt.studyolle.account;

@RequiredArgsConstructor
@Controller
public class AccountController {

    // 기존 코드 ......
    
    @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:/";
    }
}

 

Account엔티티에 이메일 토큰을 발행한지 1시간이 지났는지 아닌지 확인하는 코드 추가

package me.weekbelt.studyolle.domain;

@Builder @AllArgsConstructor @NoArgsConstructor
@Getter @Setter @EqualsAndHashCode(of = "id")
@Entity
public class Account {

    // 기존 코드 .........

    private LocalDateTime emailCheckTokenGeneratedAt;           // 토큰 발행 시간 필드 추가

    public void generateEmailCheckToken() {
        this.emailCheckToken = UUID.randomUUID().toString();
        this.emailCheckTokenGeneratedAt = LocalDateTime.now();  // 추가
    }

    // 기존 코드 .........

    // 추가
    public boolean canSendConfirmEmail() {
        return this.emailCheckTokenGeneratedAt.isBefore(LocalDateTime.now().minusHours(1));
    }
}

 

AccountService의 sendSignUpConfirmEmail() 메소드를 private에서 public으로 접근 제한 수정

package me.weekbelt.studyolle.account;

@RequiredArgsConstructor
@Service
public class AccountService {

    // 기존코드 ....
    
    public void sendSignUpConfirmEmail(Account newAccount) {         //public으로 수정
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("스터디올래, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" + newAccount.getEmailCheckToken() + "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);
    }
    
    // 기존코드 ......
}

 

 

 

참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#

 

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

이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다. 활용 웹 개발 프레임워크 및 라이브러리 Java Spring Spring Boot Spring Data JPA Thymeleaf 온라인 강의 스

www.inflearn.com