2023. 10. 16. 10:17ㆍ기술 창고/Spring
Spring으로 이메일로 메일을 전송하는 기능을 구현해보도록 하겠습니다.
!! 또한 maven 환경에서 프로젝트를 운영하고 있으며, 일부러 Spring Boot 버전을 낮춰 사용하므로 사용하는 디펜던시들의 버전 또한 낮은 버전을 사용하고 있음을 미리 알려드립니다.
1. Dependency Import
[Maven일 경우]
<!-- javax mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- spring boot email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>3.1.4</version>
</dependency>
======================================================================
======================================================================
[Gradle일 경우]
// spring boot email
implementation 'org.springframework.boot:spring-boot-starter-mail'
// javax mail
implementation group: 'javax.mail', name: 'mail', version: '1.4.7'
pom.xml 에 이메일과 관련된 dependency를 import 해줍니다.
버전이 낮으므로 javax.mail dependency 를 사용하고 있습니다.
2. 구글 계정 설정
자신의 구글 계정의 계정 관리에 들어가서 보안 탭의 2단계 인증 항목으로 들어갑니다.
앱 비밀번호 항목으로 진입니다.
앱 이름을 입력합니다.
앱 이름을 입력하여 생성을 완료하면 앱 비밀번호가 나옵니다.
해당 비밀번호는 4문자씩 4개 총 16개의 문자로 이루어져있고, 절대로 노출 되어서는 안됩니다.
생성이 완료되었으면 만들어진 앱이 보이는 것을 볼 수 있습니다.
3. application.properties 설정
# email setting
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username={구글 이메일}
spring.mail.password={방금 생성한 앱 비밀번호 그대로 입력 (띄어쓰기도 되어있는 상태로)}
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.trust=*
spring.mail.properties.mail.smtp.ssl.protocols=TLSv1.2
username은 구글 이메일을 입력하는 설정인데, 이 부분이 메일을 발송하는 사람의 이메일을 나타냅니다.
나중에 발송하는 메일을 변경해야할 경우 이 설정값을 변경해주거나 해야합니다.
또 주의해서 봐야할 것은 password 설정값 부분인데 방금 생성한 앱 비밀번호를 그대로 입력하면 됩니다.
혹시 띄어쓰기를 지우고 넣어야 하나 헷갈릴 수도 있으나 그냥 그대로 띄어쓰기가 있는 상태로 입력하면 됩니다.
4. 실제로 메일을 보낼 관련 View, Controller, Service 생성
View
<form th:action="@{/gds/contact/inquiry}" th:method="post" enctype="multipart/form-data">
<input type="text" id="contactor" name="contactor" placeholder="이름을 입력하세요" required>
<br>
<input type="text" id="address" name="address" placeholder="주소를 입력하세요" required>
<br>
<input type="tel" id="phoneNumber" name="phoneNumber" placeholder="전화번호를 입력하세요" required>
<br>
<input type="email" id="email" name="email" placeholder="이메일을 입력하세요" required>
<br>
<textarea id="additionalInfo" name="additionalInfo" placeholder="추가 정보를 입력하세요"></textarea>
<br>
<div class="upload-box">
<label for="file-upload" style="cursor: pointer;">
<img th:src="@{/img/upload.png}" alt=""> Upload Your File
</label>
<input type="file" id="file-upload" name="contactImage" style="display: none;">
</div>
<br>
<input type="submit">
</form>
메일을 보낼 html 코드 부분입니다.
타임리프를 사용하고 있어서 th 키워드의 html 코드가 보이는 것이 특징입니다.
하나씩 살펴보면,
(1)
form 태그의 th:action 속성을 넣어 api 주소를 매핑하고, th:method 속성을 통해 method 방식을 post로 지정해주었습니다.
enctype 속성에 multipart/form-data 를 명시함으로서 File 타입의 데이터도 같이 요청됨을 명시해주었습니다.
(2)
form 태그 내부의 name 속성 값이 contactor, address, phoneNumber, email, additionalInfo 인 태그들의 값은 요청되는 api의 @ModelAttribute("contactRequest") ContactRequestDto contactRequestDto 요청 RequestDto의 속성 명과 매핑되어 들어오게 됩니다.
@Setter
@Getter
public class ContactRequestDto {
private String contactor; // 문의자
private String address; // 문의자 주소
private String phoneNumber; // 문의자 전화번호
private String email; // 문의자 이메일
private String additionalInfo; // 문의자 추가 정보
}
또한, name 속성이 contactImage 인 파일은 api의 MultipartHttpServletRequest imageRequest 요청 변수에 들어오고, get Files(" contactImage ") 함수를 통해 요청받은 파일들을 가져올 수 있습니다.
(3)
submit 버튼을 누르면 요청되게됩니다.
ContactController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/mail")
@Controller
public class ContactController {
private final ContactService contactService;
// 문의 메일 기능 api
@PostMapping("/contact/inquiry")
public RedirectView contactInquiry(
MultipartHttpServletRequest imageRequest,
@ModelAttribute("contactRequest") ContactRequestDto contactRequestDto) throws IOException {
log.info("문의 작성 기능 api");
// 문의용 이미지 파일을 리스트에 저장
List<MultipartFile> contactImage = imageRequest.getFiles("contactImage");
// service 단으로 진입하여 실질적으로 문의 사항을 등록시킬 비즈니스로직 수행 (이후 이메일로 직접적으로 문의 메일을 보낼 로직 추가 필요)
contactService.contactInquiry(contactImage, contactRequestDto);
return new RedirectView("/gds/contact");
}
}
ContactController를 만들고 제가 메일 기능 api를 생성해줍니다.
해당 api는 요청 변수로 MultiPartHttpServletRequest, RequestDto 객체를 받습니다.
- MultiPartHttpServletRequest : html에서 form 태그를 통해 전달받은 이미지나 동영상과 같은 요청 파일
- RequestDto : html에서 form 태그를 통해 전달받은 json 형식으로 매핑된 요청 데이터
MultiPartHttpServletRequest 로 들어온 파일들은 getFile로 단일 MultiPartFile 로 받아오거나 getFiles로 다중 MultiPartFile로 받아올 수 있습니다.
제가 적은 getFiles를 기준으로 설명하면, html에 name 속성이 contactImage라는 값을 가진 태그의 파일들을 받아온다는 뜻입니다.
이제 받아온 FIle들과 요청 Json 데이터를 우선 ContactService 로 넘겨 메일 보내고 정상적으로 수행되면 RedirectView로 페이지를 넘어가게끔 설정해주었습니다.
ContactService
@Slf4j
@RequiredArgsConstructor
@Service
public class ContactService {
private final ContactRepository contactRepository;
private final MediaUploadInterface mediaUploadInterface;
private final MailService mailService;
// 문의 작성 기능 service
public void contactInquiry(List<MultipartFile> contactFile, ContactRequestDto contactRequestDto) throws IOException {
log.info("문의 작성 기능 service 진입 - 문의자 : {}", contactRequestDto.getContactor());
Contact contact = Contact.builder()
.contactor(contactRequestDto.getContactor()) // 문의자
.address(contactRequestDto.getAddress()) // 문의자 주소
.phoneNumber(contactRequestDto.getPhoneNumber()) // 문의자 전화번호
.email(contactRequestDto.getEmail()) // 문의자 이메일
.additionalInfo(contactRequestDto.getAdditionalInfo()) // 문의자 추가 정보
.build();
// 문의 작성 내역 저장
contactRepository.save(contact);
log.info("[Success] 문의 사항 생성 완료");
// 문의 사항 시 포함된 파일 등록
if(mediaUploadInterface.contactUploadFile(contactFile, contact, "contact")){
log.info("[Success] 문의 사항 관련 파일들이 정상적으로 등록 되었습니다.");
}else{
log.info("[Error] 문의 사항 관련 파일들의 업로드가 실패하였습니다.");
}
// 이메일 보내기
mailService.sendSimpleEmail(contactRequestDto);
}
}
전달받은 데이터들을 가지고 Contact 엔티티에 문의 내역을 저장, 같이 넘어온 File들도 DB에 저장한 뒤, MailService로 메일로 넘길 데이터를 넘깁니다.
MailService 에서 실제로 메일을 전송할 로직들이 실행됩니다.
MailService
@Slf4j
@RequiredArgsConstructor
@Service
public class MailService {
private final JavaMailSender javaMailSender;
// 단순 문자 메일 보내기
public void sendSimpleEmail(ContactRequestDto contactRequestDto){
try {
// 단순 문자 메일을 보낼 수 있는 객체 생성
SimpleMailMessage message = new SimpleMailMessage();
// message.setFrom("test@test.com"); // 메일을 보낼 송신 이메일
message.setTo("receiver@test.com"); // 메일을 받을 목적지 이메일
message.setSubject("[문의] " + contactRequestDto.getContactor() + "님의 문의"); // 메일 제목
message.setText(
" - 문의자 : " + contactRequestDto.getContactor() + "\n" +
" - 주소 : " + contactRequestDto.getAddress() + "\n" +
" - 전화번호 : " + contactRequestDto.getPhoneNumber() + "\n" +
" - 문의 내용 : " + "\n" + contactRequestDto.getAdditionalInfo() + "\n"
); // 메일 내용
javaMailSender.send(message);
} catch (MailSendException m){
m.printStackTrace();
}
}
// HTML 메일 보내기
public void sendHTMLEmail(){
}
// 6자리 랜덤 비밀번호 생성
public void createRandomPw(){
}
}
전달받은 데이터들을 가지고 sendSimpleEmail 함수를 통해 메일을 보냅니다.
SimpleMailMessage 객체를 생성하여 메일을 보낼 내용들을 넣어줍니다.
- setFrom("{메일을 보낼 송신 측 이메일}") : 메일을 보낼 송신 측 이메일
- setTo("{메일을 받을 수신 측 이메일}") : 메일을 받을 수신 측 이메일
- setSubject("{메일 타이틀}") : 메일 제목
- setText("{내용}") : 메일 내용 (\n 기호를 포함하면 내용에 개행이 적용되어 보내지게 됩니다.)
JavaMailSender 객체를 의존성 주입받아 작성한 메일을 실제로 전송합니다.
- send(message) : 작성한 SimpleMailMessage를 실제로 전송하는 함수
아래의 sendHTMLEmail, createRandomPw 함수는 아직 내부 로직을 구현하지는 않았지만 나중에 필요하게 될 시 만들 함수들 입니다.
이제 프로젝트를 실행하여 api를 실행해보겠습니다.
저는 PostMan 과 같이 외부에서 테스트할 수 있는 툴을 사용하는 것이 아니라 html 에서 직접적으로 요청 버튼을 눌러서 확인했습니다.
메일을 확인해보면 정상적으로 작성한 SimpleMailMessage 내용들이 전달된 것을 볼 수 있습니다!
'기술 창고 > Spring' 카테고리의 다른 글
[Spring Boot] Thymeleaf (타임리프) (0) | 2023.11.13 |
---|---|
[Spring Boot] 로컬 환경 + 배포 서버에 파일 업로드 (trasferTo 사용법) (0) | 2023.10.19 |
[Spring Boot] Springdoc-openapi Swagger 적용 (+ JWT 사용 시 적용법) (0) | 2023.10.04 |
[Spring Boot] 구글 Oauth2 인증 및 구글 소셜 로그인 기능 구현 (0) | 2023.09.20 |
[Spring Boot] mp3 파일 정보 추출 (0) | 2023.09.13 |