๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring

[Spring] Spring ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”์‹œ์ง€ ๋‹ค๊ตญ์–ด ๋ฐ ์žฌ๋กœ๋”ฉ ์ฒ˜๋ฆฌ

by Leica 2020. 3. 4.
๋ฐ˜์‘ํ˜•

[Spring] Spring ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”์‹œ์ง€ ๋‹ค๊ตญ์–ด ๋ฐ ์žฌ๋กœ๋”ฉ ์ฒ˜๋ฆฌ

Spring IoC ์ปจํ…Œ์ด๋„ˆ = ApplicationContext๊ฐ€ ๊ฐ–๊ณ ์žˆ๋Š” ๊ธฐ๋Šฅ ์ค‘ MessageSource๋Š” ๊ตญ์ œํ™”(i18n)์™€ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋Š”๋ฐ ์ด๋ฅผ ์ด์šฉํ•ด์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ถœ๋ ฅํ•˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ค๊ตญํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

1. ๋ฉ”์‹œ์ง€ ํŒŒ์ผ ์ž‘์„ฑ

resources ๋””๋ ‰ํ† ๋ฆฌ ์•„๋ž˜์— messages.properties, messages_ko_KR.properties ํŒŒ์ผ์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค.

์—ฌ๊ธฐ์— ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ถœ๋ ฅํ•  ๋ฉ”์‹œ์ง€๋ฅผ messages.properties์—๋Š” ์˜์–ด๋กœ, messages_ko_KR.proeprties์—๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•  ๊ฒƒ์ด๋‹ค.

ํŒŒ์ผ๋ช…์— ์˜คํƒ€๊ฐ€ ๋‚˜์ง€ ์•Š๋„๋ก ์กฐ์‹ฌํ•˜์ž.

ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‚˜๋ฉด ์ž๋™์œผ๋กœ 'messages'๋ผ๋Š” ์ด๋ฆ„์˜ resource bundle๋กœ ๋ฌถ์ธ๋‹ค.

 

messages.properties์™€ messages_en_US.properties์—๋Š” greeting=Hello, {0}๋ฅผ, messages_ko_KR.properties์—๋Š” greeting=์•ˆ๋…•, {0}์„ ์ž‘์„ฑํ•œ๋‹ค.

{0}์€ ๋„˜์–ด์˜จ argument๊ฐ€ ๋ฐ”์ธ๋”ฉ๋  ๋ถ€๋ถ„์ด๋‹ค.

 

2. ๋ฉ”์‹œ์ง€ ๋นˆ ๋“ฑ๋ก

1) Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ

Spring boot๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฉ”์‹œ์ง€๋ฅผ ๋นˆ์œผ๋กœ ์ง์ ‘ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
@Bean
public MessageSource messageSource() {
    var messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    messageSource.setDefaultEncoding("UTF-8");
 
    return messageSource;
}
cs

var๋Š” Java 10 ์ด์ƒ๋ถ€ํ„ฐ ์ง€์›๋˜๋ฏ€๋กœ 10 ๋ฏธ๋งŒ์ด๋ผ๋ฉด MessageSource ์ธ์Šคํ„ด์Šค๋ฅผ ResourceBundleMessageSource ํƒ€์ž…์œผ๋กœ ์ƒ์„ฑํ•ด์ค€๋‹ค.

setBasename() ๋ฉ”์†Œ๋“œ๋กœ ๋ฉ”์‹œ์ง€ ๋ฆฌ์†Œ์Šค ๋ฒˆ๋“ค์„ ์ง€์ •ํ•˜๊ณ  ํ•œ๊ธ€์ด ๊นจ์ง€์ง€ ์•Š๋„๋ก setDefaultEncoding() ๋ฉ”์†Œ๋“œ๋กœ ๋ฌธ์ž ์ง‘ํ•ฉ์„ ์„ค์ •ํ•œ๋‹ค.

 

2) Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

Spring boot ๊ธฐ๋ฐ˜์˜ ํ”„๋กœ์ ํŠธ์ผ ๊ฒฝ์šฐ ResourceBundleMessageSource๊ฐ€ ์ž๋™์œผ๋กœ ๋นˆ์œผ๋กœ ๋“ฑ๋ก์ด ๋˜๊ณ  ์ด ๋นˆ์ด messages ๋ฆฌ์†Œ์Šค ๋ฒˆ๋“ค์„ ๋ฉ”์‹œ์ง€ ์†Œ์Šค๋กœ ์ฝ์–ด์˜จ๋‹ค.

๋”ฐ๋ผ์„œ ๋นˆ์„ ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

3. ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
 
import java.util.Locale;
 
@Component
public class AppRunner implements ApplicationRunner {
 
    @Autowired
    MessageSource messageSource;
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
       Locale.setDefault(Locale.ROOT);
        System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.getDefault()));
        System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.US));
        System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.KOREA));
    }
}
cs

MessageSource ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…๋ฐ›๊ณ  getMessage()๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

getMessage()์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ฉ”์‹œ์ง€ ํ”„๋กœํผํ‹ฐ์˜ key, ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ ์‹œ ์ „๋‹ฌํ•  argument, ์„ธ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” Locale์„ ์„ค์ •ํ•œ๋‹ค.

 

์‹คํ–‰ ๊ฒฐ๊ณผ

์ฒซ ๋ฒˆ์งธ ์ถœ๋ ฅ๋ฌธ์€ messages.properties์˜ ๋ฉ”์‹œ์ง€๋ฅผ,

๋‘ ๋ฒˆ์งธ ์ถœ๋ ฅ๋ฌธ์€ messages_en_US.properties์˜ ๋ฉ”์‹œ์ง€๋ฅผ,

์„ธ ๋ฒˆ์งธ ์ถœ๋ ฅ๋ฌธ์€ messages_ko_KR.properties์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

 

4. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘์— ๋ฉ”์‹œ์ง€ ๋ฆฌ์†Œ์Šค Reload ํ•˜๊ธฐ

MessageSource์˜ ๊ตฌํ˜„์ฒด๋ฅผ ResourceBundleMessageSource๊ฐ€ ์•„๋‹Œ ReloadableResourceBundleMessageSource๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘์— ๋ฉ”์‹œ์ง€ ๋ฆฌ์†Œ์Šค ๋ณ€๊ฒฝ์„ reloadํ•˜๊ฒŒ ํ•˜์—ฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

MessageSource ๋นˆ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜ํ•œ๋‹ค.

 

1
2
3
4
5
6
7
8
9
@Bean
public MessageSource messageSource() {
    var messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("classpath:/messages");
    messageSource.setDefaultEncoding("UTF-8");
    messageSource.setCacheSeconds(3);
 
    return messageSource;
}
cs

ResourceBundleMessageSource๋กœ ์ƒ์„ฑํ• ๋•Œ์™€ ๋‹ฌ๋ฆฌ ReloadableResourceBundleMessageSource๋ฅผ ์‚ฌ์šฉํ• ๋•Œ๋Š” setBasename()์— "classpath:/messages" ํ˜•์‹์œผ๋กœ basename์„ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.

setCacheSeconds()๋กœ ๋ฉ”์‹œ์ง€ ์†Œ์Šค๋ฅผ ์บ์‹œํ•˜๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„(์ดˆ)๋ฅผ ์„ค์ •ํ•œ๋‹ค.

์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด 3์ดˆ๊นŒ์ง€๋Š” ์บ์‹ฑํ•˜๊ณ  3์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ค์‹œ ์ฝ๋Š”๋‹ค.

 

1
2
3
4
5
6
7
while (true) {
    System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.getDefault()));
    System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.US));
    System.out.println(messageSource.getMessage("greeting"new String[]{"Leica"}, Locale.KOREA));
 
    Thread.sleep(1000l);
}
cs

๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ ์ฝ”๋“œ๋ฅผ while๋กœ ๊ฐ์‹ธ๊ณ  Thread.sleep()์„ ํ˜ธ์ถœํ•˜์—ฌ 1์ดˆ์— ํ•œ๋ฒˆ์”ฉ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์–ด ์ถœ๋ ฅํ•˜๋„๋ก ๋ณ€๊ฒฝํ•œ๋‹ค.

 

ReloadableResourceBundleMessageSource๋กœ MessageSource ๋นˆ์„ ์ƒ์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด์ œ ์‹คํ–‰ ์ค‘์— ๋ฉ”์‹œ์ง€ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๊ณ  while๋ฌธ์ด ๋Œ๋ฉด์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋™์•ˆ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ์„ ๋ฐ”๊พธ๊ณ  buildํ•ด๋ณด์ž.

๋ฐ”๋€ ๋ฉ”์‹œ์ง€๊ฐ€ ์ ์šฉ๋  ๊ฒƒ์ด๋‹ค.

 

์‹คํ–‰ ๊ฒฐ๊ณผ

 

References

์ธํ”„๋Ÿฐ - ๋ฐฑ๊ธฐ์„ ๋‹˜์˜ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ํ•ต์‹ฌ ๊ธฐ์ˆ 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€