@Configuration은 스프링에서 클래스를 통해 값, 또는 @Bean을 이용한 함수를 설정하여 지정된 객체에 의존성 주입을 할 수 있는 어노테이션이다. 즉 객체를 미리 생성시켜 두는 건데, 이는 객체 사이의 의존도를 낮추고 스프링 컨테이너에서 객체의 생명주기와 의존관계를 관리하기에 상당히 유연한 코드를 작성할 수 있다는 장점이 있다.
예를 들어, 다음과 같이 SMTP를 통해 메일을 발송하는 코드가 있다고 가정해보자.
예시
@Service public class EmailService { public void sendMail(MailInfoDTO mailInfoDTO) throws MessagingException, IOException { MimeMessagePreparator preparator = mimeMessage -> { final MimeMessageHelper message = new MimeMessageHelper(mimeMessage,true,"UTF-8"); message.setTo(mailInfoDTO.getReceiver()); message.setFrom("example@gmail.com"); message.setSubject(mailInfoDTO.getTitle()); message.setText(mailInfoDTO.getContents())); }; JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost("smtp.gmail.com"); mailSender.setPort(465); mailSender.setUsername("example@gmail.com"); mailSender.setPassword("examplePassword"); Properties props = mailSender.getJavaMailProperties(); props.put("mail.smtp.auth", true); props.put("mail.smtp.starttls.enable", true); mailSender.send(preparator); } }
해당 코드는 JavaMailSenderImpl을 통해 정해진 계정으로 타인에게 메일을 전송하는데, sendMail을 실행할 때마다 mailSender 객체를 생성하여 리소스가 낭비되고, mailSender와 message.setFrom()을 설정하는 값이 하드코딩되어 가독성과 유지보수성이 낮다.
이를 @Configuration을 통해 미리 객체와 값을 설정하여 해결할 수 있다.
먼저 하드코딩된 값들을 리소스 파일을 통해 설정하는 방법을 알아보자.
Configuration으로 선언된 클래스 내에 값을 직접 설정하면, 그 또한 하드코딩이기에 yml 설정 파일을 생성하여 설정한다.
해당 파일을 Spring Boot 프로젝트 내 resources폴더에 생성하고, application.properties에서 resources폴더 내에 생성한 yml 설정 파일 내의 값들을 가져온다.
values.yml
spring: mail: host: smtp.gmail.com port: 587 username: example@gmail.com password: appPasswordFromGmail encoding: UTF-8 propString: auth: mail.smtp.auth enable: mail.smtp.enable smtp: auth: true starttls: enable: true
application.properties
spring.config.import=classpath:values.yml
이후, @Configuration과 @ConfigurationProperties를 통해 application.properties내에서 spring.mail 필드에 해당되는 값들을 불러온다. 불러온 값들은 @Getter를 통해 차후 호출할 수 있도록 한다. spring.mail 필드에서 파생되는 필드들에 대해서는 내부에 별도로 클래스를 생성하여 이를 매핑한다.
MailConfig
@Configuration @ConfigurationProperties(prefix = "spring.mail") @Data public class MailConfig { private String host; private Integer port; private String username; private String password; private String encoding; private PropString propString; //파생 필드 private Smtp smtp; //파생 필드 @Data public static class PropString{ private String auth; private String enable; } @Data public static class Smtp { private boolean auth; private Starttls starttls; //파생 필드 @Data public static class Starttls { private boolean enable; } } }
다음은 객체를 미리 설정하는 방법이다.
JavaMailSender 구현체를 반환하는 javaMailSender 매서드를 빈으로 등록하여 애플리케이션이 시작될 때 JavaMailSender 빈이 필요할 때마다 새로운 인스턴스를 생성하는 것이 아니라, 미리 생성된 인스턴스를 사용하도록 한다.
다만 MimeMessageHelper는 메일의 내용을 설정하는데 사용되는데, 메일 전송 후에도 내용이 메모리에 남아있을 수 있어, 이전 메일의 내용이 다음 메일에 영향을 주지 않도록 각각의 메일마다 새로운 MimeMessageHelper 인스턴스를 생성해야 하므로 설정하지 않는다.
MailSenderConfig
@Configuration public class MailSenderConfig { @Autowired private MailConfig mailConfig; @Bean public JavaMailSender javaMailSender() { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost(mailConfig.getHost()); mailSender.setPort(mailConfig.getPort()); mailSender.setUsername(mailConfig.getUsername()); mailSender.setPassword(mailConfig.getPassword()); Properties props = mailSender.getJavaMailProperties(); props.put(mailConfig.getPropString().getAuth(), mailConfig.getSmtp().isAuth()); props.put(mailConfig.getPropString().getEnable(), mailConfig.getSmtp().getStarttls().isEnable()); return mailSender; } }
EmailService
@Service @RequiredArgsConstructor public class EmailService { private final MailSenderConfig mailConfig private final JavaMailSender mailSender; public void sendMailWithCurrentInfo(CurrentNewsDTO currentNewsDTO) throws MessagingException, IOException { MimeMessagePreparator preparator = mimeMessage -> { final MimeMessageHelper message = new MimeMessageHelper(mimeMessage,true,mailConfig.getEncoding()); message.setTo(currentNewsDTO.getReceiver()); message.setFrom(mailConfig.getUsername()); message.setSubject(currentNewsDTO.getTitle()); message.setText(currentNewsDTO.getNewsUrl()); }; mailSender.send(preparator); } }