Spring Framework (9) - Java Mail Sender Day 56

jaehoon ahn·2025년 12월 8일

Spring boot

목록 보기
7/20
post-thumbnail

Java Mail Sender

  • 자바에서 메일을 보낼 수 있게 해주는 모듈/라이브러리
  • 구글 SMTP 서버 + 자바를 사용
  • 2단계 인증 + APP KEY(앱 비밀번호)를 발급받아서 사용(인증절차가 될 것임)
  • 이메일을 보내는데 사용되는 인터페이스로 JavaMailSenderImpl 클래스를 통해 직접적으로 구현됨.
  • SMTP 서버를 사용하여 이메일을 보내기 위한 구성을 제공함

SMTP

SMTP (Simple Mail Transfer Protocol): 이메일을 전송하는 데 사용되는 표준 통신 프로토콜.
	 *	-> 이메일 클라이언트가 이메일을 작성하여 전송할 때, SMTP 서버를 통해 수신자의 이메일
	 * 서버로 메일을 전달하는 역할
	 * 	-> 기본적으로 포트 25를 사용하지만, 보안을 강화한 포트 587이나 포트 
	 * 465(SSL/TLS)가 자주 사용됨
	 *
	 * 동작방식
	 *
	1. Spring Boot 애플리케이션에서 내가 이메일을 보냄
	2. Google SMTP 서버로 전송 (Google 서버가 "중간 배달자" 역할을 하는 셈)
	3. Google SMTP 서버가 이메일 주소에 맞는 최종 수신자의 이메일 서버(예: Gmail, Yahoo, 
	   회사 이메일 서버)로 이메일을 다시 보냄.
	4. 수신자가 이메일을 받음
	 *
	 *
	 * (참고)
	 * HTTP (Hypertext Transfer Protocol): 웹 브라우저와 서버 간에 웹 페이지나 파일을 
	 * 주고받는 데 사용
	 *
	 * 전송 방향
	 * - HTTP: 클라이언트-서버 간 통신을 기반으로 하며, 클라이언트가 서버에 요청(Request)을 
	 * 보내고 서버가 응답(Response).
	 *  - SMTP: 서버 간 통신이 가능하며, 이메일을 발신 서버에서 수신 서버로 전달하는 식으로 
	 * 동작함.

개발자 쪽에서 EMAIL이 전달 ⇒ Google SMTP가 받음 ⇒ 사용자

⇒ 즉, Google SMTP는 중간 배달자 역할을 함

⇒ 구글에서 만든 SMTP 프로토콜을 사용하는 것, 사용자는 어떠한 이메일 형식이든 상관이 없음

@Value

⇒ properties에 작성된 내용 중 key가 일치하는 value값을 얻어와 필드에 대입


public class EmailConfig {
.
.	
	
	@Value("${spring.mail.username}")
	private String userName;
	@Value("${spring.mail.password}")
	private String password;
.
.
// ${} 안에 key값 넣기	
}

2단계 인증하기

구글 앱 비밀번호 생성

.properties파일에 작성하기

spring.mail.username=이메일
spring.mail.password=생성된 앱 비밀번호 (빈칸없이)

EmailConfing.java 작성하기

package edu.kh.project.common.config;

import java.util.Properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Configuration
@PropertySource("classpath:/config.properties")
// classpath => src/main/resources
// properties에 있는 코드를 가져오기 위해 쓰는 어노테이션
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmailConfig {
	/*
	 *
	 * SMTP (Simple Mail Transfer Protocol): 이메일을 전송하는 데 사용되는 표준 통신 프로토콜. -> 이메일
	 * 클라이언트가 이메일을 작성하여 전송할 때, SMTP 서버를 통해 수신자의 이메일 서버로 메일을 전달하는 역할 -> 기본적으로 포트 25를
	 * 사용하지만, 보안을 강화한 포트 587이나 포트 465(SSL/TLS)가 자주 사용됨
	 *
	 * 동작방식
	 *
	 * 1. Spring Boot 애플리케이션에서 내가 이메일을 보냄 2. Google SMTP 서버로 전송 (Google 서버가 "중간 배달자"
	 * 역할을 하는 셈) 3. Google SMTP 서버가 이메일 주소에 맞는 최종 수신자의 이메일 서버(예: Gmail, Yahoo, 회사
	 * 이메일 서버)로 이메일을 다시 보냄. 4. 수신자가 이메일을 받음
	 *
	 *
	 * (참고) HTTP (Hypertext Transfer Protocol): 웹 브라우저와 서버 간에 웹 페이지나 파일을 주고받는 데 사용
	 *
	 * 전송 방향 - HTTP: 클라이언트-서버 간 통신을 기반으로 하며, 클라이언트가 서버에 요청(Request)을 보내고 서버가
	 * 응답(Response). - SMTP: 서버 간 통신이 가능하며, 이메일을 발신 서버에서 수신 서버로 전달하는 식으로 동작함.
	 */

	// @Value : properties에 작성된 내용 중
	// key가 일치하는 value값을 얻어와 필드에 대입
	@Value("${spring.mail.username}")
	private String userName;
	@Value("${spring.mail.password}")
	private String password;

	@Bean
	public JavaMailSender javaMailSender() {
		// Spring 에서 JavaMailSender를 구성하는 Bean을 정의하고자 만든 메서드
		// JavaMailSender : 이메일을 보내는데 사용되는 인터페이스로
		// JavaMailSenderImpl 클래스를 통해 직접적으로 구현됨.
		// SMTP 서버를 사용하여 이메일을 보내기 위한 구성을 제공함
		JavaMailSenderImpl mailSender = new JavaMailSenderImpl();

		Properties prop = new Properties();
		prop.setProperty("mail.transport.protocol", "smtp"); // 전송 프로토콜을 설정. 여기서는 SMTP를 사용
		prop.setProperty("mail.smtp.auth", "true"); // SMTP 서버 인증을 사용할지 여부를 설정함.
													// true로 설정되어 있으므로 인증이 사용됨
													// SMTP 서버를 사용하여 이메일을 보내려면 보안 상의 이유로 인증이 필요.
													// (사용자이름(이메일)과 비밀번호(앱비밀번호) 확인)
		prop.setProperty("mail.smtp.starttls.enable", "true"); // STARTTLS를 사용하여 안전한 연결을 활성화할지 여부를 설정
		prop.setProperty("mail.debug", "true"); // 디버그 모드를 설정
		prop.setProperty("mail.smtp.ssl.trust", "smtp.gmail.com"); // 신뢰할 수 있는 SMTP 서버 호스트를 지정
		prop.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); // SSL 프로토콜을 설정. 여기서는 TLSv1.2를 사용

		mailSender.setUsername(userName); // 이메일 계정의 사용자
		mailSender.setPassword(password); // 이메일 계정의 비밀번호
		mailSender.setHost("smtp.gmail.com"); // SMTP 서버 호스트를 설정.
		mailSender.setPort(587); // SMTP 서버의 포트 587로 설정
		mailSender.setDefaultEncoding("UTF-8"); // 기본 인코딩 설정
		mailSender.setJavaMailProperties(prop);
		// JavaMail의 속성을 설정(앞서 정의해 둔 prop에 있는 설정들을 적용한 것)
		
		return mailSender;
		// 위처럼 각종 설정이 적용된 JavaMailSender를
		// Bean으로 등록하여 
		// Spring 애플리케이션에서 이메일을 보내기 위한 구성을 제공함
		
	}

}

흐름 정리

 <div class="signUp-input-area">
            <input
              type="text"
              name="memberEmail"
              id="memberEmail"
              placeholder="아이디(이메일)"
              maxlength="30"
              autocomplete="off"
            />
		<button id="sendAuthKeyBtn" type="button">인증번호 받기</button>
	</div>

인증번호 받기 클릭 시

sendAuthKeyBtn.addEventListener("click", () => {
	.
	.
	.
	fetch("/email/signup", {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: memberEmail.value,
		// 단일값인 경우 JSON 형식으로 전달이 됌
	})
		.then((resp) => resp.text())
		.then((result) => {
			if (result == 1) {
				console.log("인증 번호 발송 성공");
			} else {
				console.log("인증 번호 발송 실패!!!");
			}
		});
	.
	.
});
  • 등록된 이벤트를 실행
  • "/email/signup” POST 요청을 받을 수 있는 controller를 아래와 같이 생성
	@ResponseBody
	@PostMapping("signup")
	public int signup(@RequestBody String email) {
		String authKey = service.sendEmail("signup", email);
		if (authKey != null) {
			// 인증번호가 반환되어 돌아옴
			// == 이메일 보내기 성공
			return 1;
		}
		// 이메일 보내기 실패
		return 0;
	}

⇒ signup 메서드를 통해서

  1. 인증키 생성(UUID)

  2. 인증키 update or insert

    @Transactional(rollbackFor = Exception.class) // 메서드 레벨에서도 이용 가능
    	public boolean storeAuthKey(Map<String, String> map) {
    		// 1. 기존 이메일에 대한 인증키 update 수행
    		int result = mapper.updateAuthKey(map);
    
    		// 2. update 실패 시 insert 수행
    		if (result == 0) {
    			result = mapper.insertAuthKey(map);
    		}
    		// 3. 성공 여부 반환 (true / false)
    		return result > 0;
    	}

⇒ 생성 여부에 따라서

if (!storeAuthKey(map)) {
			return null;
}

위와 같이 DB 저장이 실패를 했다면 null, 아니면 다음 단계로

MimeMessage mimeMessage = mailSender.createMimeMessage();
		// 메일 발송 시 사용하는 객체

		try {
			// 메일 발송을 도와주는 Helper 클래스 사용해서 파일 첨부 or html 등 템플릿으로 설정해서 보낼지
			// 쉽게 처리할 수 있도록 도와주는 클래스

			MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
			// - mimeMessage : MimeMessage 객체로, 이메일 메시지의 내용을 담고있음
			// (이메일의 본문, 제목, 수신자 정보 등을 포함하고 있음)
			// - true : 파일 첨부를 사용할 지 말지에 대한 여부를 지정해주는 것
			// (파일첨부 및 내부 이미지 삽입 가능)
			// - "UTF-8" : 이메일 내용이 UTF-8 인코딩으로 전송

			// 메일 기본 정보 셋팅
			helper.setTo(email); // 받는 사람(수신자)
			helper.setSubject("[boardProject] 회원 가입 인증번호 입니다."); // 제목 세팅
			helper.setText(loadHtml(authKey, type), true);
			// setText => html을 보내고 싶을 때, 첫번째는 html결과, 그리고 true는 html 요소를
			// 전달할지 말지에 대한 매개변수
			// HTML 내용 설정

			// helper.setText("인증번호 입니다 : " + authKey);
			// 인증번호 입니다 : agdsey

			// 메일에 이미지를 첨부할 것인데, 어떤 이미지를 첨부할 지 넣는것(여기선 로고를 넣을 것)
			helper.addInline("logo", new ClassPathResource("static/images/logo.jpg"));

			// 실제 메일 발송
			mailSender.send(mimeMessage);
			
			return authKey;
		} catch (Exception e) {
			e.printStackTrace();
			return null; // 메일 발송 실패 시 null 반환
		}

0개의 댓글