본문 바로가기
Java+Spring 박살내기

Redis와 Gmail SMTP 활용하여 이메일 인증 구현하기

by 창브로 2024. 7. 26.
728x90

오늘은 Redis와 Gmail SMTP를 활용하여 이메일 인증을 구현하는 방법을 알아보겠습니다!

 

 

일단 Redis와 Gmail SMTP에 대해 알아야겠죠?
먼저 Gmail SMTP부터 알아봅시다!

Gmail SMTP란?

 

Gmail은 Google에서 제공하는 메일로 다들 알고 계실 겁니다.
그럼 이제 SMTP가 뭔지 알아야겠죠?

SMTP란 Simple Mail Transfer Protocol의 약자로 일반적으로 이메일을 송수신하는 서버를 뜻합니다.

 

그럼 Gmail과 SMTP 두 개를 합치면 Gmail을 송수신하는 서버를 뜻합니다.

이 서버를 통해서 우리는 인증을 원하는 사용자에게 인증 메일을 보낼 것입니다.


그럼 이제 Redis를 알아볼까요??

 

Redis란?

Redis는 중요하기 때문에 조금 더 자세하게 알아봅시다!

 

Redis는 Remote Dictionary Server의 약자로 원격에 위치하고
프로세스로 존재하는 key-value 구조 데이터 관리 서버 시스템입니다.

 

key-value 구조 데이터란 mysql 같은 관계형 데이터가 아닌 비 관계형 구조로
그냥 key와 value로만 데이터들이 저장되어 있는 구조입니다.

 

다들 아시겠지만 데이터의 고속 읽기와 쓰기에 최적화되어 있습니다. 

근데 인증 메일을 보내고 인증하는데 대체 Redis를 왜 쓰냐고요?

이걸 얘기하기 전에 Cache에 대해 알고 갈 필요가 있습니다.

Cache한번 조회된 데이터미리 특정 공간에 저장해 놓고
똑같은 요청이 발생하게 되면 서버에게 다시 요청하지 않고
저장해 놓은 데이터를 제공해서 빠르게 서비스를 제공해 주는 기능을 의미합니다.

 

Cache에 대해 이해가 되시나요?

 

Redis가 이런 캐싱을 하도록 도와주는 것입니다.

 

물론 다른 세션 관리, 메시지 중개인, 대기열 처리 등의 기능도 있지만
이번 글에서는 Redis의 캐싱을 활용하여 개발을 할 것이기 때문에 캐싱 부분만 설명하도록 하겠습니다.

 

주로 Redis 캐싱을 활용한 사례는

- 임시 비밀번호

- 로그인 세션

- JWT


일정 주기로 갱신해도 괜찮은 데이터에 많이 사용된다고 합니다.

이메일 인증 기능에 이걸 왜 쓰냐고요??

Redis의 캐싱을 활용하지 않았을 때 구동방식을 봅시다.

유저가 이메일 인증 요청

인증 메일 보내는 api 실행

인증 메일에 담을 랜덤 숫자 생성

이메일과 랜덤 숫자를 매치해서 db에 저장

사용자에게 인증 메일 전송

유저가 인증 메일 확인 후 인증 링크 클릭

인증 메일 검증하는 api 실행

사용자의 이메일을 db에서 검색

이메일과 매치된 랜덤 숫자 db에서 return

가져온 랜덤 숫자와 인증 메일 검증하는 api에서 받은 숫자랑 비교

숫자가 같을 시 인증 성공

 

이게 한 명의 유저만 하면 그렇게 느리지 않다고 생각하실 수 있는데 10만 명의 유저가 진행한다고 했을 때 db에서 검색을 계속해야 하니까 엄청 버벅거리겠죠? 


그래서 저희는 Redis의 캐싱을 활용할 것입니다.

 

캐싱을 활용했을 때 구동방식을 봅시다.

 

유저가 이메일 인증 요청

인증 메일 보내는 api 실행

인증 메일에 담을 랜덤 숫자 생성

이메일을 key로 숫자를 value로 Redis의 서버에 저장

사용자에게 인증 메일 전송

유저가 인증 메일 확인 후 인증 링크 클릭

인증 메일 검증하는 api 실행

사용자의 이메일을 Redis의 서버에서 뽑아옴

이메일의 value를 숫자랑 비교

숫자가 같을 시 인증 성공

 

찾아오는 게 같은데 뭐가 다르냐라고 생각할 수 있는데 Redis는 위에도 말씀드렸듯이

key-value로 데이터를 관리하는 구조로 읽기와 쓰기가 SQL DB보다 훨씬 빠릅니다.

 

 

그럼 이제 Redis와 Gmail SMTP를 활용하여 이메일 인증을 구현해 보도록 합니다!

Gmail SMTP를 활용하려면 google 계정을 생성하시고 설정에서 app password를 발급받으셔야 합니다!!!

 

 

 

먼저 이 두줄을 gradle에 implementation 해줍시다

 


redis와 mail을 활용하기 위해서 추가해줘야 합니다.

 

이제 application.yml 파일에 이 두 코드를 추가해 줍니다.


APP_PASSWORD는 제가 처음에 구글 계정을 만들고 생성하라고 했던 app password입니다.

 

 

 

 

다음으론 레디스와의 서버 연결 설정을 해줍니다.

 

 

 

다음으로 redis를 활용해야 하는 부분을 구현해 줍니다.

 

 

 

 

다음으로 인증 메일을 생성하고 전송하고 검증 기능이 있는 Service를 구현합니다.

@RequiredArgsConstructor
@Service
public class EmailCertificationService {

    // 이메일 전송을 위한 Spring의 메일 전송 인터페이스
    private final JavaMailSender mailSender;
    private final CertificationNumberDao certificationNumberDao;

    public void sendEmailForCertification(String email) throws NoSuchAlgorithmException, MessagingException {
        // 인증번호 생성
        String certificationNumber = getCertificationNumber();
        // 아래 링크를 클릭하여 인증을 완료하도록
        // TODO: 도메인 구매시 링크 변경 필요
        String content = String.format("%s/api/users/verify?certificationNumber=%s&email=%s   ${메시지 내용}", "${본인 도메인 주소}", certificationNumber, email);

        sendMail(email, content);
        certificationNumberDao.saveCertificationNumber(email, certificationNumber);
    }

    private static String getCertificationNumber() throws NoSuchAlgorithmException {
        String result;

        do {
            int i = SecureRandom.getInstanceStrong().nextInt(999999);
            result = String.valueOf(i);
        } while (result.length() != 6);

        return result;
    }

    private void sendMail(String email, String content) throws MessagingException {
        MimeMessage mimeMessage = mailSender.createMimeMessage(); // 새로운 MIME 메시지를 생성
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage); // 메시지의 다양한 속성을 설정하는 헬퍼

        helper.setTo(email); // 수신자 이메일 주소 설정
        helper.setSubject("detour 사이트 인증 요청 메일입니다."); // 제목 설정
        helper.setText(content); // 이메일 본문 설정
        mailSender.send(mimeMessage); // 이메일 전송
    }

    public void verifyEmail(String certificationNumber, String email) {
        if (isVerify(certificationNumber, email)) {
            throw new CustomException(ErrorCode.VERIFY_NOT_ALLOWED);
        }
        certificationNumberDao.removeCertificationNumber(email); // 유효성 인증 검사 성공시 레디스에서 인증번호 삭제
    }

    private boolean isVerify(String certificationNumber, String email) {
        return !(certificationNumberDao.hasKey(email) && // 인증번호가 저장되어 있는지 확인
                certificationNumberDao.getCertificationNumber(email) // 인증번호 들고옴
                        .equals(certificationNumber)); // 인증번호와 제공된 인증번호 비교
    }

 

 


이렇게 구현하셨으면 본인이 구현한 Controller에 api를 구현 후 api를 실행시키면 작동됩니다


이메일 전송 api 실행 시 이렇게 제가 설정한 제목과 메시지로 이메일이 오게 됩니다!

 


그리고 인증 링크를 클릭 시 검증 api가 실행되며 인증이 완료된 것을 확인할 수 있습니다.

 

 

긴 글 읽는다고 수고많으셨습니다🙇🏻‍♂️