취미/프로그래밍

Spring Boot & Redis 사용한 이메일 인증코드 구현

Anything Doing OK 2024. 11. 28. 00:40

저번에 Laravel 로 이메일 인증코드를 구현하는 방법을 알아보았다. 

https://normal-operating.tistory.com/37

 

Laravel 이메일 인증코드 구현

https://normal-operating.tistory.com/36  Laravel 이메일 인증 연동 구현하기회사에서 레거시만 사용하다보니 기능을 간단하게 구현할 수 있는 laravel 을 통해서 구현해보았다.  진행하면서 발생한 오류

normal-operating.tistory.com

동작이미지

기능은 거의 비슷하다. 

 

Redis 설치

https://jaey0ng.tistory.com/54

 

[Redis] 윈도우 환경에서 redis 설치/실행하기

윈도우 설치 Redis 공식문서입니다. https://redis.io/docs/install/install-redis/install-redis-on-windows/ Install Redis on Windows Use Redis on Windows for development redis.io 레디스 공식문서에는 위처럼 사용하면 된다라고 써

jaey0ng.tistory.com

 

다른 점은 Redis 를 사용해서 인증번호를 저장하고 인증번호를 이메일로 전송해준다는 점

 

설정사항

1. SecurityConfig 에 인증 관련 경로인

"/api/email/send",

를 추가해준다 

.requestMatchers("/login", "/register","/api/register", "/api/email/send", "/verify").permitAll() // 인증 관련 경로는 누구나 접근 가능

 

2. application.properties 에 다음과 같이 설정 추가

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=인증받은gmail
spring.mail.password=16자리-없이
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000
spring.mail.transport.protocol=smtp

 

3.build.gradle의 dependencies에 다음과 같이 설정 추가

implementation 'org.springframework.boot:spring-boot-starter-mail'

 

4. MailConfig 생성 후

 

package com.application.member.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.util.Properties;

@Configuration
public class MailConfig {

    @Bean
    public JavaMailSender getJavaMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost("smtp.gmail.com");
        mailSender.setPort(587);

        mailSender.setUsername("이메일");
        mailSender.setPassword("인증코드16자리-없이");

        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.debug", "true");

//      SSL 설정 추가
        props.put("mail.smtp.ssl.trust", "smtp.gmail.com");
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");

        return mailSender;
    }
}

다음과 같이 코드 작성

 

5. RedisConfig 에 

package com.application.member.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration
public class RedisConfig {

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

다음과 같이 코드 작성

 

6.  EmailService에 

@Service
public class EmailService {

    private final JavaMailSender mailSender;
    private final StringRedisTemplate redisTemplate;

    public EmailService(JavaMailSender mailSender, StringRedisTemplate redisTemplate) {
        this.mailSender = mailSender;
        this.redisTemplate = redisTemplate;
    }

    // 인증번호 생성
    public String generateVerificationCode() {
        Random random = new Random();
        int code = 100000 + random.nextInt(900000); // 6자리 랜덤 숫자
        return String.valueOf(code);
    }

    // 인증번호 Redis에 저장 (5분 유효기간 설정)
    public void saveVerificationCode(String email, String verificationCode) {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        ops.set(email, verificationCode, 5, TimeUnit.MINUTES); // 5분 TTL
    }

    // 인증번호 검증
    public boolean verifyCode(String email, String code) {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        String storedCode = ops.get(email);

        if (storedCode != null && storedCode.equals(code)) {
            redisTemplate.delete(email); // 인증 성공 시 Redis에서 제거
            return true;
        }
        return false;
    }
    // 이메일 발송
    public void sendVerificationEmail(String toEmail, String verificationCode) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            message.setFrom(toEmail);
            message.setRecipients(MimeMessage.RecipientType.TO, toEmail);
            String body = "";
            body += "<h3>" + "요청하신 인증 번호입니다." + "</h3>";
            body += "<h1>" + verificationCode + "</h1>";
            body += "<h3>" + "감사합니다." + "</h3>";
            message.setText(body,"UTF-8", "html");
            mailSender.send(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

다음과 같이 코드작성

 

7.  EmailController 에

@RestController
@RequestMapping("/api/email")
public class EmailController {

    private final EmailService emailService;

    public EmailController(EmailService emailService) {
        this.emailService = emailService;
    }

    @PostMapping("/send")
    public String sendVerificationCode(@RequestParam String email) {
        System.out.println("Received email: " + email);
        String verificationCode = emailService.generateVerificationCode();
        System.out.println("Generated Verification Code: " + verificationCode);
        emailService.saveVerificationCode(email, verificationCode); // Redis에 저장
        emailService.sendVerificationEmail(email, verificationCode); // 이메일 발송
        return "인증번호가 발송되었습니다.";

    }

 

다음과 같이 코드작성

 

프론트 코드 

 

  const email = document.getElementById("email").value;
  const response = await fetch('/api/email/send', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ email })
  });

 

Spring Security 때문에 고생좀 했다.

2일에 걸쳐서 구현하게 되어 빠져있는 부분이 있을 수 있습니다.