Backend

[Java Springboot] 이메일 발송 API 코드 리팩토링

사과만쥬 2024. 7. 24. 10:00

2024.07.12 - [Backend] - [Java Springboot] 이메일 발송 API 만들기

 

[Java Springboot] 이메일 발송 API 만들기

SSAFY에서 프로젝트를 하면서 1:1 문의를 이메일로 받을 수 있게끔 처리했다.그 API를 작업하려고 한다.난이도는 크게 어렵지 않다. 금방 할 수 있는 수준. 사전에 필요한 것(Backend)1. 최소한의 Java

manjyuv.tistory.com

 

이전 글을 작성하고 나서 제 글을 보셨던 분께서 카톡으로 개선하는 것이 어떻냐는 연락을 주셨다.

(작성일 기준) 고수 6명과 짱 쎄지고 싶은 만쥬가 있는 채팅방

 

사실 개선을 할 생각을 굳이 안 하고 있었는데, 이 채팅방에서 나온 이야기들 + 친구 코드의 도움을 받아서 리팩토링을 시도해 보았다.

 

내가 작업했던 이메일 발송 API의 경우에는 로그인이 필요없는 항목이어서 만드는 것 자체는 사실 별로 어렵지 않았다. 그러나 코드 작업 당시에는 언제나 그렇듯, 일단 '기능이 작동해야 한다'에 집중한 나머지 코드의 재사용성이나 구조에 대해서는 전혀 신경쓰지 못했다. 그래서 구조나 재사용성이나 여러모로 미흡한 코드가 만들어져서 그때 만들었던 코드를 하나씩 리팩토링을 하고 있다.

 

 

일단 리팩토링한 결과물을 보면 아래와 같다.

 

 

QnaController.java

import lombok.RequiredArgsConstructor;
import org.example.async_pjt.qna.dto.QnaRequestDto;
import org.example.async_pjt.qna.service.QnaService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class QnaController {

    private final QnaService qnaService;

    @PostMapping("/api/v1/qna")
    public String sendQna(@RequestBody QnaRequestDto qnaRequestDto) {
        qnaService.sendEmail(qnaRequestDto.getEmail(), qnaRequestDto.getContent());
        return "문의가 성공적으로 전송되었습니다.";
    }
}

 

 

 

QnaService.java

import lombok.RequiredArgsConstructor;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class QnaService {

    private final JavaMailSender mailSender;

    public void sendEmail(String email, String content) {
        SimpleMailMessage emailForm = createEmailForm(email, content);
        mailSender.send(emailForm);
    }

    private SimpleMailMessage createEmailForm(String email, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(""); // 받아야 되는 사람의 이메일
        message.setReplyTo(email); // 보내는 사람의 이메일(프론트단에서 이메일 입력으로 받음)
        message.setText(content); // 이메일 내용
        message.setSubject("이메일 문의");

        return message;
    }
}

 

 

기존 코드와 하나하나 비교해보도록 하겠다.

 

 

1. 의존성 주입 방식 변경

 

일단 컨트롤러단과 서비스단에서 잊고 있었던 `@RequiredArgsConstructor` 코드를 집어 넣었다.  @RequiredArgsConstructor 어노테이션은 lombok을 추가하면 사용할 수 있는 어노테이션으로 생성자를 일일히 작업할 수고를 덜어주는 어노테이션이다.

 

기존 코드를 비교해 보면 @Autowired를 통해서 의존성을 주입했는데, 이 @Autowired의 경우 테스트가 어려워질 수 있다는 단점이 있다. (이와 관련해서는 추후에 다른 포스팅을 작성할 예정이다.)

 

// QnaService.java 변경 전

    @Autowired
    private JavaMailSender mailSender;
    
// QnaController.java 변경 전

    @Autowired
    private QnaService qnaService;

 

기존 코드는 위와 같다.

 

 

 

2. 서비스단에서 이메일 생성 및 발송 로직 분리

기존의 QnaService.java 파일

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

@Service
public class QnaService {

    @Autowired
    private JavaMailSender mailSender;

    public void sendMessage(String email, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("문의를 받아야 할 사람의 이메일");
        message.setReplyTo(email); // 보내는 사람의 이메일(프론트단에서 이메일 입력으로 받음)
        message.setText(content); // 이메일 내용
        message.setSubject("이메일 문의");

        mailSender.send(message);
    }
}

 

기존 서비스단의 경우에는 로직이 분리되어있지 않았다. sendMessage에서 발송과 실행을 모두 진행하기 때문에 만약에 오류가 발생한다면 디버깅이 어려운 상황이었다.

다행스럽게도 프로젝트 기간 동안 만들어 놓은 api에서는 오류가 발생하지 않았다...

 

새 코드는 아래와 같이 로직을 분리해서 작업했다.

 

    private SimpleMailMessage createEmailForm(String email, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("sminju1009@gmail.com");
        message.setReplyTo(email); // 보내는 사람의 이메일(프론트단에서 이메일 입력으로 받음)
        message.setText(content); // 이메일 내용
        message.setSubject("이메일 문의");

        return message;
    }

이 부분은 메시지를 생성하는 로직이다.

 

    public void sendEmail(String email, String content) {
        SimpleMailMessage emailForm = createEmailForm(email, content);
        mailSender.send(emailForm);
    }

이 부분은 메시지를 발송하는 로직이다.

 

 

 

 

 

번외. 기타 개선이 필요한 점

1) message.setTo의 경우 메일 주소를 하드코딩했는데, 이건 사실 이 api 말고는 이메일 발송이 필요한 api가 없었어서 하드코딩으로 처리했다. 그러나 실 사용을 위해 더 큰 서비스를 제공하는 경우에는 properties 파일이나 yml 파일을 통해서 하드코딩하지 않고 수정할 수 있도록 해야할 것이다.

 

2) @Slf4j 어노테이션을 이용해서 로그를 찍는 과정을 빼먹었다. 서버단에서 오류를 확인할 수 있는 간편한 방법인데, 매번 빼먹고 있다. 다음 코드 작성부터는 @Slf4j 역시도 놓치지 않도록 해야겠다.

 

3) 아직 테스트코드를 작성하지 않았다. 테스트코드 작성 과정도 다른 포스팅을 통해서 다뤄보려고 한다.