Project/LOATODO

[LOATODO] 이메일 인증 Redis 서버 -> RDBMS로 변경

마볼링 2024. 2. 1. 17:18

1. Redis 사용 중지

LoaTodo는 현재(2024년 2월 1일) 이메일로 가입하는 방식과 구글과 연동해서 가입하는 방식

두가지를 지원하고 있습니다.

 

그 중 인증번호를 확인하는 로직에서 

데이터를 저장하는 용도로 Redis를 사용하고 있고

Redis 서버는 AWS ElastiCache에서 구축하고 있습니다.

 

 

 

[ 코드 일부 ]

public int sendMail(String mail){
    MimeMessage message = createMail(mail);
    javaMailSender.send(message);
    saveRedis(mail, number);
    return number;
}

 

public boolean checkMail(MailCheckDto mailCheckDto) {
    List<Mail> allByMail = repository.findAllByMail(mailCheckDto.getMail());
    if (!allByMail.isEmpty()) {
        for (Mail mail : allByMail) {
            if (mail.getNumber() == mailCheckDto.getNumber()
                    && Duration.between(mail.getRegDate(), LocalDateTime.now()).toMinutes() <= 3) {
                mail.setCheck(true);
                repository.save(mail);
                return true;
            }
        }
    }
    return false;
}

 

  1. sendMail 메소드는 인증번호를 전송하면서 동시에 Redis에 보낸 메일과 인증번호를 저장합니다.
  2. checkMail 메소드는 사용자가 입력한 인증번호와 Redis에 저장된 인증번호가 일치하는지 확인 후
    일치하면 저장된 데이터의 check(boolean) 값을 변경합니다.
    • check(boolean) 값은 회원가입 로직이 실행될때 또한 확인되어 이중으로 이메일 인증이 확인됩니다.

 

또한 Redis에 자주 읽는 데이터를 저장하여 필요할 때 빠르게 읽을 수 있는 장점이 있어

프로젝트에서 유용하게 사용할 수 있지만...

 

비쌉니다....

 

 

 

37.8달러면 현재 시세로 약 5만원 정도....

 

 

가난한 개발자가 감당하기 힘든 금액으로 Redis 사용을 중지하려고 합니다.

 

그럼 이메일 인증에서 

인증번호를 저장하는 저장소를 다른 곳에 구현해야하는데 두가지 방식이 있습니다.

  1. 백엔드 서버(Spring boot) 메모리에 저장
  2. 관계형 데이터베이스(MySQL)에 저장

 

2. 백엔드 서버 메모리에 저장

간단히 HashMap 혹은 ConcurrentHashMap을 이용해서 메모리에 저장해놓는 방식입니다.

하지만 이러한 방식은 단점이 많습니다.

  • 메모리에 저장하는 것이므로, 서버의 메모리 한계에 도달할 수 있습니다
  • 서버가 재시작되거나 오류가 생기면 모든 데이터가 손실될 수 있습니다
  • 로드밸런서를 사용하여 서버가 여러개가 분리되면 데이터 불일치가 발생할 수 있습니다.
  • 스레드의 불안정으로 인하여 여러 클라이언트가 동시에 HashMap을 수정하려는 경우 데이터 손상이 발생할 수 있습니다
  • 메모리 내에 데이터를 저장하기 때문에 보안에 취약합니다

AWS EC2로 사용하고 있는 서버의 메모리가 부족한 관계로 이 방식은 사용하지 않았습니다.

 

3. 관계형 데이터베이스에 저장

Redis에 저장하던 데이터를 그대로 RDBMS에 저장하는 방식입니다.

읽고 쓰는 속도면에서는 다소 차이가 날 수 있으나

컬럼이 많이 없고, 구글로그인 대비 하루에 가입하는 가입자 수가 적어 차이가 미미할 것입니다.

 

SELECT
  DATE(created_date),
  COUNT(CASE WHEN auth_provider = 'none' THEN 1 END) AS 이메일_인증_가입자,
  COUNT(CASE WHEN auth_provider = 'google' THEN 1 END) AS 구글_로그인_가입자
FROM
  member
GROUP BY
  DATE(created_date);