Java/Spring

[Spring] 이메일 인증을 구현하기 위한 설정 (SMTP, mail.properties)

SeungbeomKim 2023. 4. 2. 16:58

 

오늘은 Spring 기반으로 회원가입 시 이메일 인증 코드를 발급해 주는 기능을 구현해보려고 합니다.

해당 기능을 구현함으로써 보안성을 강화하고, 서비스의 신뢰성을 높일뿐더러, 스팸 및 악성 가입을 방지할 수 있습니다. 또한 Spring에서는 JavaMail 모듈이 워낙 잘 되어 있어서 해당 모듈을 활용해야겠다고 생각했습니다. 

 

SMTP를 이용하여 메일 서버를 구축하고, 해당 프로젝트 내에서 MailConfig, mail.properties 설정을 통해 구현하는 과정에 대해 상세히 설명드리도록 하겠습니다.

 

SMTP (Simple Mail Transfer Protocol)

  • 인터넷을 통해 이메일을 보내고 받는 데 사용되는 통신 프로토콜

 

Spring Mail Module

  • JavaMail API를 추상화하여 편리한 메일 전송 기능을 제공합니다.
  • 해당 API에서 제공하는 MimeMessage를 이용하여 전달한 이메일의 내용을 구성할 수 있습니다.
  • 파일과 같은 내용을 첨가하려면 MimeMessageHelper 클래스를 이용해야 합니다.

 

Dependency

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

 

 

mail.properties

spring.mail.host=smtp.naver.com
spring.mail.username={username}
spring.mail.password={password}

 

 

MailConfig

@PropertySource("classpath:mail.properties")
@Configuration
public class MailConfig {

    private static final int SMTP_SERVER_PORT_NUMBER = 465;
    @Value("${spring.mail.host}")
    private String host;
    @Value("${spring.mail.username}")
    private String id;
    @Value("${spring.mail.password}")
    private String password;

    @Bean
    public JavaMailSender javaMailService() {
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();

        javaMailSender.setHost(host); // 메인 도메인 서버 주소 => 정확히는 smtp 서버 주소
        javaMailSender.setUsername(id); // 네이버 아이디
        javaMailSender.setPassword(password); // 네이버 비밀번호

        javaMailSender.setPort(SMTP_SERVER_PORT_NUMBER); // 메일 인증서버 포트

        javaMailSender.setJavaMailProperties(getMailProperties()); // 메일 인증서버 정보 가져오기

        return javaMailSender;
    }

    private Properties getMailProperties() {
        Properties properties = new Properties();
        properties.setProperty("mail.transport.protocol", "smtp"); // 프로토콜 설정
        properties.setProperty("mail.smtp.auth", "true"); // smtp 인증
        properties.setProperty("mail.smtp.starttls.enable", "true"); // smtp strattles 사용
        properties.setProperty("mail.debug", "true"); // 디버그 사용
        properties.setProperty("mail.smtp.ssl.trust", "smtp.naver.com"); // ssl 인증 서버는 smtp.naver.com
        properties.setProperty("mail.smtp.ssl.enable", "true"); // ssl 사용
        return properties;
    }
}

 

이제 스프링 애플리케이션 단에서 메일 서버 설정을 마쳤습니다.

다음으로 이메일 인증코드 발급 로직에 대해 작성해 보도록 하겠습니다.

 

MailService

@RequiredArgsConstructor
@Service
public class EmailService {

    private static final int CERTIFICATION_NUMBER_LENGTH = 8;
    private static final int CERTIFICATION_NUMBER_OPTION = 3;
    private static final int NUM_LETTERS_ALPHABET = 26;
    private static final int ASCII_OFFSET_LOWER_A = 97;
    private static final int ASCII_OFFSET_UPPER_A = 65;
    private static final int NUM_DIGITS = 10;

    private String ePw;
    private final EmailAuthRepository emailAuthRepository;
    private final JavaMailSender emailSender;

    public MimeMessage createMessage(String to) throws MessagingException, UnsupportedEncodingException {

        MimeMessage message = emailSender.createMimeMessage();

        message.addRecipients(RecipientType.TO, to);// to => 보내는 대상
        message.setSubject("Hand-Over 회원가입 이메일 인증");// 메일 제목

        // 메일 내용
        // 아래에서 메일의 subtype 을 html 로 지정해주었기 때문인지 html 문법을 사용가능하다
        String msgg = "";
        msgg += "<div style='margin:100px;'>";
        msgg += "<h1> 안녕하세요. 케어 서비스를 제공해주는 매칭 플랫폼 Hand-Over 입니다.</h1>";
        msgg += "<h1 style='color:green;'>인증번호 안내 메일입니다.</h1>";
        msgg += "<br>";
        msgg += "<p>" + to + "님 Hand-Over 플랫폼의 회원가입을 환영합니다.<p>";
        msgg += "<br>";
        msgg += "<p>해당 이메일은 회원가입을 위한 인증번호 안내 메일입니다.<p>";
        msgg += "<br>";
        msgg += "<p>하단 인증번호를 '이메일 인증번호' 칸에 입력하여 가입을 완료해주세요.<p>";
        msgg += "<br>";
        msgg += "<div align='center' style='border:1px solid black; font-family:verdana';>";
        msgg += "<h3 style='color:blue;'>회원가입 인증 코드입니다.</h3>";
        msgg += "<div style='font-size:130%'>";
        msgg += "CODE : <strong>";
        msgg += ePw + "</strong><div><br/> "; // 인증번호 넣기
        msgg += "</div>";
        message.setText(msgg, "utf-8", "html");// 내용, charset 타입, subtype
        message.setFrom(new InternetAddress("handover1@naver.com", "핸드오버 관리자"));

        return message;
    }

    public String createKey() {

        StringBuffer key = new StringBuffer();
        Random random = new Random();

        for (int i = 0; i < CERTIFICATION_NUMBER_LENGTH; i++) { // 인증코드 8자리
            int index = random.nextInt(CERTIFICATION_NUMBER_OPTION); // 0~2 까지 랜덤, rnd 값에 따라서 아래 switch 문이 실행됨

            switch (index) {
                case 0:
                    key.append((char) ((int) (random.nextInt(NUM_LETTERS_ALPHABET)) + ASCII_OFFSET_LOWER_A)); // 영어 소문자
                    // a~z (ex. 1+97=98 => (char)98 = 'b')
                    break;
                case 1:
                    key.append((char) ((int) (random.nextInt(NUM_LETTERS_ALPHABET)) + ASCII_OFFSET_UPPER_A)); // 영어 대문자
                    // A~Z
                    break;
                case 2:
                    key.append((random.nextInt(NUM_DIGITS))); // 숫자
                    // 0~9
                    break;
            }
        }

        return key.toString();
    }

    @Transactional
    public String sendSimpleMessage(String to) throws Exception {
        ePw = createKey();
        MimeMessage message = createMessage(to);
        try {
            if(emailAuthRepository.existsByEmail(to)) {
                emailAuthRepository.deleteByEmail(to);
            }
            emailSender.send(message); // 메일 발송
        } catch (MailException errorMessage) {
            errorMessage.printStackTrace(); // 에러의 발생근원지를 찾아서 에러 출력
            throw new IllegalArgumentException();
        }
        EmailAuth emailAuth = new EmailAuth(ePw, to);
        emailAuthRepository.save(emailAuth);
        return ePw; // 메일로 보냈던 인증 코드를 서버로 반환
    }
}

 

위에서 설명드렸듯이 MimeMessage를 통해 클라이언트에게 전송할 메시지를 생성하고, JavaMailSender 클래스를 활용하여 랜덤으로 생성한 인증코드, 메시지를 전달할 수 있게 되었습니다. 

 

이상으로 포스팅 마치겠습니다. 감사합니다.