[Spring Boot] MessageSource에 대해 알아보자

몽머·2022년 8월 19일
0
post-thumbnail
post-custom-banner

* 주의! 이 글은 스프링의 공식 문서와 코드를 보고 해석한 내용으로, 작성자의 부족한 영어 실력과 코드 해석 능력으로 인해 틀린 내용이 있을 수 있습니다 *
* 주의! 이 글은 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(사실 파일명 바꿀 수 있음)에서 값을 읽어와서 스트링으로 뱉어주는 착한 친구다.

여느때와 다름 없이 무지성으로 사용하다 갑자기 궁금증이 생겼다.

이거 어떻게 읽어오는 거지?


MessageSource가 빈으로 등록되고 사용되기까지..

궁금증을 해결하기에 가장 좋은 방법은 공식문서라고 생각하는 사람으로서 공식문서를 뒤져보지 않을 수가 없었다.

일단 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 하나 주입 받기 위한 과정이 종료됐다.
그냥 사용하는 별 것 아닌 놈이 더럽게 복잡한 과정을 거쳐서 주입된다.

profile
백엔드 개발자
post-custom-banner

0개의 댓글