특정 단어를 모두 다른 단어로 바꾸고 싶으면 모든 곳을 수정해야하는 난관에 봉착할 수도 있다?!
수십개의 파일을 모두 고치려면 머리아프다
HTML에 하드코딩되어있기 때문인데 -> 좀 더 편하게 하는 방법이 없을까? // 메세지 기능을 사용하자!
별도로 만들 필요 없음 - 설정까지 다 되어있음 스프링 부트에서는
메세지 - 인터페이스
setBasenames - 읽을 파일
defaultEncoding - 인코딩 형식
스프링 메세지 소스 설정
위에서 아주 간략하게 설명한 스프링 메세지를 사용하려면
스프링 부트는 해당 기능이 이미 빈으로 등록되어 있어 application.properties
에 간단하게 다음 내용의 등록을 통해 사용할 수 있으나, 단순 스프링이라면 다음과 같이 스프링 빈을 등록해주어야 한다.
@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "error");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
basenames
: 설정 파일의 이름defaultEncoding
: 인코딩 정보를 지정한다.
messages.properties
파일
label.item=상품
label.item.id=상품 IDlabel.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
page.items=상품 목록
page.item=상품 상세
page.addItem=상품 등록
page.updateItem=상품 수정
button.save=저장
button.cancel=취소
messages_en.properties
파일
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=price
label.item.quantity=quantity
page.items=Item List
page.item=Item Detail
page.addItem=Item Add
page.updateItem=Item Update
button.save=Save
button.cancel=Cancel
다음과 같이 작성하면 Locale 정보에 따라 위의 properties 파일이 기본값으로 동작하나
Locale 정보가 en-us 등 으로 오는 경우, messages_en.properties에 기술된 내용으로 html이 렌더링된다
스프링 메세지 소스 사용
MessageSource
인터페이스는 다음과 같이 기술되어 있다
public interface MessageSource {
/**
* Try to resolve the message. Return default message if no message was found. * @param code the message code to look up, e.g. 'calculator.noRateSet'.
* MessageSource users are encouraged to base message names on qualified class * or package names, avoiding potential conflicts and ensuring maximum clarity. * @param args an array of arguments that will be filled in for params within
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message), * or {@code null} if none
* @param defaultMessage a default message to return if the lookup fails
* @param locale the locale in which to do the lookup
* @return the resolved message if the lookup was successful, otherwise
* the default message passed as a parameter (which may be {@code null})
* @see #getMessage(MessageSourceResolvable, Locale)
* @see java.text.MessageFormat
*/ @Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* Try to resolve the message. Treat as an error if the message can't be found. * @param code the message code to look up, e.g. 'calculator.noRateSet'.
* MessageSource users are encouraged to base message names on qualified class * or package names, avoiding potential conflicts and ensuring maximum clarity. * @param args an array of arguments that will be filled in for params within
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message), * or {@code null} if none
* @param locale the locale in which to do the lookup
* @return the resolved message (never {@code null})
* @throws NoSuchMessageException if no corresponding message was found
* @see #getMessage(MessageSourceResolvable, Locale)
* @see java.text.MessageFormat
*/ String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/**
* Try to resolve the message using all the attributes contained within the * {@code MessageSourceResolvable} argument that was passed in.
* <p>NOTE: We must throw a {@code NoSuchMessageException} on this method
* since at the time of calling this method we aren't able to determine if the * {@code defaultMessage} property of the resolvable is {@code null} or not.
* @param resolvable the value object storing attributes required to resolve a message
* (may include a default message) * @param locale the locale in which to do the lookup
* @return the resolved message (never {@code null} since even a
* {@code MessageSourceResolvable}-provided default message needs to be non-null)
* @throws NoSuchMessageException if no corresponding message was found
* (and no default message was provided by the {@code MessageSourceResolvable})
* @see MessageSourceResolvable#getCodes()
* @see MessageSourceResolvable#getArguments()
* @see MessageSourceResolvable#getDefaultMessage()
* @see java.text.MessageFormat
*/ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
해당 내용을 보면 코드를 포함하여 일부 파라미터를 메세지로 읽어오는 것을 볼 수 있는데
package hello.itemservice.message;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import java.util.Locale;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource messageSource;
@Test
void helloMessage(){
Locale.setDefault(Locale.KOREA);
String result = messageSource.getMessage("hello", null, null);
Assertions.assertThat(result).isEqualTo("안녕");
// 로케일 정보가 없으면 basename에서 설정한 기본 이름 메세지 파일을 조회
// basename으로 messages를 지정했으므로, messages.properties 파일에서 데이터 조회
}
// 메세지가 없는 경우 기본 메세지
@Test
void notFoundMessageCode(){
assertThatThrownBy(() -> messageSource.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
// 메세지가 없는 경우, NoSuchMessageException이 발생한다
@Test
void notFoundMessageCodeDefaultMessage(){
String result = messageSource.getMessage("no_code", null, "기본 메세지" ,null);
assertThat(result).isEqualTo("기본 메세지");
}
// 메세지가 없어도 기본 메세지를 사용하면 기본 메세지가 반환된다.
@Test
void argumentMessage(){
Locale.setDefault(Locale.KOREA);
String message = messageSource.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(message).isEqualTo("안녕 Spring");
}
// 매개변수 사용, {0} 부분을 전달해서 사용할 수 있음 (단, 배열로 넘겨야 함.)
}
웹 애플리케이션에 메세지 적용하기
위에서 등록한 messages.properties
와 messages_en.properties
를 기준으로 설명하겠다.
타임리프에서 #{...}
을 사용하면 스프링의 메세지를 편리하게 조회할 수 있다.
예를 들어서 방금 등록한 상품이라는 이름을 조회하려면 #{label.item}
식으로 기술한다.
- 렌더링 전
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
- 렌더링 후
<h2>상품 등록</h2>
웹 애플리케이션에 국제화 적용하기
Accept-Language
에 따라 국제화를 적용하면 언어를 다르게 표시할 수 있다
이 때 Accept-Language
의 경우 웹 브라우저 설정에 따라 결정된다.
MessageSource
는 기본적으로 Locale
정보를 알아야 언어 선택이 가능하다
이 헤더값을 사용하거나 스프링은 Locale
선택 방식을 변경할 수 있도록 LocaleResolver
라는 인터페이스를 제공한다.
기본은 Accept-Language
를 활용하는 AcceptHeaderLocaleResolver
를 사용한다.
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
Locale
선택 방식 변경을 위해서는 LocaleResolver
의 구현체를 변경해서 쿠키/세션 기반의 Locale
선택 기능을 사용할 수 있다.
'스프링 공부 (인프런 김영한 선생님) > 스프링 MVC 2편' 카테고리의 다른 글
[스프링 웹 MVC 2편] 006. Validation (2) (0) | 2023.08.15 |
---|---|
[스프링 웹 MVC 2편] 005. Validation (1) (0) | 2023.08.13 |
[스프링 웹 MVC 2편] 003. 스프링 통합과 폼 (0) | 2023.08.11 |
[스프링 웹 MVC 2편] 002. 타임리프 기본기능(2) (0) | 2023.08.11 |
[스프링 웹 MVC 2편] 001. 타임리프 기본기능 (0) | 2023.08.05 |
특정 단어를 모두 다른 단어로 바꾸고 싶으면 모든 곳을 수정해야하는 난관에 봉착할 수도 있다?!
수십개의 파일을 모두 고치려면 머리아프다
HTML에 하드코딩되어있기 때문인데 -> 좀 더 편하게 하는 방법이 없을까? // 메세지 기능을 사용하자!
별도로 만들 필요 없음 - 설정까지 다 되어있음 스프링 부트에서는
메세지 - 인터페이스
setBasenames - 읽을 파일
defaultEncoding - 인코딩 형식
스프링 메세지 소스 설정
위에서 아주 간략하게 설명한 스프링 메세지를 사용하려면
스프링 부트는 해당 기능이 이미 빈으로 등록되어 있어 application.properties
에 간단하게 다음 내용의 등록을 통해 사용할 수 있으나, 단순 스프링이라면 다음과 같이 스프링 빈을 등록해주어야 한다.
@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "error");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
basenames
: 설정 파일의 이름defaultEncoding
: 인코딩 정보를 지정한다.
messages.properties
파일
label.item=상품
label.item.id=상품 IDlabel.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
page.items=상품 목록
page.item=상품 상세
page.addItem=상품 등록
page.updateItem=상품 수정
button.save=저장
button.cancel=취소
messages_en.properties
파일
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=price
label.item.quantity=quantity
page.items=Item List
page.item=Item Detail
page.addItem=Item Add
page.updateItem=Item Update
button.save=Save
button.cancel=Cancel
다음과 같이 작성하면 Locale 정보에 따라 위의 properties 파일이 기본값으로 동작하나
Locale 정보가 en-us 등 으로 오는 경우, messages_en.properties에 기술된 내용으로 html이 렌더링된다
스프링 메세지 소스 사용
MessageSource
인터페이스는 다음과 같이 기술되어 있다
public interface MessageSource {
/**
* Try to resolve the message. Return default message if no message was found. * @param code the message code to look up, e.g. 'calculator.noRateSet'.
* MessageSource users are encouraged to base message names on qualified class * or package names, avoiding potential conflicts and ensuring maximum clarity. * @param args an array of arguments that will be filled in for params within
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message), * or {@code null} if none
* @param defaultMessage a default message to return if the lookup fails
* @param locale the locale in which to do the lookup
* @return the resolved message if the lookup was successful, otherwise
* the default message passed as a parameter (which may be {@code null})
* @see #getMessage(MessageSourceResolvable, Locale)
* @see java.text.MessageFormat
*/ @Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* Try to resolve the message. Treat as an error if the message can't be found. * @param code the message code to look up, e.g. 'calculator.noRateSet'.
* MessageSource users are encouraged to base message names on qualified class * or package names, avoiding potential conflicts and ensuring maximum clarity. * @param args an array of arguments that will be filled in for params within
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message), * or {@code null} if none
* @param locale the locale in which to do the lookup
* @return the resolved message (never {@code null})
* @throws NoSuchMessageException if no corresponding message was found
* @see #getMessage(MessageSourceResolvable, Locale)
* @see java.text.MessageFormat
*/ String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/**
* Try to resolve the message using all the attributes contained within the * {@code MessageSourceResolvable} argument that was passed in.
* <p>NOTE: We must throw a {@code NoSuchMessageException} on this method
* since at the time of calling this method we aren't able to determine if the * {@code defaultMessage} property of the resolvable is {@code null} or not.
* @param resolvable the value object storing attributes required to resolve a message
* (may include a default message) * @param locale the locale in which to do the lookup
* @return the resolved message (never {@code null} since even a
* {@code MessageSourceResolvable}-provided default message needs to be non-null)
* @throws NoSuchMessageException if no corresponding message was found
* (and no default message was provided by the {@code MessageSourceResolvable})
* @see MessageSourceResolvable#getCodes()
* @see MessageSourceResolvable#getArguments()
* @see MessageSourceResolvable#getDefaultMessage()
* @see java.text.MessageFormat
*/ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
해당 내용을 보면 코드를 포함하여 일부 파라미터를 메세지로 읽어오는 것을 볼 수 있는데
package hello.itemservice.message;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import java.util.Locale;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource messageSource;
@Test
void helloMessage(){
Locale.setDefault(Locale.KOREA);
String result = messageSource.getMessage("hello", null, null);
Assertions.assertThat(result).isEqualTo("안녕");
// 로케일 정보가 없으면 basename에서 설정한 기본 이름 메세지 파일을 조회
// basename으로 messages를 지정했으므로, messages.properties 파일에서 데이터 조회
}
// 메세지가 없는 경우 기본 메세지
@Test
void notFoundMessageCode(){
assertThatThrownBy(() -> messageSource.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
// 메세지가 없는 경우, NoSuchMessageException이 발생한다
@Test
void notFoundMessageCodeDefaultMessage(){
String result = messageSource.getMessage("no_code", null, "기본 메세지" ,null);
assertThat(result).isEqualTo("기본 메세지");
}
// 메세지가 없어도 기본 메세지를 사용하면 기본 메세지가 반환된다.
@Test
void argumentMessage(){
Locale.setDefault(Locale.KOREA);
String message = messageSource.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(message).isEqualTo("안녕 Spring");
}
// 매개변수 사용, {0} 부분을 전달해서 사용할 수 있음 (단, 배열로 넘겨야 함.)
}
웹 애플리케이션에 메세지 적용하기
위에서 등록한 messages.properties
와 messages_en.properties
를 기준으로 설명하겠다.
타임리프에서 #{...}
을 사용하면 스프링의 메세지를 편리하게 조회할 수 있다.
예를 들어서 방금 등록한 상품이라는 이름을 조회하려면 #{label.item}
식으로 기술한다.
- 렌더링 전
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
- 렌더링 후
<h2>상품 등록</h2>
웹 애플리케이션에 국제화 적용하기
Accept-Language
에 따라 국제화를 적용하면 언어를 다르게 표시할 수 있다
이 때 Accept-Language
의 경우 웹 브라우저 설정에 따라 결정된다.
MessageSource
는 기본적으로 Locale
정보를 알아야 언어 선택이 가능하다
이 헤더값을 사용하거나 스프링은 Locale
선택 방식을 변경할 수 있도록 LocaleResolver
라는 인터페이스를 제공한다.
기본은 Accept-Language
를 활용하는 AcceptHeaderLocaleResolver
를 사용한다.
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
Locale
선택 방식 변경을 위해서는 LocaleResolver
의 구현체를 변경해서 쿠키/세션 기반의 Locale
선택 기능을 사용할 수 있다.
'스프링 공부 (인프런 김영한 선생님) > 스프링 MVC 2편' 카테고리의 다른 글
[스프링 웹 MVC 2편] 006. Validation (2) (0) | 2023.08.15 |
---|---|
[스프링 웹 MVC 2편] 005. Validation (1) (0) | 2023.08.13 |
[스프링 웹 MVC 2편] 003. 스프링 통합과 폼 (0) | 2023.08.11 |
[스프링 웹 MVC 2편] 002. 타임리프 기본기능(2) (0) | 2023.08.11 |
[스프링 웹 MVC 2편] 001. 타임리프 기본기능 (0) | 2023.08.05 |