본문 바로가기

개인 지식/Spring

스프링 부트 + 타임리프 다국어 메일 보내기(다국어 부분)🔥

안녕하세요. 오늘은 스프링 부트와 타임리프를 활용해서 다국어 메일을 보내려고 합니다.

 

제 코드가 더 나은 방향이 될 수 있게 댓글 남겨주시면 감사하겠습니다.

 

먼저 요구사항 먼저 정리하겠습니다.

 

[요구사항]

한 서버에 여러 개의 프로젝트가 생성되어 있습니다.

여러 개의 프로젝트가 회원에게 메일을 전송합니다. 그래서 메일을 전송하는 프로젝트를 따로 생성하여

관리하게 되었습니다.

 

[기존] 

 

[변경 후]

메일 전송을 담당하는 Project D는 앞으로 메일 서버라고 하겠습니다.

메일 서버는 http 요청을 받아서 html메일을 전송합니다.

 

[새로운 요구사항]

국제화 기능을 추가해야합니다. 기본에는 target이 한국뿐이었기 때문에 html 파일에 static하게 한국어가 들어가

있었습니다. 이를 국제화를 해야합니다.

 

[고민하는 과정]

1. 스프링에서 제공하는 기능을 사용해보자.

2. 스프링과 타임리프를 사용하여 다국어 처리를 해보자. (기존에는 그냥 html)

3. 들어오는 요청마다 어떤 언어로 보낼지 확인해서 보내보자

 

 

먼저 dependency를 추가해줍니다.

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

 

스프링부트에서는 다국어 처리를 위해 LocalResolver라는 기능을 제공합니다.

LocaleResolver는 요청에서 Locale 정보를 가져오는 역할을 합니다.

 

LocalResolver 종류

1. AcceptHeaderLocaleResolver : HTTP 요청 헤더인 "Accept-Language"를 사용하여 Locale 정보를 가져옵니다.

2. CookieLocaleResolver : 쿠키를 사용하여 Locale 정보를 가져옵니다.

3. SessionLocaleResolver : 세션을 사용하여 Locale 정보를 가져옵니다.

4. FixedLocaleResolver: 설정되고, 고정된 Locale 정보를 무조건 사용합니다. 

 

기본적으로는 AcceptHeaderLocaleResolver가 사용됩니다.

 

저는 각각의 메일전송 요청마다 다르게 언어를 선택해야하기 때문에 AcceptHeaderLocaleResolver를 사용하기로 결정하였습니다.

 

ThymeleafConfig

@Configuration
public class TymeleafConfig {

	@Bean
	public TemplateEngine htmlTemplateEngine() {
            TemplateEngine templateEngine = new SpringTemplateEngine();
            templateEngine.addTemplateResolver(springResourceTmeplateResolver());
            return templateEngine;
	}

	@Bean
	public SpringResourceTemplateResolver springResourceTmeplateResolver() {
            SpringResourceTemplateResolver springResourceTemplateResolver = new SpringResourceTemplateResolver();
            springResourceTemplateResolver.setOrder(1);
            springResourceTemplateResolver.setPrefix("classpath:templates/");
            springResourceTemplateResolver.setSuffix(".html");
            springResourceTemplateResolver.setTemplateMode(TemplateMode.HTML);
            springResourceTemplateResolver.setCharacterEncoding("UTF-8");
            springResourceTemplateResolver.setCacheable(false);

            return springResourceTemplateResolver;
	}
}

템플릿 엔진을 설정해주는 클래스입니다.

 

htmlTemplateEngine() 메서드는 SpringTemplateEngine 객체를 생성하고 SpringResourveTemplateResolver를 추가하여 이를 템플릿 엔진으로 사용합니다.

 

SpringResourceTemplateResolver() 메서드는 SpringResourveTemplateResolver 객체를 생성하여 템플릿 파일을 읽어올 위치를 설정합니다.

prefix : "classpath:templates/" 템플릿 파일을 읽어올 경로를 설정합니다.
suffix : ".html" 파일 확장자를 설정합니다.
templateMode : TemplateMode.HTML 템플릿 모드를 HTML로 설정합니다.
characterEncoding : "UTF-8" 템플릿 파일의 인코딩을 UTF-8로 설정합니다.
cacheable(false) : 캐시를 사용하지 않도록 설정합니다.

 

WebMvcConfiguration

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
	
	@Bean
	public LocaleResolver localeResolver() {
		// en-US, fr-CA, de-DE, es-ES, ko-KR, zh-CN 등이 있다.
		AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
		localeResolver.setDefaultLocale(Locale.KOREAN);
		return localeResolver;
	}
	
	@Bean
	public ReloadableResourceBundleMessageSource messageSource() {
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		messageSource.setBasename("classpath:/messages/message");
                messageSource.setCacheSeconds(180);
                Locale.setDefault(Locale.KOREAN);
                return messageSource;
	}
}

localResolver() 메서드는 기본적으로 사용할 언어를 설정합니다. 저는 기본으로 한국어를 설정하였습니다.

 

messageSource() 메서드는 다국어 처리가 된 message.properties 파일들을 읽어오는 설정을 합니다.

 

제 다국어 properties 파일들의 경로입니다.

/src/main/resources/messages/message_en.properties
/src/main/resources/messages/message_ko.properties

 

MailController

	
@RequestMapping(method = RequestMethod.POST, value = "/mail/send")
public CommonResponse sendMail(@Valid @RequestBody MailInfoDto mailInfo, Locale locale) throws Exception {
    mailService.sendMail(mailInfo, locale);
    return responseService.getSuccessResult();
}

해당 코드는 생략된 부분이 꽤 많은걸 감안해주세요 ㅎ

여기서 중요한 부분은 컨트롤러의 파라미터로 Locale locale을 받을 수 있다는 것입니다.

 http 요청 시에 헤더에 Accept-Language를 설정해주면 lcoale에는 해당 언어 정보가 담기게 됩니다.

 

postman Accept-Language 설정 예시

 

이제 해당 locale에 담긴 정보를 활용하여 properties 파일을 가져오겠습니다.

import org.thymeleaf.context.Context;

// ~ 생략

@Autowired
private TemplateEngine htmlTemplateEngine;
    
@Override
public CommonResponse sendMail(MailInfoDto mailInfo, Locale locale) throws Exception {
        
        // 이전 작업 생략
        
        String mailName = "helloMail"; // 예시 : html 파일 이름 입니다.
        
        Map<String, Object> propMap = getProperties(locale);
        Context context = getContext(propMap);
        
        String body = htmlTemplateEngine.process(mailName, context);
	
    	// 메일 발송부분 생략
}

private Map<String, Object> getProperties(Locale locale) throws Exception{

    Map<String, Object> result = new HashMap<>();

    Properties properties = PropertiesLoaderUtils.loadProperties
                    (new EncodedResource(new ClassPathResource("messages/message_"+locale.getLanguage()+".properties"), "UTF-8"));

    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
        result.put((String) entry.getKey(), (String) entry.getValue());
    }
    return result;
}


private Context getContext(Map<String, Object> propMap) {
    Context context = new Context();
    context.setVariables(propMap);
    return context;
}

 

getProperties 메서드는 해당 lcoale 에 맞는 properties의 모든 값을 Map<String, Object>로 리턴합니다. 그냥 가져오면 한글이 깨지는 문제 때문에 new EncodedResource -> "UTF-8"로 인코딩 해주었습니다.

 

그리고 그 map을 타임리프의 Context에 담았습니다.

 

htmlTemplateEngine.process 메서드에서는 mailName과 context를 인자로 html의 다국어화를 하여 String으로 return 받습니다.

 

html은 타임리프에서 지원하는 것처럼 th:text를 사용하여 설정해주시면 됩니다.

 

다국어화한 html을 메일로 전송하면 완료입니다.

 

메일을 전송하는 방법은 다른 참조 블로그가 많기 때문에 저는 다루지 않도록 하겠습니다.

 

좀 더 좋은 방법이 있을 것이라고 생각합니다. 있다면 저에게 조언해주시면 감사하겠습니다😉