본문 바로가기
스프링과 JPA 기반 웹 어플리케이션 개발/3부 개발 환경 설정

44. MimeMessage 전송하기

by Backchus 2020. 4. 23.

HTML 이메일 전송하기

  • MimeMessage 전송
  • MimeMessageHelper: MimeMessage 만들 때 사용할 수 있는 유틸리티

EmailService 인터페이스

  • void sendEmail(EmailMessage emailMessage);

EmailService 인터페이스 구현체

  • ConsoleEmailService: 콘솔 출력
  • HtmlEmailService: JavaMailSender를 사용해서 HTML로 이메일 전송

 

우리는 지금까지 SimpleMailMessage로 사용해서 보냈지만 실제로 특정한 웹서비스 에서 보내는 이메일은 대부분 HTML로 만들어진 이메일이다. HTML로 만들어진 이메일을 보내려면 MIME 메시지로 보내야 한다.

package me.weekbelt.studyolle.account;

@Transactional
@RequiredArgsConstructor
@Service
public class AccountService implements UserDetailsService {

    private final AccountRepository accountRepository;
    private final JavaMailSender javaMailSender;
    private final PasswordEncoder passwordEncoder;
    private final ModelMapper modelMapper;

    // .................

    public void sendSignUpConfirmEmail(Account newAccount) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("스터디올래, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" + newAccount.getEmailCheckToken() + "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);
    }

    // ................
}

 

package me.weekbelt.studyolle.account;


@Slf4j                 // 로깅메시지를 위해 추가
@Transactional
@RequiredArgsConstructor
@Service
public class AccountService implements UserDetailsService {

    // ...........

    public void sendSignUpConfirmEmail(Account newAccount) throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        try {
            MimeMessageHelper mimeMessageHelper =
                    new MimeMessageHelper(mimeMessage, false, "UTF-8");
            mimeMessageHelper.setTo(newAccount.getEmail());
            mimeMessageHelper.setSubject("스터디올래, 회원 가입 인증");
            mimeMessageHelper.setText("/check-email-token?token=" + newAccount.getEmailCheckToken() +
                    "&email=" + newAccount.getEmail(), false);
            javaMailSender.send(mimeMessage);
        } catch (MessagingException e) {
            log.error("failed to send email", e);
        }

        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("스터디올래, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" +
                newAccount.getEmailCheckToken() + "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);
    }

    // .............
}

 

위 코드 처럼 실제로 메일을 보낼때는 MimeMessage로 보내고 로컬로 보낼때는 SimpleMailMessage로 보내려고 하면 번거롭기 때문에 EmailService를 추상화 해야 한다.

EmailService 인터페이스 생성

package me.weekbelt.studyolle.mail;

public interface EmailService {

    void sendEmail(EmailMessage emailMessage);
}

 

EmailMessage DTO 생성

package me.weekbelt.studyolle.mail;

@Data
@Builder
public class EmailMessage {

    private String to;
    private String subject;
    private String message;
}

 

로컬과 실제로 보낼 이메일 처리를 위한 EmailService의 구현체 생성

로컬을 위한 서비스

package me.weekbelt.studyolle.mail;


@Slf4j
@Profile("local")
@Component
public class ConsoleEmailService implements EmailService{

    @Override
    public void sendEmail(EmailMessage emailMessage) {
        log.info("sent email: {}" + emailMessage.getMessage());
    }
}

 

실제 HTML 이메일을 보내는 구현체

package me.weekbelt.studyolle.mail;

@Slf4j
@RequiredArgsConstructor
@Profile("dev")
@Component
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(), false);  // 지금은 html이 아니므로 false
            javaMailSender.send(mimeMessage);
            log.info("sent email: {}", emailMessage.getMessage());
        } catch (MessagingException e) {
            log.error("failed to send email", e);
        }
    }
}

 

ConsoleMailSender 삭제

 

JavaMailSender를 추상화한 EmailService로 대체

package me.weekbelt.studyolle.account;


@Slf4j
@Transactional
@RequiredArgsConstructor
@Service
public class AccountService implements UserDetailsService {

    // ....
    
//    private final JavaMailSender javaMailSender;
    private final EmailService emailService; // 대체
    
    // ....

    public void sendSignUpConfirmEmail(Account newAccount) throws MessagingException {
        EmailMessage emailMessage = EmailMessage.builder()
                .to(newAccount.getEmail())
                .subject("스터디올래, 회원 가입 인증")
                .message("/check-email-token?token=" +     // message는 나중에 HTML로 수정
                        newAccount.getEmailCheckToken() + "&email=" + newAccount.getEmail())
                .build();

        emailService.sendEmail(emailMessage);
    }

    // ....

    public void sendLoginLink(Account account) {
        account.generateEmailCheckToken();
        EmailMessage emailMessage = EmailMessage.builder()
                .to(account.getEmail())
                .subject("스터디올래, 로그인 링크")
                .message("/login-by-email?token=" + account.getEmailCheckToken() + // message는 나중에 HTML로 수정
                        "&email=" + account.getEmail())
                .build();

        emailService.sendEmail(emailMessage);
    }

    // ......
}

 

 

깨진 테스트 수정

package me.weekbelt.studyolle.account;

@Transactional
@AutoConfigureMockMvc
@SpringBootTest
class AccountControllerTest {

    // .......

    //@MockBean JavaMailSender javaMailSender;
    @MockBean EmailService emailService;        // 대체

    // ........

    @DisplayName("회원 가입 처리 - 입력값 정상")
    @Test
    public void signUpSubmit_with_correct_input() throws Exception {
       
       //......
       
        then(emailService).should().sendEmail(any(EmailMessage.class)); // 수정

    }

    // ........
}

 

콘솔로 이메일 검증

 

실제 이메일 검증

 

참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#

 

스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런

이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다. 활용 웹 개발 프레임워크 및 라이브러리 Java Spring Spring Boot Spring Data JPA Thymeleaf 온라인 강의 스

www.inflearn.com