[spring boot] JavaMailSender 설정하며 알게된 것들

탄이·2023년 7월 24일
0
post-thumbnail

사이드프로젝트를 진행하면서 '메일 발송' 기능을 담당하게 됐다.
회사 소스에 이미 세팅된 걸 파악하기 보단, 내가 처음부터 세팅하면서 알아가고 싶어서 자원했다.

하지만 뜻대로 되지 않아 의외로 시간을 많이 쏟았고 원인은 다음 세 가지였다.

  1. yml 혹은 properties에 작성된 값을 어떻게 바인딩해야 할 지 몰랐음
  2. 각 설정 단계에 대한 이해 부족
  3. 구글의 보안 관련 정책 변경

실질적으로 세팅하는 방법은 다른 포스팅에 적어두려고 한다.
이번엔 시간을 많이 쓴 원인과 알게된 것들을 정리하고자 한다.


1. yml 혹은 properties의 바인딩

가장 근본적인 원인이었다.
application.properties에 smtp 관련 값을 추가해두고 바인딩이 안 되는 것을 모른 채 테스트를 진행했다.

우선 yaml과 properties는 다음과 같은 차이점이 있다.

properties

  • key-value 구조

yaml

  • 계층적 구조
  • prefix 중복 제거 가능

어떤 형식을 쓸 것인지는 취향 차이인 것 같다.

작성된 데이터 바인딩 방법

부끄럽게도 아래 이미지처럼 세팅해두면 자동으로(?) MailProperties에 바인딩되는 줄 알았다...!

모든 과정을 디버깅 찍어가면서 확인하다가 알게된 것이 저렇게만 둔다고 알아서 주입되는 것이 아니라는 점이었다.

방법은 아래와 같다.

1) @Value

@Getter
@Setter
@Component
public final class MailProperties {

    // SMTP 서버
    @Value("${spring.mail.host}")
    private String host;

    // 계정
    @Value("${spring.mail.username}")
    private String username;
    
}

이렇게 필드값마다 어노테이션을 달아주고, Placeholder나 SpEL을 명시해주는 방식이다.
다만, 객체가 생성된 후에 주입이 되기 때문에 객체 생성자가 실행되는 시점에선 @Value 값이 null이 되므로 주의할 필요가 있다.

2) @ConfigurationProperties

@Getter
@Setter
@Configuration
@ConfigurationProperties("mail")
public final class MailProperties {

    // SMTP 서버
    private String host;

    // 계정
    private String username;
    
    //smtp
    private Properties properties;
    
    @Getter
    @Setter
    public static class Properties {
        private Smtp smtp;
        
        @Getter
        @Setter
        public static class Smtp {
        	private boolean auth;
        
        }
    
    }
    
}

이런 방식으로 정의한다.
prefix를 적어줘야하며, inner class를 사용하게 되는 경우 이름을 똑같이 일치 시켜야 한다.
또, setter를 반드시 정의해줘야 한다.
@Value보다 간결하고, inner class 사용 시 좀 더 간편하다는 장점이 있다.

3) @ConstructorBinding

spring boot 2.2부터는 @ConfigurationProperties가 달린 클래스를 변경 불가능하도록 @ConstructorBinding을 쓸 수 있게 됐다.
그래서 final 필드에 대해 값을 주입해준다.

@Getter
@RequiredArgsConstructor
@ConfigurationProperties("mail")
public final class MailProperties {

    // SMTP 서버
    private final String host;

    // 계정
    private final String username;
    
    //smtp
    private final Properties properties;
    
    @Getter
    @RequiredArgsConstructor
    public static final class Properties {
        private final Smtp smtp;
        
        @Getter
        @RequiredArgsConstructor
        public static final class Smtp {
        	private final boolean auth;
        
        }
    
    }
    
}

이 방식은 MailProperties 클래스에 직접적으로 bean을 만들어주지 않는다.
따라서 @EnableConfigurartionProperties를 이용해서 클래스 타입을 명시해줘야한다.


프로젝트에는 @Value 방식을 썼다.

1) 대규모 프로젝트가 아니기에 yml 파일이 여러 개 생성되지 않는다는 점
2) 메일 관련 설정을 MailProperties 외에 다른 클래스에서 산발적으로 호출하지 않는다는 점
3) 세팅에 좀 더 많은 시간을 쓸 수 없었다는 점

을 이유로 그렇게 사용했다.

프로젝트 마무리 시점에 시간적 여유가 된다면 @Value 외에 다른 방식으로 바인딩하게 변경해보고 싶다.

+) 3번 시간 이슈에 대해 좀 더 설명하자면 @ConfigurationProperties를 쓰려고 설정했지만 주입이 안 됐다. @EnableConfigurartionProperties 도 달아줬으나 안 돼 해결하고 싶었다.
하지만 혼자하는 프로젝트가 아니어서 다른 팀원들이 merge를 기다리고 있는 상황이라 우선 @Value로 마무리 지었다.

2. 각 설정 단계에 대한 이해 부족

참고한 블로그 글들, 회사 소스들 모두 방식은 각자에 맞게 했지만 원리는 동일했다.
하지만 각 단계별로 왜 이렇게 세팅 하는지를 이해하지 못하고 베끼다보니 원인 파악도 느렸다.

결국 각 단계별로 필요한 설정 순서는 아래와 같다.
1) .yml 혹은 .properties에 필요한 설정 값을 넣어준다
2) MailProperties에 값을 매핑해준다
3) MailConfig에 javaMailService 메소드를 @Bean 등록 해준다
3-1) javaMailService 메소드 내 JavaMailSender 객체를 생성하고, 설정값을 set해준다
3-2) Properties 객체에 smtp.auth, starttls 등도 put 해준다
3-3) javaMailSender에 session도 세팅해준다
4) password 보안 때문에 Authenticator를 상속받아 MailAuthenticator를 만들어준다
5) PasswordAuthentication 객체를 재정의 해준다

각 순서에 대해 전체적인 그림을 그리고, 현재 어떤 단계에 있는지를 생각하면서 코드를 작성했으면 문제가 발생하는 위치를 빠르게 잡을 수 있지 않았을까 싶다.

3. 구글 정책 변경

대다수의 블로그를 보면, 메일 발송이 안되면 '보안수준이 낮은 앱의 액세스 허용'을 해주라고 나온다.
하지만 구글 정책상 이 허용값 설정 자체가 불가능하게 바뀌었다.

그러다가 발견한 stackOverflow 글.
친절하게 캡쳐를 첨부해 설명해줘서 이해하고 해결했다.

링크 : https://stackoverflow.com/questions/72930539/javax-mail-authenticationfailedexception-535-5-7-8-username-and-password-not-ac


작업을 해보며..

회고 아닌 회고글이 됐지만 이번 작업으로 느낀 점이 많았다.

첫째는 이미 선배 개발자들이 세팅해둔 회사 소스를 눈으로 보는 것보다 내가 직접 설정하면서 겪어야 부족한 부분을 뼈저리게 느낀다는 것이다.
두 번째는 기본기를 확실하게 다져야 하고, 구멍이 숭숭 나 있다는 것이었다.

이렇게 느끼기 위해서 사이드 프로젝트를 시작했는데 역시나 하길 잘 했다.
앞으로 배우고 느낄 게 많아서 행복하다.

profile
백엔드 개발자의 로그

0개의 댓글