image.png

image.png

image.png

비밀번호 반드시 따로 저장

라이브러리 추가

dependencies {
    // 메일 전송 위한 라이브러리 추가
    implementation 'org.springframework.boot:spring-boot-starter-mail'
}

application.yml 설정

spring:
  mail:
    host: smtp.gmail.com
    port: 587
    username: 이메일보낼name
    password: 앱비밀번호
    properties:
      mail:
        smtp:
          auth: true # 사전인증 설정
          starttls:
            enable: true # STL 필수설정

요청받기(Dto)

// 1. 이메일 입력받음
public record EmailDuplicationRequest(
	String email
) {
}

------------------------------------------------

// 2. 이메일 + 인증코드 검증
public record EmailAuthRequest(
	String email,
	String authCode
) {
}

이메일 보내기 (Service)

package com.ssafy.econimal.domain.auth.service;

import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import com.ssafy.econimal.domain.auth.dto.request.EmailRequest;
import com.ssafy.econimal.domain.auth.util.HtmlContent;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class AuthEmailSendService {

	private final JavaMailSender javaMailSender;
	private final AuthEmailService authEmailService;
	private final static String EMAIL_SUBJECT = "Econimal Team: 인증코드";

	public void handleSendEmail(EmailRequest request) {
		// 인증코드 생성
		String authCode = authEmailService.generateVerificationCode();

		// 인증코드 저장
		authEmailService.saveVerificationCode(request.email(), authCode);

		// 이메일 전송
		sendEmail(request.email(), authCode);
	}

	// 이메일 보내기
	private void sendEmail(String email, String authCode) {
		MimeMessage mimeMessage = javaMailSender.createMimeMessage();

		try {
			MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, false, "UTF-8");
			helper.setTo(email); // 이메일 받을 사람
			helper.setSubject(EMAIL_SUBJECT);
			
			// 이메일 내용, HTML 설정(true)
			helper.setText(HtmlContent.emailAuthContent(authCode), true);

			javaMailSender.send(mimeMessage);
			log.debug("이메일 전송 성공: email: {}, code: {}", email, authCode);
		} catch (MessagingException e) {
			throw new RuntimeException("알 수 없는 이유로 이메일 전송 실패");
		}
	}
}

이메일 인증하기 (Service2)

package com.ssafy.econimal.domain.auth.service;

import java.security.SecureRandom;
import java.time.Duration;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.ssafy.econimal.domain.auth.dto.request.EmailAuthRequest;
import com.ssafy.econimal.domain.auth.dto.request.UpdatePasswordRequest;
import com.ssafy.econimal.domain.auth.util.AuthValidator;
import com.ssafy.econimal.domain.user.entity.User;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class AuthEmailService {

	private final BCryptPasswordEncoder encoder;
	private final AuthValidator validator;
	private final RedisTemplate<String, String> redisTemplate;

	private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	private static final SecureRandom random = new SecureRandom();
	private static final long EXPIRATION_TIME = 5;  // 인증 코드의 만료 시간 (분)

	// 이메일 - 인증코드 확인
	public void verifyCode(EmailAuthRequest request) {
		String authCode = redisTemplate.opsForValue().get(request.email());

		validator.verifyAuthCode(request, authCode);

		redisTemplate.delete(request.email());
	}

	// 6자리 무작위 인증코드 생성
	public String generateVerificationCode() {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 6; i++) {
			int index = random.nextInt(CHARACTERS.length());
			sb.append(CHARACTERS.charAt(index));
		}
		return sb.toString();
	}

	// 이메일, 암호 레디스 저장
	public void saveVerificationCode(String email, String code) {
		redisTemplate.opsForValue().set(email, code, Duration.ofMinutes(EXPIRATION_TIME));
	}

	// 비밀번호 일치 확인
	public void updatePassword(UpdatePasswordRequest request) {
		validator.verifyUpdatePassword(request.newPassword1(), request.newPassword2());

		User findUser = validator.findUser(request.userId());
		String encodedPassword = encoder.encode(request.newPassword1());

		findUser.updatePassword(encodedPassword);
	}
}