본문 바로가기

Tip

[spring] 메일 보내기

스프링프레임웍 JavaMailSenderImpl을 이용해서 메일을 발송하는 방법에 대해 알아 보겠습니다. 

 

메일을 발송하려면 메일을 발송해주는 메일 서버(SMTP Server)가 있어야 합니다. 메일 서버를 통해 메일을 보낼때 서버에 접속하는 방법은 보통 두 가지가 있습니다.

 

첫 번째는 서버가 릴레이를 허용하는 경우 입니다. 메일 서버가 릴레이를 허용하는 경우 그 메일 서버에 계정이 없더라도 메일을 발송할 수 있습니다. 이렇게 릴레이를 허용하는 경우 타인에 의해 스팸 메일을 보내는데 악용이 될 수 있으므로 특정 IP에서만 릴레이가 되도록 하는게 일반적입니다.

 

두 번째는 메일 서버에 계정이 있어서 아이디와 비밀번호로 인증후 메일을 보내는 방법 입니다. 요즘은 두 번째가 가장 일반적인 방법일 것입니다.

 

1. 메일 발송을 위한 의존성을 pom.xml 파일에 추가 합니다.

 

org.springframework.mail.javamail.JavaMailSenderImpl을 사용하기 위해 필요합니다. ${org.springframework-version} 은 자신에 사용중인 스프링 프레임웍 버전을 사용하면 되겠습니다.

 

MIME 형식의 메일을 보내기 위해서 JavaMailSender 인터페이스를 사용하는 것입니다. 이것을 사용함으로해서 javax.mail.MimeMessage 를 사용할 수 있게 됩니다. (스프링프레임웍 기본 메일 인터페이스는 MainSender입니다. 이것은 스프링프레임웍의 SimpleMailMessage를 사용하고, 첨부파일 등을 지원하지 않습니다.)

 

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${org.springframework-version}</version> </dependency>

 

javax.mail.internet.MimeMessage를 사용하기 위해 필요합니다.

 

<dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.5.6</version> </dependency>

 

 

 

2. "mailSender" 빈을 설정합니다.

 

i) 릴레이를 허용하는 서버를 사용할 경우 빈 설정 입니다.

 

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.yourEmailDomain.co.kr"/> </bean>

 

이 경우 SMTP 서버의 주소만 있으면 됩니다. SMTP 의 기본 port 번호는 25 입니다. 기본 포트번호를 사용한다면 port 는 생략할 수 있습니다. 만약 발송용 SMTP 서버의 포트 번호를 변경하여 사용한다면 port 프로퍼티를 추가해서 변경된 port 번호를 입력하면 되겠습니다. (<property name="port" value="25" />)

 

릴레이를 허용하는 서버의 경우 명령창에서 telnet 명령으로 간단하게 메일 발송을 테스트 해볼 수 있습니다. 다음은 Microsoft IIS 메일서버를 이용해서 명령창으로 메일을 발송하는 예 입니다. 나오는 메세지는 서버에 따라 다를 수 있습니다.

 

C:\>telnet smtp.yourdomain.co.kr 25 220 Server_Hostname Microsoft ESMTP MAIL Service, Version: ... HELO localhost 250 Server_Hostname Hello [xxx.xxx.xxx.xxx] MAIL FROM: 보내는_사람_메일주소 250 2.1.0 보내는_사람_메일주소....Sender OK RCPT TO: 받는_사람_메일주소 250 2.1.5 받는_사람_메일주소 DATA 354 Start mail input; end with <CRLF>.<CRLF> Subject: 여기에 메일 제목이 옵니다. 메일내용이 여기에 옵니다. . 250 2.6.0 <xxxxxx@smtp_hostname> Queued mail for delivery 이스케이프 문자: 'CTL+]' Microsoft Telnet> quit

 

DATA 명령후에 Subject: 다음에 제목을 작성하고 엔터키를 두번 누릅니다. 메일 내용을 작성한 후 라인의 첫글자로 마침표(.) 를 입력하고, 바로 엔터키를 누르면 내용 작성이 끝났다는 표시 입니다.

 

텔넷에서 빠져나오기 위해서 'CTRL + ]' 를 누르고 Telnet 프롬프트가 나오면 quit를 입력하여 종료합니다.

 

 

ii) 아이디/비밀번호로 인증후 발송하는 설정 입니다.

 

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.yourEmailDomain.co.kr"/> <property name="port" value="25" /> <property name="username" value="yourUserName"/> <property name="password" value="yourPassword"/> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> </props> </property> </bean>

 

이 방식은 기본적인 방법이고, 요즘은 메일 프로토콜도 암호화 하기 때문에 좀더 복잡한 설정을 하여야 하는 경우도 있을 것입니다.

 

 

iii) 구글 계정으로 메일을 발송하는 설정입니다.

 

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.gmail.com" /> <property name="port" value="587" /> <property name="username" value="your_id@gmail.com" /> <property name="password" value="your_password" /> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.smtp.starttls.enable">true</prop> </props> </property> </bean>

 

구글은 인증에 TLS를 사용합니다. TLS는 SSL과 같은 것인데, SSL 3.0 부터 TLS 라고 부른다고 합니다. 통신에 사용되는 데이터를 암호화 하는 것입니다. 하지만 이 설정만으로는 구글 계정으로 메일을 발송할 수 없었습니다. 이 설정으로 메일을 발송하면 (설정 정보가 정확했다면),  보내는 구글 계정으로 다음과 같은 메일이 올 것입니다.

 

아마도 구글 메일의 2차 인증 부분이 문제가 되는것이 아닌가 생각됩니다. 그래도 구글 계정을 통해 메일을 발송하려면 아래쪽의 "보안 수준이 낮은 앱에 액세스하도록 허용"을 클릭해서 허용을 하면 메일이 발송이 됩니다.

 

 

3. 메일을 발송하는 방법입니다.

 

메일 발송을 위해 import 되는 클래스들 입니다. 메세지 객체는 텍스트 데이터만을 전송하는 SimpleMailMessage와 멀티파트 데이터를 처리할 수 있는 MimeMessage 를 사용할 수 있습니다. 여기서는 MimeMessage만 알아보도록 하겠습니다.

 

import javax.mail.internet.MimeMessage; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.core.io.FileSystemResource;

 

메일을 발송하는 곳에서 스프링프레임웍 설정에서 정의한 "mailSender" 빈을 인젝션 하여 사용합니다.

 

@Autowired private JavaMailSenderImpl mailSender;

 

 

i) 제목과 내용만을 보내는 기본 발송입니다.

 

@RequestMapping(value = "/sendMail.do") public String sendMail(final MailVO vo) { final MimeMessagePreparator preparator = new MimeMessagePreparator() { @Override public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); helper.setFrom(vo.getFrom()); helper.setTo(vo.getTo()); helper.setSubject(vo.getSubject()); helper.setText(vo.getContents(), true); } }; mailSender.send(preparator); return "result"; }

 

MimeMessage 객체를 직접 생성하여 메일을 발송하는 것도 가능하지만, MimeMessagePreparator MimeMessageHelper를 사용하는 것이 가장 간단한 방법입니다.

 

MimeMessageHelper 생성자의 두 번째 인자 true  멀티파트 메세지를 사용하겠다는 뜻 입니다.

 

new MimeMessageHelper(mimeMessage, true, "UTF-8");

 

메일 내용을 채우는 setText() 메소드의 두 번째 인자 true 는 html 을 사용하겠다는 뜻 입니다.

 

helper.setText(vo.getContents(), true);

 

메일 주소에 이름과 이메일 주소를 모두 표기 하려면 다음과 같이 하면 됩니다.

 

helper.setFrom("YourName <your_email@address>");

 

 

ii) 파일 첨부하여 발송하는 방법입니다.

 

@RequestMapping(value = "/sendMailAttach.do") public String sendMailAttach(final MailVO vo) { final MimeMessagePreparator preparator = new MimeMessagePreparator() { @Override public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); helper.setFrom(vo.getFrom()); helper.setTo(vo.getTo()); helper.setSubject(vo.getSubject()); helper.setText(vo.getContents(), true); FileSystemResource file = new FileSystemResource(new File("E:/test.hwp")); helper.addAttachment("test.hwp", file); } }; mailSender.send(preparator); return "result"; }

 

FileSystemResource 객체란 만들어서 helper.addAttachment(); 를 호출하여 첨부 합니다.

 

 

iii) 첨부파일이 아니라 메일 메세지 내에 이미지를 삽입하는 방법입니다.

 

@RequestMapping(value = "/sendMailInline.do") public String sendMailInline(final MailVO vo) { final MimeMessagePreparator preparator = new MimeMessagePreparator() { @Override public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); helper.setFrom(vo.getFrom()); helper.setTo(vo.getTo()); helper.setSubject(vo.getSubject()); String contents = vo.getContents() + "<img src=\"cid:DUKE.gif\">"; helper.setText(contents, true); FileSystemResource file = new FileSystemResource(new File("E:/DUKE.gif")); helper.addInline("DUKE.gif", file); } }; mailSender.send(preparator); return "result"; }

 

이미지를 삽입하고자 하는 부분에 "<img src=\"cid:DUKE.gif\" />" 와 같이 src 속성에 "cid:이미지를_구분하는_값" 을 입력합니다.

 

FileSystemResource file = new FileSystemResource(new File("E:/DUKE.gif"));

helper.addInline("DUKE.gif", file);

 

삽입하고자 하는 이미지 파일에 대한 FileSystemResource 객체를 생성하고, helper.addInline("이미지를_구분하는_값", file); 를 호출 하여 삽입합니다.

 

 

4. 대량 메일을 발송하는 방법

 

메일을 대량으로 발송하려면 백그라운드로 실행되는 batch 작업으로 구현하는 것이 일반적이겠습니다. 하지만 여기서는 발송 버튼 클릭시 수백건 정도의 메일을 루핑하면서 발송할 때 좀더 효율적인 방법을 알아 보겠습니다.

 

mailSender.send() 메소드를 호출해서 메일을 발송할때 마다 메일서버와의 연결이 수행됩니다. mailSender.send() 메소드에는 MimeMessagePreparator의 배열을 인자로 받는 오버로딩된 메소드가 있습니다. 이 메소드를 사용하면 한번의 연결을 사용해서 다수의 메일을 발송할 수 있으므로 좀 더 효율적으로 메일을 발송할 수 있습니다. 다음과 같이 사용합니다.(실제로는 배열을 인자로 받는 것이 아니라 가변인자를 받습니다. 가변인자는 배열을 입력해도 됩니다.)

 

MimeMessagePreparator[] preparators = new MimeMessagePreparator[mails.size()]; int i = 0; for(final MailVO vo: mails) { preparators[i++] = new MimeMessagePreparator() { @Override public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); helper.setFrom(vo.getFrom()); helper.setTo(vo.getTo()); helper.setSubject(vo.getSubject()); helper.setText(vo.getContents(), true); } }; } mailSender.send(preparators);

 

 

감사합니다.
출처: https://offbyone.tistory.com/167 [쉬고 싶은 개발자]