본문 바로가기
JSP/성낙현의 JSP 자바 웹 프로그래밍

SMTP를 활용한 메일 전송하기

by k-mozzi 2023. 7. 5.
반응형
Preface

 

이번 장에선 SMTP를 사용해 메일 전송 폼을 작성해봤다.

 

코드 자체는 어려운 부분이 없어 금방 작성했지만, 계속해서 아래와 같은 오류가 발생했다.

 

DEBUG: setDebug: JavaMail version 1.4.7
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.naver.com", port 465, isSSL false
javax.mail.MessagingException: Could not connect to SMTP host: smtp.naver.com, port: 465;
  nested exception is:
	javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

 

GPT와 구글링 통해 얻은 답을 바탕으로 JavaMail의 버전과 포트번호도 확인해보고, Mac의 Mail 애플리케이션, MS outlook 등을 체크하는 등 이런저런 방법을 전부 시도해봤지만 해결되지 않았다.

 

반쯤 포기한 상태로 개발자 친구의 직장 동료 및 선배분들이 계신 Slack에 질문을 올렸는데, 정말 감사하게도 한 분께서 해결 방법을 알려주셨다.

 

나는 오류 로그를 보고도 당연히 포트 번호나 내 네이버 아이디의 설정 문제로 인해 프로토콜이 거부된 것이라고 생각했었는데,

 

사실 네이버 서버와 SSL 통신 자체가 되지 않았던 것이 원인이었다.

 

serverInfo.put("mail.smtp.ssl.protocols", "TLSv1.2");

 

위 코드를 NaverSMTP 생성자에 추가하면 문제 없이 코드가 작동한다.

 

앞으론 오류 로그를 조금 더 자세히 읽어볼 필요가 있을 것 같다.


 

1. SMTP란?

 

 

- SMTP(Simple Mail Transfer Protocol): 이매일을 보낼 때 사용하는 메일 서버의 기본 프로토콜

 

 

- 네이버 SMTP를 사용하려면 네이버 메일 환경설정에서 POP3/IMAP 설정 탭을 통해 "POP3/IMAP 사용함 버튼"을 활성화시켜야 한다.

 

 

- SMTP는 메일을 보내는 역할만 하고, 메일을 받을 땐 다음과 같은 프로토콜을 이용한다.

1) POP3(Post Office Protocol 3): 클라이언트가 메일 서버에서 메일을 받아오는 프로토콜

→ 메일 서버에 저장된 메일을 사용자 컴퓨터로 가져와서 확인할 수 있게 해준다.

2) IMAP(Internet Message Access Protocol): 사용자가 메일 서버에서 메일을 내려받는 프로토콜

→ 중앙 서버에서 동기화가 이루어지므로, 같은 계정으로 연결된 모든 장치에서 같은 내용이 보인다.

 


 

2. 이메일 전송 프로그램 작성

 

 

- 라이브러리 설치

1) 자바 메일: 메일 서비스와 관련된 전반적인 기능 수행

→ 링크: https://mvnrepository.com/artifact/javax.mail/mail/1.4.7

2) 자바빈즈 액티베이션 프레임워크: 자바메일 API가 MIME 데이터를 관리하기 위해 사용

→ 링크: https://mvnrepository.com/artifact/javax.activation/activation/1.1.1

 

 

- 이메일 작성 페이지

→ 네이버 메일 서버를 사용하므로, 보내는 사람에는 본인의 네이버 메일 주소를 입력해야 한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SMTP 이메일 전송</title>
</head>
<body>
<h2>이메일 전송하기</h2>
<form method="post" action="SendProcess.jsp">
<table border=1>
    <tr>    
        <td>
            보내는 사람 : <input type="text" name="from" value="" />
        </td>
    </tr>
    <tr>    
        <td>
            받는 사람 : <input type="text" name="to" value="" />
        </td>
    </tr>
    <tr>    
        <td>
            제목 : <input type="text" name="subject" size="50" value="" />
        </td>
    </tr>
    <tr>    
        <td>
            형식 :
            <input type="radio" name="format" value="text" checked />Text
            <input type="radio" name="format" value="html" />HTML
        </td>
    </tr>
    <tr>
        <td>
            <textarea name="content" cols="60" rows="10"></textarea>
        </td>
    </tr>
    <tr>
        <td>
            <button type="submit">전송하기</button>
        </td>
    </tr>
</table>
</form>
</body>
</html>

 

 

- 이메일 전송 클래스: 메일 서버에 대한 인증 처리와 전송 처리

→ 네이버 2단계 인증을 꺼두어야 작동한다.

package smtp;

import java.util.Map;
import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

// 네이버 SMTP 서버를 통해 이메일을 전송하는 클래스
public class NaverSMTP {
	private final Properties serverInfo; // 서버 정보
	private final Authenticator auth; // 인증 정보

	public NaverSMTP() {
		// 네이버 SMTP 서버 접속 정보
		serverInfo = new Properties();
		serverInfo.put("mail.smtp.host", "smtp.naver.com");
		serverInfo.put("mail.smtp.port", "465");
		serverInfo.put("mail.smtp.starttls.enable", "true");
		serverInfo.put("mail.smtp.auth", "true");
		serverInfo.put("mail.smtp.debug", "true");
		serverInfo.put("mail.smtp.socketFactory.port", "465");
		serverInfo.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		serverInfo.put("mail.smtp.socketFactory.fallback", "false");
        
        	// 네이버 서버와 ssl 통신이 되지 않을 경우 추가
		serverInfo.put("mail.smtp.ssl.protocols", "TLSv1.2");

		// 사용자 인증 정보
		auth = new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication("네이버 아이디", "네이버 비밀번호");
			}
		};
	}

	// 주어진 메일 내용을 네이버 SMTP 서버를 통해 전송
	public void emailSending(Map<String, String> mailInfo) throws MessagingException {
		// 1. 세션 생성
		Session session = Session.getInstance(serverInfo, auth);
		session.setDebug(true);

		// 2. 메시지 작성
		MimeMessage msg = new MimeMessage(session);
		msg.setFrom(new InternetAddress(mailInfo.get("from"))); // 보내는 사람
		msg.addRecipient(Message.RecipientType.TO, new InternetAddress(mailInfo.get("to"))); // 받는 사람
		msg.setSubject(mailInfo.get("subject")); // 제목
		msg.setContent(mailInfo.get("content"), mailInfo.get("format")); // 내용

		// 3. 전송
		Transport.send(msg);
	}
}

 

 

- HTML 형식의 메일 템플릿

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MustHave 메일 템플릿</title>
</head>
<body>
    <h2>MustHave 메일 템플릿</h2>
    <table border=1 width="100%">
        <tr>
            <td width="50">내용</td>
            <td>__CONTENT__</td>
        </tr>
        <tr>
            <td>이미지</td>
            <td><img src="https://github.com/goldenrabbit2020/musthave_jsp/blob/main/GOLDEN-RABBIT_LOGO_150.png?raw=true" alt="골든래빗" /></td>
        </tr>
    </table>
</body>
</html>

 

 

- 메일 전송 처리 페이지

<%@ page import="java.io.BufferedReader"%>
<%@ page import="java.io.FileReader"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ page import="smtp.NaverSMTP"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
// 폼값(이메일 내용) 저장
Map<String, String> emailInfo = new HashMap<String, String>();
emailInfo.put("from", request.getParameter("from"));  // 보내는 사람
emailInfo.put("to", request.getParameter("to"));      // 받는 사람
emailInfo.put("subject", request.getParameter("subject"));  // 제목

// 내용은 메일 포맷에 따라 다르게 처리
String content = request.getParameter("content");  // 내용
String format = request.getParameter("format");    // 메일 포맷(text 혹은 html)
if (format.equals("text")) {
    // 텍스트 포맷일 때는 그대로 저장
    emailInfo.put("content", content);
    emailInfo.put("format", "text/plain;charset=UTF-8");
}
else if (format.equals("html")) {
    // HTML 포맷일 때는 HTML 형태로 변환해 저장
    content = content.replace("\r\n", "<br/>");  // 줄바꿈을 HTML 형태로 수정

    String htmlContent = ""; // HTML용으로 변환된 내용을 담을 변수
    try {
        // HTML 메일용 템플릿 파일 읽기
        String templatePath = application.getRealPath("/JSP_Example_Code/ch16/MailForm.html");
        BufferedReader br = new BufferedReader(new FileReader(templatePath));

        // 한 줄씩 읽어 htmlContent 변수에 저장
        String oneLine;
        while ((oneLine = br.readLine()) != null) {
            htmlContent += oneLine + "\n";
        }
        br.close();
    }
    catch (Exception e) {
        e.printStackTrace();
    }

    // 템플릿의 "__CONTENT__" 부분을 메일 내용 대체
    htmlContent = htmlContent.replace("__CONTENT__", content);
    // 변환된 내용을 저장
    emailInfo.put("content", htmlContent);
    emailInfo.put("format", "text/html;charset=UTF-8");
}

try {
    NaverSMTP smtpServer = new NaverSMTP();  // 메일 전송 클래스 생성
    smtpServer.emailSending(emailInfo);      // 전송
    out.print("이메일 전송 성공");
}
catch (Exception e) {
    out.print("이메일 전송 실패");
    e.printStackTrace();
}
%>

 

728x90
반응형

댓글