Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

이지선의 블로그

[프로젝트] [기능구현] [Redis] 이메일 인증 서비스 본문

Project/늘품

[프로젝트] [기능구현] [Redis] 이메일 인증 서비스

easyxun 2024. 7. 5. 14:22

 

구현 내용

메일 서비스를(네이버, 구글) 제공하는 기업들은 각자의 SMTP서버를 가지고 있다.

사용자가 서버로 이메일 인증 요청을 보내면 SMTP 서버를 통해 사용자에게 인증 코드를 전송한다.

 

구글 SMTP 서버와 스프링에서 기본으로 제공하는 API를 이용하여 이메일 인증 서비스를 구현해보자!


구현 코드

✏️ 이메일 인증 API 작성

1. 이메일 인증 의존성 주입

먼저 스프링 프레임워크의 이메일 관련 의존성을 주입한다.

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

 

2. EmailConfig 작성

설정 파일에 SMTP 서버의 정보와 인증 정보를 설정한다.

@Configuration
public class EmailConfig {
    @Value("${spring.mail.host}")
    private String host;

    @Value("${spring.mail.port}")
    private int port;

    @Value("${spring.mail.username}")
    private String username;

    @Value("${spring.mail.password}")
    private String password;

    @Value("${spring.mail.properties.mail.smtp.starttls.enable}")
    private Boolean starttlsEnable;

    @Value("${spring.mail.properties.mail.smtp.starttls.required}")
    private Boolean starttlsRequired;

    @Value("${spring.mail.properties.mail.smtp.auth}")
    private Boolean auth;

    @Value("${spring.mail.properties.mail.smtp.connectiontimeout}")
    private int connectionTimeOut;

    @Value("${spring.mail.properties.mail.smtp.timeout}")
    private int timeOut;

    @Value("${spring.mail.properties.mail.smtp.writetimeout}")
    private int writeTimeOut;

    @Bean
    public JavaMailSender javaMailSender() {
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
        javaMailSender.setHost(host);
        javaMailSender.setPort(port);
        javaMailSender.setUsername(username);
        javaMailSender.setPassword(password);
        javaMailSender.setJavaMailProperties(createProperties());
        javaMailSender.setDefaultEncoding("UTF-8");

        return javaMailSender;
    }

    private Properties createProperties(){
        Properties properties = new Properties();

        properties.put("mail.smtp.starttls.enable", starttlsEnable);
        properties.put("mail.smtp.starttls.required", starttlsRequired);
        properties.put("mail.smtp.auth", auth);
        properties.put("mail.smtp.connectiontimeout", connectionTimeOut);
        properties.put("mail.smtp.timeout", timeOut);
        properties.put("mail.smtp.writetimeout", writeTimeOut);

        return properties;
    }

}

 

3. application-dev.yml

Gmail SMTP 서버의 호스트, 포트(587), 사용자 이름, 비밀번호를 설정한다.

mail:
  host: smtp.gmail.com
  port: 587
  username: ${MAIL_USERNAME}
  password: ${MAIL_PASSWORD}
  auth-code-expiration-millis: 600000
  properties:
    mail:
      smtp:
        starttls:
          enable: true
          required: true
        auth: true
        connectiontimeout: 5000
        timeout: 5000
        writetimeout: 5000

 

3. 컨트롤러 코드 작성

사용자가 이메일 인증 요청을 보내면 해당 이메일로 인증 코드를 전송한다.

@RestController
@RequestMapping("/api/v1/users")
public class EmailController {
    private final EmailService emailService;

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

    @PostMapping("/verification")
    public ResponseEntity<?> verifyEmail(@RequestBody EmailVerificationRequestDto verificationRequest) {
        String email = verificationRequest.email();
        emailService.verifyEmail(email);
        return ResponseEntity.ok().body("send email successful");
    }
}

 

이렇게 이메일 인증 로직은 구현이 완료되었다!

이 후 서비스 로직에서 인증번호 확인을 위해 메일로 보낸 인증번호를 서버에서 인지하고 있어야 하는데, 인증번호는 짧은 시간동안 사용되고 버려지므로 Redis를 사용하는 것이 적합하다고 생각했다.

 

따라서  Redis를 사용하여 인증코드를 저장하고 확인해 보자!

✏️ Redis 설정

1. Redis 의존성 주입

implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.2.2'

 

2. RedisConfig 설정

@Configuration
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String redisHost;

    @Value("${spring.data.redis.port}")
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHost, redisPort));
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<?, ?> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        return template;
    }

}

 

3. application-dev.yml

  data:
    redis:
      port: 6381
      host: localhost

 

4. RedisService 설정

 

- 이메일과 인증코드를 저장 :

setValue()

public void setValue(String to, String verificationCode, Long timeout, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(to, verificationCode, timeout, timeUnit);
    }

 

- 인증 코드 비교 :

이메일 주소와 인증 코드를 비교하여 일치 여부를 Boolean타입으로 반환

public boolean compareValue(String email, String verificationCode) {
        if (emailNotMatch(email)) {
            throw new IllegalStateException("email not exist");
        }

        String findCode = redisTemplate.opsForValue().get(email);
        return findCode != null && findCode.equals(verificationCode);
    }

 

- 인증 코드 삭제 :

주어진 키를 활용하여 Redis에서 해당 값을 삭제

public void deleteValue(String key) {
        redisTemplate.delete(key);
    }

 

 

5. 이메일 서비스 로직 작성

@Service
@RequiredArgsConstructor
public class EmailService {
    private final JavaMailSender mailSender;
    private final RedisService redisService;

    @Value("${spring.mail.auth-code-expiration-millis}")
    private Long timeout;

    public void sendEmail(String to, String verificationCode) {
        try {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setTo(to);
            message.setSubject("neulpoom 가입을 위한 이메일 인증 코드");
            message.setText("인증 코드: " + verificationCode);
            mailSender.send(message);
        } catch (MailException e) {
            throw new IllegalArgumentException("이메일 전송 실패");
        }
    }
    public void verifyEmail(String email) {

        //1. 가입 요청 들어오면 이메일전송(이 안에 코드 생성까지 존재)
        String verificationCode = generateRandomCode();
        sendEmail(email, verificationCode);
        
        //2. 레디스에 위에 쓴 이메일이랑 코드 저장(setValue)
        redisService.setValue(email, verificationCode, timeout, TimeUnit.MILLISECONDS);

    }

    private String generateRandomCode() {
        Random random = new Random();
        int code = 100000 + random.nextInt(900000);
        return String.valueOf(code);
    }

}

결과

 

 

아주 야물딱지다~~