회원 가입 이후 이메일 인증을 한 사용자에게만 서비스 접근을 할 수 있도록 구현하고 싶어 찾아본 결과 구글에서 제공하는 SMTP 서비스를 통해 비교적 쉽게 구현할 수 있었다. 단, 구글 SMTP 서비스는 한 이메일 당 하루 100건씩 제한을 걸기 때문에 로컬에서 여러 번 테스트하고 싶을 때는 실제 메일을 보내지 않고 콘솔에 메일 내용을 로그로 작성하는 것을 추천한다.
spring :
mail :
host : smtp.gmail.com
port : 587
username : [본인 Gmail email]
password : [비밀번호]
properties :
mail :
smtp :
auth : true
starttls :
enable : true
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-mail'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
....
}
위와 같이 .yml에 구글에서 받은 앱 비밀번호와 자신의 GMAIL계정을 적고
build.gradle에 의존성을 주입해준다. 현재 목표는 개발환경과 로컬환경에서 각각 다르게 메일을 보낼 것이다.
개발환경
: 개발환경(DEV) SMTP를 이용하여 회원가입한 이메일로 직접 인증 메일을 보내줄 것 이다.
로컬환경
:로컬환경(local) 에서는 에서는 메일을 전송하지않고 콘솔창에 메일과 이메일 토큰을 보내줄 것이다.
결론 : EmailService의 추상화가 필요하다.
개발환경과 로컬환경에서 재정의할 sendEmail() 메서드
public interface EmailService {
void sendEmail(EmailMessage emailMessage);
}
이메일을 보내기 위한 DTO
@Data
@Builder
public class EmailMessage {
//수신자
private String to;
//제목
private String subject;
//내용
private String message;
}
콘솔에 해당 이메일과 이메일 인증토큰을 발급 받을것이다.
@Profile
스프링에서는 프로필(Profile)을 통해 런타임 환경을 설정할 수 있는 기능을 제공한다. 이 기능을 이용하여 테스트환경에서 여러 테스트를 돌리고 난 다음 프로덕션 환경으로 전환하는 것을 어렵지 않게 할 수 있다.아래를 보면 @Profile("local")을 볼 수 있는데 local 환경에서 동작한다는 어노테이션이다.🔽
/**local에서 개발할 때**/
@Slf4j
@Profile("local")
@Component
public class ConsoleEmailService implements EmailService{
@Override
public void sendEmail(EmailMessage emailMessage) {
log.info("sent email: {}", emailMessage.getMessage());
}
}
직접 html 템플릿을 개발하여 인증 이메일을 발송해줄 것이다.
@Slf4j
@Profile("dev")
@Component
@RequiredArgsConstructor
public class HtmlEmailService implements EmailService {
private final JavaMailSender javaMailSender;
@Override
public void sendEmail(EmailMessage emailMessage) {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8");
mimeMessageHelper.setTo(emailMessage.getTo());
mimeMessageHelper.setSubject(emailMessage.getSubject());
mimeMessageHelper.setText(emailMessage.getMessage(), true);
javaMailSender.send(mimeMessage);
log.info("sent email: {}", emailMessage.getMessage());
} catch (MessagingException e) {
log.error("failed to send email", e);
throw new RuntimeException(e);
}
}
}
@Service
@Transactional
@Slf4j
@RequiredArgsConstructor
public class AccountService implements UserDetailsService {
private final EmailService emailService;
private final TemplateEngine templateEngine;
/**
* 이메일에 token보내는 메서드
**/
public void sendSignUpConfirmEmail(Account newAccount) {
Context context = new Context();
context.setVariable("link", "/check-email-token?token=" + newAccount.getEmailCheckToken() +
"&email=" + newAccount.getEmail());
context.setVariable("nickname", newAccount.getNickname());
context.setVariable("linkName", "이메일 인증하기");
context.setVariable("message", "스터디히어 서비스를 사용하려면 링크를 클릭하세요.");
context.setVariable("host", appProperties.getHost());
String message = templateEngine.process("mail/simple-link", context);
EmailMessage emailMessage = EmailMessage.builder()
.to(newAccount.getEmail())
.subject("스터디히어, 회원 가입 인증")
.message(message)
.build();
emailService.sendEmail(emailMessage);
}
1.회원가입 인증 메일을 보내준 것을 확인할 수 있다.🔽
2.다음과 같이 이메일 인증 확인할 수 있다.🔽