์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„ ํ…œํ”Œ๋ฆฟ ์—”์ง„์œผ๋กœ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์„ ํƒํ•˜์—ฌ ์‚ฌ์šฉํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๋ถ€๋ถ„ ํƒ€์ž„๋ฆฌํ”„์— ๋Œ€ํ•ด์„œ ์„ค๋ช…ํ•˜๋ฏ€๋กœ ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ ์—”์ง„์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•œ ๊ธ€์€ ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์ง€ ์•Š๋‹ค. ๋‚˜๋Š” ํƒ€์ž„๋ฆฌํ”„๋ผ๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„ ๋ณด๋‹ค๋Š” ํ”„๋ฆฌ๋งˆ์ปค์˜ ๋ฌธ๋ฒ•์ด ๋” ๊ฐ„๋‹จํ•˜๊ณ  ๋Š๋ผ๊ธฐ์— ๋” ์„ ํ˜ธํ•˜๋Š” ํŽธ์ด๋‹ค. ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ…œํ”Œ๋ฆฟ ์—”์ง„๊ณผ ๋น„๊ตํ•ด์„œ๋„ ์ค€์ˆ˜ํ•œ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-freemarker'
}

FreeMarkerAutoConfiguration

์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ์ž๋™ ๊ตฌ์„ฑ์€ FreeMarkerAutoConfiguration๋กœ ์‹œ์ž‘๋˜๋ฉฐ FreeMarkerServletWebConfiguration์—์„œ ๋ทฐ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋“ฑ๋ก๋˜๋ฉฐ FreeMarkerNonWebConfiguration์œผ๋กœ FreeMarkerConfigurationFactoryBean๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์ด๋ฉ”์ผ ๋‚ด์šฉ ์ฒ˜๋ฆฌ์™€ ๊ฐ™์€ ์›น ์š”์ฒญ๊ณผ ๊ด€๋ จ๋˜์ง€ ์•Š์€ ๊ณณ์—์„œ๋„ ํ…œํ”Œ๋ฆฟ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ง€์›ํ•œ๋‹ค. ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” SpringTemplateLoader๋กœ ํด๋ž˜์ŠคํŒจ์Šค์— ์กด์žฌํ•˜๋Š” ํ…œํ”Œ๋ฆฟ์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋ฉฐ ํ”„๋ฆฌ๋งˆ์ปค์—์„œ๋Š” StringTemplateLoader๋ฅผ ํ†ตํ•ด ๋ฌธ์ž์—ด๋กœ ์ •์˜๋œ ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ์ œ๊ณตํ•œ๋‹ค.

Internationalization

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•ด์„œ ๋‹ค๊ตญ์–ด ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก spring.ftl๋ผ๋Š” ๋งคํฌ๋กœ๊ฐ€ ํฌํ•จ๋œ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์•„๋ž˜์™€ ๊ฐ™์ด ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ์—์„œ ์Šคํ”„๋ง ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์— ์ง์ ‘ ๋ช…์‹œํ•˜๊ฑฐ๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœํผํ‹ฐ๋กœ auto_import ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด์„œ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

index.ftlh
<#import "/spring.ftl" as spring/> <@spring.message "messageKey"/>
application.yml
spring: freemarker: settings: auto_import: spring.ftl as spring

๋‹ค๋งŒ, ์œ„ spring.ftl ํŒŒ์ผ์— ์ •์˜๋œ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋Š” ์›น ์š”์ฒญ์— ์˜ํ•œ ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฉ”์ผ ๋‚ด์šฉ ์ฒ˜๋ฆฌ์™€ ๊ฐ™์ด ์›น ์š”์ฒญ์ด ์•„๋‹Œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์—์„œ ์‚ฌ์šฉํ•  ์ˆœ ์—†๋‹ค.

๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ์‹œ ๋‹ค๊ตญ์–ด ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ

์‚ฌ์šฉ์ž์˜ ์›น ์š”์ฒญ์— ์˜ํ•œ ํ…œํ”Œ๋ฆฟ ์ฒ˜๋ฆฌ๊ฐ€ ์•„๋‹Œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์—์„œ ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์— ๋‹ค๊ตญ์–ด ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ ์—”์ง„์—์„œ ๋ฆฌ์†Œ์Šค ๋ฒˆ๋“ค์„ ๋ชจ๋ธ๋กœ ๋ฉ”์‹œ์ง€ ๋ณด๊ฐ„์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ResourceBundleModel๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ์€ ์Šคํ”„๋ง ๋ถ€ํŠธ ์ž๋™ ๊ตฌ์„ฑ์— ์˜ํ•ด ๋“ฑ๋ก๋˜๋Š” FreeMarkerConfigurationFactoryBean๊ณผ ResourceBundleModel์„ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ˆ์‹œ์ด๋‹ค.

Locale locale = Locale.ROOT;

Configuration configuration = configurationFactoryBean.createConfiguration();
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template", "${bundle(\"application.name\")}");
configuration.setTemplateLoader(stringTemplateLoader);
Template template = configuration.getTemplate("template", locale);

ResourceBundle resourceBundle = ResourceBundle.getBundle("messages", locale);
Map<String, Object> model = new HashMap<>();
model.put("bundle", new ResourceBundleModel(resourceBundle, new BeansWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build()));
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);

ํ…œํ”Œ๋ฆฟ ์ฒด์ด๋‹

ํ”„๋ฆฌ๋งˆ์ปค ํ…œํ”Œ๋ฆฟ ์—”์ง„์—์„œ ์ง€์›ํ•˜๋Š” MultiTemplateLoader๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ์‹์˜ ํ…œํ”Œ๋ฆฟ ๋กœ๋”ฉ์„ ํ†ตํ•ด ํ…œํ”Œ๋ฆฟ ๋กœ๋”์— ์˜ํ•ด ๋กœ๋“œ๋˜๋Š” ํ…œํ”Œ๋ฆฟ์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, MultiTemplateLoader์— ์ „๋‹ฌ๋˜๋Š” ํ…œํ”Œ๋ฆฟ ๋กœ๋”์˜ ์ˆœ์„œ์— ๋”ฐ๋ผ SpringTemplateLoader๋กœ ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ๋ ˆ์ด์•„์›ƒ์„ ๋งŒ๋“ค๊ณ  ์‹ค์ œ ํ…œํ”Œ๋ฆฟ ๋‚ด์šฉ์€ StringTemplateLoader๋กœ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("email.ftlh", "${bundle(\"application.name\")}");
stringTemplateLoader.putTemplate("template", "<#include \"email.ftlh\" >");

Configuration configuration = configurationFactoryBean.createConfiguration();
SpringTemplateLoader springTemplateLoader = new SpringTemplateLoader(resourceLoader, "classpath:/templates/");
MultiTemplateLoader multiTemplateLoader = new MultiTemplateLoader(new TemplateLoader[]{stringTemplateLoader, springTemplateLoader});
configuration.setTemplateLoader(multiTemplateLoader);
Template template = configuration.getTemplate("template", locale);

MultiTemplateLoader์˜ ์ƒ์„ฑ์ž์— ์ „๋‹ฌ๋˜๋Š” ํ…œํ”Œ๋ฆฟ ๋กœ๋”์˜ ์ˆœ์„œ๋Œ€๋กœ ํ…œํ”Œ๋ฆฟ์„ ์ฐพ๋„๋ก ์œ„์ž„ํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.