본문 바로가기

문제해결

IllegalArgumentException - 구글 SMTP 메일 전송

► 상황

구글 SMTP 메일전송 실습을 진행했다.

application-local.yml 파일을 설정하고 EmailConfiguration 클래스에 @Value 어노테이션을 사용해 경로를 지정해줬다. 그런다음 SimpleEmailSendable 클래스와 이벤트클래스, 이벤트리스너 클래스까지 작성한 뒤 MemberService 클래스에 이벤트 발생 로직을 추가 한 뒤 애플리케이션을 실행했다. 그런데 아래와 같은 오류 메시지가 떴다.

Caused by: java.lang.IllegalArgumentException: 
Could not resolve placeholder 'mail.smtp.host' in value "${mail.smtp.host}"

 

► 에러내용

이 에러는 Spring 프레임워크에서 mail.smtp.host와 같은 placeholder(플레이스홀더)를 찾을 수 없어서 발생했다. @Value 어노테이션을 사용해 EmailConfiguration 클래스에서 이 값을 가져올 때 Spring이 이러한 값을 찾지 못했기 때문이다.

 

 

► 시도

1. application.yml 파일의 들여쓰기, 양식 등을 charGPT를 통해 점검했다. 문제가 없었다.

 

2. 모든 @Value("${경로}") 의 경로에 하드코딩을 했다. 그랬더니 문제 없이 잘 됐다. 

 

3. @ConfigurationProperties(prefix = "mail.smtp") 설정 추가.

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "mail.smtp")
public class EmailConfiguration {

    private String host;
    private int port;
    private String username;
    private String password;
    private String auth;
    private String tlsEnable; // (1)

하지만 yml 파일에서 (1) 의 경로(mail.smtp.starttls.enable)를 표현하기 위해 static 클래스를 만들어 그안에 필드를 넣어야 했고, 이것은 아래와 같은  또다른 에러를 발생시켰다. 해결방법을 알아봤으나 이 내용을 이해하기엔 아직 학습이 부족하고 또 레퍼런스 코드에선 @Valid 어노테이션을 사용하고 있기에 우선 넘어갔다. 나중에 다시 찾아보기 위해 아래에 더보기(접은글)로 남겨놓았다.

더보기

UnsatisfiedDependencyException 예외는 스프링 프레임워크에서 빈을 생성하거나 주입할 때, 해당 빈의 의존성 주입이 실패했을 때 발생합니다.

이 예외가 발생한 원인은 emailSender 빈 생성 시 생성자 파라미터인 simpleEmailSendable 빈을 주입할 때, 해당 빈의 생성자에서 다시 javaMailSender 빈을 주입하려고 했지만, javaMailSender 빈 생성 과정에서 NullPointerException이 발생했기 때문입니다.

따라서 이 예외를 해결하려면, javaMailSender 빈을 정상적으로 생성할 수 있도록 설정을 수정하거나, simpleEmailSendable 빈 생성자에서 javaMailSender 빈 주입 과정에서 발생한 NullPointerException을 해결해야 합니다.

 

 

4. 레퍼런스 코드 복사 붙여넣기, 를 시도했으나 똑같은 오류가 났고 뭔가 이상함을 느꼈다. 하지만 이때도 나는 관련한 클래스들의 코드와 어노테이션에 문제가 있을것이란 생각에 계속 그쪽에서만 해결방법을 찾으려고 했다.

 

 

 

5. 아무리 많은 동영상과 예시를 봐도 분명 코드에는 문제가 없었다. 그러다 문득 이때까지 사용했던 yml 설정파일은 'application.yml ' 이었는데 왜 이번 실습에서 사용하는 건 'application-local.yml' 이지? 라는 생각이 스쳤다. 여러가지 경우 중 Spring Boot 애플리케이션에서 YAML 파일이 제대로 로드되지 않은 경우, YAML 파일이 제대로 읽히지 않는 경우에 이런 문제가 생길 수 있다는 것을 알게됐다. Spring Boot는 application.yml 파일에서 profiles 를 사용해 다양한 환경에서 다른 설정을 사용할 수 있게끔 설정할 수 있다...

 

그러니까, 나는 지금 application-local.yml 파일에 필요한 설정정보를 넣어놨고 이것을 기본설정파일로 사용하기 위해선 application.yml 파일에 spring.prifiles.active 속성을 local로 추가해야 했던거다. 정말 속상했지만 이전에는 이론으로 와닿지 않았던 것을 이번 시간을 계기로 제대로 경험해본거라 생각한다. 

 

 


 

► 결론

spring.profiles.active는 애플리케이션 실행 환경에 따라 다르게 설정된다. 예를 들어, 로컬 개발 환경에서는 local 프로필을, 테스트 환경에서는 test 프로필을, 운영 환경에서는 production 프로필을 사용할 수 있다. 만약 spring.profiles.active를 설정하지 않은 경우 기본값으로 default 프로필이 사용된다. 즉, 명시적으로 설정하지 않아도 되지만, 기본값으로 동작할 수 있도록 기본 프로필을 설정해두는 것이 좋다.

 

# application.yml

# 기본 프로필 설정
spring:
  profiles:
    active: default

# 공통 설정
logging:
  level:
    root: INFO
    com.example: DEBUG

server:
  port: 8080

# default 프로필 설정
---
spring:
  profiles: default

# default 프로필에서만 사용하는 설정
datasource:
  url: jdbc:mysql://localhost:3306/mydb
  username: myuser
  password: mypass

# production 프로필 설정
---
spring:
  profiles: production

# production 프로필에서만 사용하는 설정
logging:
  level:
    root: WARN
    com.example: ERROR

server:
  port: 80

 

► 느낀점

블로그에 오류해결 과정을 적어본것은 처음이다. 필요한것은 알았지만 항상 너무 오랜시간이 걸리거나 얼른 해결해야한다는 조급함에 이것저것 기록하지 않고 그냥 다 대입해보기 바빴다. 하지만 그러고나면 그저 7시간동안 끙끙대며 힘들어했던 기억말고는 잘 남지 않았다. 부트캠프 일정에 난 오늘도 쫒기고 있었는데 이걸 6시간 넘게 붙잡고 있었다. 생각보다 너무 허무하게 해결된 문제에 내가 들인 시간을 헛되이 보낼 수 없단 생각이 들었다. 추가로 1시간 반정도 걸려 이 글을 썼고, 쓰고보니 든든하다.