[비동기-1편] javax.mail로 메일보내기

2025. 10. 21. 23:53·개인 프로젝트/[2025] Happy2SendingMails
더보기

비동기 시리즈

0편:스프링 초기세팅하기 - https://dev-dx2d2y-log.tistory.com/102 

1편:javax.mail로 메일보내기

1.5편:내부클래스로 코드 간략화 - https://dev-dx2d2y-log.tistory.com/104

2편:비동기처리로 클라이언트 풀어주기 - https://dev-dx2d2y-log.tistory.com/108

오랜만에 간단한 스프링부트 프로젝트를하고 싶어서 간단한 메일전송 프로그램을 만들어보고자한다.

쓰는김에 HTML이랑 타임리프도 한 번 써보고

 

그래서 API 주소는 한 개 밖에 없다.

/mail (POST) -> 메일주소와 내용을 입력받아 메일을 전송함


https://dev-dx2d2y-log.tistory.com/10

 

[GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #5 - OAuth 최종점검

개발 글을 5개를 썼는데 모두 OAuth에 관한 내용이긴한데 오늘은 어제 만든 리프레시 토큰을 통한 액세서 토큰 만료 시 재발급 코드만 짜면 되기 때문에 ㄱㅊ1. JWT AccessToken 재발급 마무리 어제 마

dev-dx2d2y-log.tistory.com

이전에 메일보내기 기능은 해본 적이 있어서 기능만 되살리면 될 것 같다.

 

javax.mail 라이브러리를 사용할 것이고,

implementation 'com.sun.mail:javax.mail:1.6.2'

gradle에 다음과 같은 dependency를 추가해주면 끝

그리고 HTML 폼 입력으로 제목, 내용, 보낼 이메일주소는 모두 입력받을 것이기 때문에 실제로는 그냥 인자만 받아서 전송하면 된다.

 

@Getter
@Builder
public class MailContentDTO {
    private String title;
    private String content;
    private String emailAddress;
}

이렇게 받을 인자를 지정하고

 

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;

@Controller
class MailSender {
    public boolean sendMail(MailContentDTO contents) throws MessagingException, AddressException {
        String user = "#";
        String password = "#";

        String title = contents.getTitle();
        String content = contents.getContent();
        String address = contents.getEmailAddress();

        Properties properties = new Properties();
        properties.put("mail.smtp.host", "smtp.gmail.com");
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.starttls.enable", "true");
        properties.put("mail.smtp.starttls.trust", "true");
        properties.put("mail.smtp.port", "587");

        Session session = Session.getInstance(properties, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(user, password);
            }
        });

        MimeMessage message = new MimeMessage(session);
        try {
            message.setFrom(new InternetAddress(user));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress((address)));
            message.setSubject(title);
            message.setText(content);

            Transport.send(message);
            return true;
        } catch (MessagingException  e) {
            return false;
        }
    }
}

대충 코드를 짰다.

위의 링크에서 달라진 점으로는 반환타입을 ResponseEntity로 하다가 여기는 외부에서 접근하지 못하는 코드라 그냥 boolean으로 반환타입을 지정했다정도? 이펙티브자바에서 배운 것을 좀 써먹어보려고한다. MailSender는 외부용 API가 아니라 그냥 default로 선언


무슨 뜻일까?

코드가 굉장히 복잡하다.

String user = "#";
String password = "#";

String title = contents.getTitle();
String content = contents.getContent();
String address = contents.getEmailAddress();

우선 인스턴스 선언부는 간단하다. user와 password는 이메일을 보낼 계정의 이메일과 비밀번호로, 2단계 인증이 완료된 계정이어야한다. titile, content, address는 각각 메일을 보낼 때 제목, 내용, 보낼 주소다.

 

만약 비밀번호와 계정을 정확히 입력했음에도 불구하고 javax.mail에서 에러가 터질 때가 있는데, 2단계 인증을하거나 2단계 인증을해도 에러가 생길경우 비밀번호 대신 '앱 비밀번호'를 발급받아 입력해도 된다.

https://support.google.com/mail/thread/32832052/application-specific-password-required-%EC%97%90%EB%9F%AC-%EB%B0%9C%EC%83%9D-%EC%97%86%EC%95%A0%EB%8A%94-%EB%B0%A9%EB%B2%95?hl=ko

 

Application-specific password required 에러 발생 없애는 방법 - Gmail 커뮤니티

 

support.google.com


Properties

        Properties properties = new Properties();
        properties.put("mail.smtp.host", "smtp.gmail.com");
        properties.put("mail.smtp.port", "587");
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.starttls.enable", "true");

Properties 객체는 메일을 어떻게 보낼지에 대한 정보를 저장하는 key-value 형태의 자료구조다.

내부에는 Map<Object, Object> 형태로 값이 저장된다. 사실 Properties 객체는 HashTable의 하위클래스이며, HashTable은 Map의 구현체다. 즉, 그냥 자료 구조 중 하나라는 뜻이다.

 

mail.smtp.host = smtp.gmail.com 이라는 것으로 메일을 보낼 때 사용한 smtp 서버를 설정한다. 받는 사람의 것이 아니라 보내는 사람의 것을 기준으로 설정해야하며, 나는 이메일을 보내는 계정이 구글계정이므로 smtp.gmail.com을 사용했다.

 

mail.smtp.host = 587은 서버의 포트를 설정한다.

 

mail.smtp.auth = true 는 메일을 보내기 전에, 사용자의 계정정보와 실제 계정정보를 비교하는 과정을 true로 설정한다. false면 위 과정이 진행되지 않는다. 

 

mail.smtp.starttls.enable = true 는 암호화 소통방식인 starttle를 활성화한다.

 

@Override
    public Object get(Object key) {
        return map.get(key);
    }

    @Override
    public synchronized Object put(Object key, Object value) {
        return map.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        return map.remove(key);
    }

    @Override
    public synchronized void putAll(Map<?, ?> t) {
        map.putAll(t);
    }

    @Override
    public synchronized void clear() {
        map.clear();
    }

이렇게 Map<>과 같은 자료구조의 메서드를 보관 중이다.


Session

Session session = Session.getInstance(properties, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(user, password);
            }
        });

이렇게 Properties에 저장된 설정들은 세션에 저장된다. 위 코드는 별건없고 그냥 Session 객체를 형성하는 정적팩토리메서드다. 

    public static Session getInstance(Properties props,
					Authenticator authenticator) {
	return new Session(props, authenticator);
    }

▲Session.getInstance 코드

Integer에 있는 그거와 동일하다. 역시 이펙티브자바는 유익해

 

암튼 인자로는 아까 저장한 Properties 객체와 Authenticator 객체를 받게되는데, Authenticator 클래스에 정의된 getPasswordAuthentication을 재정의한다. 일회용 익명내부클래스

 

protected PasswordAuthentication getPasswordAuthentication() {
	return null;
    }

getInstance의 인자로 넘겨주는 Authenticaotr의 익명내부클래스인데 별건없고 그냥 PasswordAuthentication 객체를 생성한다고 보면 된다.

 

package javax.mail;

public final class PasswordAuthentication {

    private final String userName;
    private final String password;

    public PasswordAuthentication(String userName, String password) {
	this.userName = userName;
	this.password = password;
    }

    public String getUserName() {
	return userName;
    }

    public String getPassword() {
	return password;
    }
}

 

그리고 PasswordAuthentication 코드. 이게 전부다. 이름이 길고 어려워보이지 그냥 VO에 해당한다.

 

결국에는 Session 객체는 메일 보낼 때의 설정값인 Properties 객체와 이메일을 보내는 계정의 아이디와 비밀번호를 담아두는 PasswordAuthentication 객체를 담아두는 객체 역할을 한다.


MimeMessage

MimeMessage message = new MimeMessage(session);

이렇게 그동안의 정보를 모두 모아 저장해놓은 Session 객체를 MimeMessage 객체에 넣어두게되는데, 여기서 그동안 설정했던 값을 받아 메일을 보낼 수 있는 상태로 변환한다.

 

try {
            message.setFrom(new InternetAddress(user));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress((address)));
            message.setSubject(title);
            message.setText(content);

            Transport.send(message);
            return true;
        } catch (MessagingException  e) {
            return false;
        }

setFrom, addRecipient, setSubject, setText 메서드는 각각 보내는 사람, 보내는 주소, 제목, 내용을 설정한다.

그리고 Transport.send 메서드를 통해 전송되게 되며 이메일이 전송된다. 여기는 코드가 너무 복잡해서 패스

 

..이렇게 메일이 전송되게 된다.

EmailSender 클래스에 접근해서 이메일만 보내면 끝

'개인 프로젝트 > [2025] Happy2SendingMails' 카테고리의 다른 글

[비동기-2편] 비동기처리로 클라이언트 풀어주기  (0) 2025.10.25
[비동기-1.5편]내부클래스 사용해보기  (0) 2025.10.22
'개인 프로젝트/[2025] Happy2SendingMails' 카테고리의 다른 글
  • [비동기-2편] 비동기처리로 클라이언트 풀어주기
  • [비동기-1.5편]내부클래스 사용해보기
Radiata
Radiata
개발을 합니다.
  • Radiata
    DDD
    Radiata
  • 전체
    오늘
    어제
    • 분류 전체보기 (211)
      • 신년사 (3)
        • 2025년 (2)
        • 2026년 (1)
      • CS (59)
        • JVM (12)
        • 백엔드 (20)
        • 언어구현 (1)
        • 객체지향 (1)
        • 논리회로 (5)
        • 컴퓨터구조 (9)
        • 데이터베이스 (1)
        • 컴퓨터 네트워크 (10)
      • 언어공부 (64)
        • Java | Kotlin (48)
        • JavaScript | TypeScript (9)
        • C | C++ (6)
      • 개인 프로젝트 (11)
        • [2025] Happy2SendingMails (3)
        • [2026] 골든리포트! (8)
        • [2026] 순수자바로 개발하기 (0)
        • 기타 이것저것 (0)
      • 팀 프로젝트 (29)
        • [2025][GDG]홍대 맛집 아카이빙 프로젝트 (29)
      • 알고리즘 (13)
        • 백준풀이기록 (11)
      • 놀이터 (0)
      • 에러 수정일지 (2)
      • 고찰 (24)
        • CEOS 23기 회고록 (2)
  • 블로그 메뉴

    • CS
    • 언어공부
    • 개인 프로젝트
    • 팀 프로젝트
    • 알고리즘
    • 고찰
    • 신년사
    • 컬러잇 개발블로그
  • 링크

    • 컬러잇 개발블로그
  • 공지사항

  • 인기 글

  • 태그

    144
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Radiata
[비동기-1편] javax.mail로 메일보내기
상단으로

티스토리툴바