* 주의! 이 글은 스프링의 공식 문서와 코드를 보고 해석한 내용으로, 작성자의 부족한 영어 실력과 코드 해석 능력으로 인해 틀린 내용이 있을 수 있습니다 *
* 주의! 이 글은 MessageSource의 사용 방법을 다루지 않습니다. MessageSource를 빈으로 주입받기까지의 과정을 서술합니다.*
@Autowired
private MessageSource messageSource;
// ...
public void method() {
String message = messageSource.getMessage("helloWorld", new Object[]{}, Locale.KOREAN));
System.out.println(message); // messages.properties에 있는 helloWorld의 값이 뿅 튀어나온다
}
다국어 처리, Exception message 몰아넣기(필자가 가장 많이 쓰는 용도) 등등을 위해 MessageSource
를 사용해본 적이 있을 것이다.(없다면 써보길 바란다. 반복되는 문자열을 사용해야 하거나(물론 요건 상수 클래스 만들어서 박아 넣어도 됨) 다국어 처리를 해야한다면 필수다!)
혹시 안 써본 사람들을 위해 간략히 설명하자면 resource/messages.properties
(사실 파일명 바꿀 수 있음)에서 값을 읽어와서 스트링으로 뱉어주는 착한 친구다.
여느때와 다름 없이 무지성으로 사용하다 갑자기 궁금증이 생겼다.
이거 어떻게 읽어오는 거지?
궁금증을 해결하기에 가장 좋은 방법은 공식문서라고 생각하는 사람으로서 공식문서를 뒤져보지 않을 수가 없었다.
일단 Spring Framework의 공식문서를 살펴보자. (읽어보고 싶은 사람은 여기로 가서 보면 된다.)
아래는 공식문서에 적혀있는 ResorceBundleMessageSource
빈 등록 코드이다.
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
..스프링부트에선 사용하지 않는 xml을 이용한 빈 등록 방법이다.
그래도 대충 org.springframework.context.support.ResourceBundleMessageSource
클래스로 messageSource
(꼭 이 이름이어야 한다. 위 공식문서에 나와있음)라는 이름의 빈을 만들면서 생성자에 basenames
를 넘겨주는구나~ 정도는 이해할 수 있을 것이다.
(의미 없지만 String... basenames
받는 생성자는 ResourceBundleMessageSource
의 부모 클래스인 AbstractResourceBasedMessageSource
에서 확인할 수 있다.)
저 생성자로 넘어가는 basename들이 MessageSource가 값을 읽어올 파일명(기본은 messages)들이다.
그런데 우리는 @Bean
을 사용해서라도 messageSource
빈을 만든 적이 없다.
또, basename도 application.properties
에서 설정한 것 같았다.
그렇다면 이 빈은 어디서 갑자기 튀어 나왔고 basename은 어떻게 설정된걸까?
이 의문에 대한 답은 Spring Boot문서의 Auto-configuration Classes에서 찾을 수 있다.
위 문서에 들어가보면 스프링의 자동설정에 관련된 클래스 목록들이 쭉 나열돼있는데, 그 중엔 MessageSourceAutoConfiguration
이 존재한다.
이 클래스의 코드를 뜯어보자.
// ...
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
// ...
이제 MessageSourceAutoConfiguration
에서 messageSource
빈을 등록하는 것을 알아냈다.
등록하면서 basename과 인코딩 등등을 설정한다.
우리는 basename이 매개변수 properties
에 담겨있다고 예상할 수 있다.
그렇다면 위 함수의 매개변수 properties
는 어디서 가져오는 걸까?
저 클래스 파일을 열어본 사람들은 모두 같이 위 함수의 위에 작성된 코드를 보자.
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
얘도 빈이다.
@ConfigurationProperties
가 낯선 사람이 있을 수 있다.
이 어노테이션은 application.properties
에 있는 값중 접두사가 spring.messages
인 친구들을 가져온다.
즉, 우리가 메세지 파일의 이름을 변경하기 위해 application.properties
에 작성한 spring.messages.basename
같은 것을 가져온다는 뜻.
이제 messageSource
빈이 어떻게 생성되는지 알게 되었다.
다시 가장 처음 나왔던 코드를 보자.
@Autowired
private MessageSource messageSource;
// ...
public void method() {
String message = messageSource.getMessage("helloWorld", new Object[]{}, Locale.KOREAN));
System.out.println(message);
}
이렇게 등록된 빈을 @Autowired
를 사용해 주입 받고, 주입 받은 빈의 getMessage
메서드를 이용하여 메세지를 받아온다.
(필드 위에 @Autowired
붙이지 말자. 예시는 예시일 뿐. 생성자 주입을 강력히 권한다.)
드디어 겨우 MessageSource
하나 주입 받기 위한 과정이 종료됐다.
그냥 사용하는 별 것 아닌 놈이 더럽게 복잡한 과정을 거쳐서 주입된다.