728x90
메시지, 국제화
- 스프링은 메시지와 국제화 기능을 통합해서 제공한다.
🔍 메시지
- 메시지 기능이란, 화면에서 공통으로 사용되는 다양한 메시지를 한 곳에서 관리하는 기능이다.
- 메시지 기능을 사용하면 메시지를 변수화하여 key값으로 불러서 사용할 수 있다.
- 그렇게하면 HTML 문서의 메시지 내용을 수정할 때 유지보수가 간편해진다.
- 스프링은 메시지 기능을 제공한다.
💡 사용예시
📌 메시지 관리용 파일 (messages.properties)
item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량
- 사용할 메시지들을 파일 한 곳에 모아서 key, value 형식으로 다룬다.
📌 HTML (해당 메시지를 사용할 HTML)
<label for="itemName" th:text="#{item.itemName}"></label>
- HTML 파일에 사용할 메시지를 key값으로 value값을 호출한다.
🔍 국제화
- 국제화 기능이란, 메시지 내용을 서비스를 지원하는 나라 언어로 제공하는 것이다.
- 메시지에서 설명한 메시지 파일(messages.properties)을 각 나라별로 별도로 관리하면 서비스를 국제화할 수 있다.
- HTTP accept-language 헤더 값을 사용하거나 또는 사용자가 직접 언어를 선택할 수 있도록 선택권을 주고, 쿠키를 사용해서 각 국가별로 맞는 언어를 제공하면 된다.
💡 사용 예시
📌 messages_ko.properties
item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량
📌 messages_en.properties
item=Item
item.id=Item ID
item.itemName=Item Name
item.price=price
item.quantity=quantity
- 이렇게 영어를 사용하는 사람이면 messages_en.properties 파일을 사용하고, 한국어를 사용하는 사람이면 messages_ko.properties 파일을 사용하면 된다.
- 이처럼 메시지와 국제화 기능을 직접 구현할 수 있지만, 스프링은 기본적으로 메시지와 국제화 기능을 모두 제공한다.
🧷 정리
- 스프링은 properties 파일에 공통 메시지를 변수화하여 제공할 수 있다.
- 여러 언어에 맞게 메시지를 제공할 수 있으며 이를 국제화 기능이라고 한다.
- 스프링은 메시지와 국제화 기능을 통합으로 제공한다.
스프링 메시지 소스 설정
- 스프링은 기본적으로 메시지 관리 기능을 제공한다.
- 스프링 부트는 프레임워크 자체적으로 MessageSource를 자동으로 스프링 빈으로 등록해준다.
- 참고로 MessageSource는 인터페이스다. 따라서 구현체인 ResourceBundleMessageSource를 스프링 빈으로 등록하면 된다. (스프링 부트에서는 자동으로 해준다.)
🔍 스프링일 경우 MessageSource 빈 등록 방법
스프링 Bean 직접 등록
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
- 메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource를 스프링 빈으로 등록해야 한다.
- setBasenames
- 파라미터로 설정 파일의 이름을 넣으면 된다. 그러면 설정 파일로 지정된다.
- messages 로 지정하면 messages.properties 파일을 찾아 읽어 사용한다.
- 추가로 국제화 기능을 적용하려면 message_en.properties, message_ko.properties 와 같이 파일명 마지막에 언어 정보를 주면 된다.
- 만약 국제화 파일이 없으면 messages.properties (언어 정보가 없는 파일명)를 기본으로 사용한다.
- 파일 위치는 /resources/messages.properties 에 두면 된다.
- 한 번에 여러 설정 파일을 지정할 수 있다. 여기서는 messages, errors 둘을 지정 햇다.
- setDefaultEncoding
- 인코딩 정보를 지정한다.
- 보통 utf-8을 사용한다.
🔍 스프링부트일 경우 MessageSource 빈 등록 방법
메시지 소스 설정
📌 application.properties
spring.messages.basename=messages
- 스프링 부트와 관련된 별도의 설정을 하지 않으면, message라는 이름으로 기본 등록 된다. (기본 설정)
- 국제화 기능을 사용하려면 기본 이름 뒤에 사용할 국가명을 붙이면 자동으로 인식된다.
- 예)
- messages_en.properties
- messages_ko.properties
- messages.properties
- 적절한 국가 설정을 찾지 못하면 기본 파일인 messages.properties를 사용한다.
- 예)
- 설정 파일이 하나일 경우 설정파일 지정을 생략해도 된다.
한 번에 설정 파일을 여러개 지정하기
spring.messages.basename=messages, config.i18n.messages
- 여러 파일을 , (콤마)를 통해 지정할 수 있다.
- config.i18n.messages
- /resources/config/i18n/messages/ 경로를 의미한다.
🔍 메시지 파일 만들기 (스프링 부트 이용)
📌 application.properties
spring.messages.basename=messages
📌 /resources/messages.properties
hello=안녕
hello.name=안녕 {0}
- {0} 처럼 파라미터 지정 가능
📌 /resources/messages_en.properties
hello=hello
hello.name=hello {0}
- {0} 처럼 파라미터 지정 가능
참고로 인털레J를 사용한다면 여러 messages 설정이 묶여서 Resource Bundle 'messages'로 뭉쳐진다.
스프링 메시지 소스 사용
🔍 MessageSource 기능
- MessageSource를 스프링 빈으로 등록하면 메시지 기능을 사용할 수 있다.
📌 MessageSource 인터페이스
public interface MessageSource {
String getMessage(String code, @Nullable Object[] args, @Nullable String
defaultMessage, Locale locale);
String getMessage(String code, @Nullable Object[] args, Locale locale) throws
NoSuchMessageException;
}
- code : 메시지에서 불러올 값의 key
- args : 메시지에 배열이 있을 경우 배열을 전달할 수 있다. 그러면 메시지 설정 파일에서 해당 배열의 인덱스 값을 출력한다.
- 예) new Object[]{"Spring"}
- defaultMessage : 인수로 전달한 key에 해당하는 값이 없는 경우 반환할 기본 값
- locale : Locale 정보를 기반으로 국제화 파일을 선택한다.
- 예)
- Locale.KOREA
- Locale.ENGLISH
- 예)
🔍 MessageSource 기능 사용 (테스트 코드로 알아보기)
📌 MessageSourceTest.java
package hello.itemservice.message;
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.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("안녕");
}
@Test
void notFoundMessageCode() {
assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
@Test
void notFoundMessageCodeDefaultMessage() {
String result = ms.getMessage("no_code", null, "기본 메시지", null);
assertThat(result).isEqualTo("기본 메시지");
}
@Test
void argumentMessage() {
String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(result).isEqualTo("안녕 Spring");
}
@Test
void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
}
@Test
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
}
- @SpringBootTest 애노테이션을 통해 테스트 파일에 스프링 부트를 실행시킨다.
- 즉, MessageSource 빈을 자동으로 등록해준다.
- MessageSource 객체를 @Autowired 를 통해 자동 의존 주입을 해줬다.
📌 messages.properties
hello=안녕
hello.name=안녕 {0}
📌 messages_en.properties
hello=hello
hello.name=hello {0}
💡 getMessage() 기본 기능
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("안녕");
}
- ms.getMessage("hello", null, null)
- code : hello
- args : null
- locale : null
- locale 정보가 없으면 application,.properties파일의 basename에서 기본 메시지 설정 파일을 조회한다.
➰ 인텔리제이 한글 호환 인코딩 에러 해결
더보기
인텔리제이 한글 호환 인코딩 에러
- 위 사진처럼 "안녕" 값이 아닌 "??" 값이 나와야한다고 적혀있다.
- 한글 인코딩 문제로 발생한 에러다.
한글 인코딩 문제 해결
- 인텔리제이의 인코딩을 UTF-8로 설정하면 된다.
- File → Settings... → Editor → File Encodings → Global Encoding → UTF-8 선택
- File → Settings... → Editor → File Encodings → Project Encoding → UTF-8 선택
- File → Settings... → Editor → File Encodings → Properties Files(*.properties) → UTF-8 선택 → 체크박스 체크
💡 해당 메시지가 없을 경우 - 예외 확인
@Test
void notFoundMessageCode() {
assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
- 해당 메시지가 없을 경우에도 잘 작동하는지 테스트 코드를 작성해야한다.
- assertThatThrownBy() 를 통해 예외처리를 가독성 있게 테스트할 수 있다.
- 메시지 설정 파일에 일치하는 메시지가 없을 경우 NoSuchMessageException 예외가 발생한다.
💡 해당 메시지가 없을 경우 - 기본 메시지 설정
@Test
void notFoundMessageCodeDefaultMessage() {
String result = ms.getMessage("no_code", null, "기본 메시지", null);
assertThat(result).isEqualTo("기본 메시지");
}
- 메시지 설정 파일에 일치하는 메시지가 없어도 기본 메시지 기능을 사용하면 기본 메시지 값이 반환된다.
- ms.getMessage("no_code", null, "기본 메시지", null)
- code : "no_code"
- args : null
- defaultMessage : "기본 메시지"
- 일치하는 메시지가 없을 경우 해당 파라미터 값이 반환된다.
- locale : null
💡 추가 매개변수 사용
@Test
void argumentMessage() {
String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(result).isEqualTo("안녕 Spring");
}
- 메시지 설정 파일에 {0} 과 같은 포멧이 있을 경우 인덱스 번째에 해당하는 배열의 값이 적용된다.
- hello.name=안녕 {0}
- messages.properties 파일에서 {0}값에 new Object{"Spring"} 배열의 0번째 인덱스인 "Spring" 값이 들어간다.
- 출력 : 안녕 Spring
- ms.getMessage("hello.name", new Object[]{"Spring"}, null)
- code : "hello.name"
- args : new Object[]{"Spring"}
- 파라미터로 Object 배열 타입이 와야한다.
- 해당 배열을 통해 메시지에 인덱스에 맞는 배열 값을 전달할 수 있다.
- locale : null
💡 국제화 파일 선택 - 기본 / 한국어
@Test
void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
}
- locale 정보를 기반으로 국제화 파일을 선택한다.
- ms.getMessage("hello", null, Locale.KOREA)
- code : "hello"
- args : null
- locale : Locale.KOREA
- 한국어 선택
- messages_ko 메시지 설정 파일이 없으면 messages 기본 메시지 설정 파일이 사용된다.
💡 국제화 파일 선택 - 영어
@Test
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
웹 애플리케이션에 메시지 적용하기
📌 messages.properties
label.item=상품
label.item.id=상품 ID
label.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
page.items=상품 목록
page.item=상품 상세
page.addItem=상품 등록
page.updateItem=상품 수정
button.save=저장
button.cancel=취소
🔍 타임리프에서 메시지 적용하기
- 사용법 : #{...}
💡 랜더링 전
<div th:text="#{label.item}"> <div>
💡 렌더링 후
<div>상품<div>
🔍 동적인 메시지
- 메시지의 값이 상황에 따라 동적으로 변하는 경우 파라미터를 사용하면 된다.
- 메시지 : hell.name=안녕 {0}
<p th:text="#{hello.name(${item.itemName})}"></p>
웹 애플리케이션에 국제화 적용하기
- 메시지 기능을 통해 웹 애플리케이션을 유연하게 메시지를 추가했고, 국제화 기능을 적용해 다른 언어의 서비스를 제공할 수 있다.
📌 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
🔍 국제화 기능 적용하기
- 구글 설정 → '언어'에 들어가서 원하는 언어를 맨위로 올려서 우선권을 주면 된다.
- 그러면 가장 우선권이 높은 언어를 기반으로 국제화 기능이 알아서 적용된다.
- 위 사진처럼 영어를 가장 우선권을 높게 설정하면 HTTP 헤더의 Accept-Language가 영어로 바뀐다.
- 영어를 우선권을 주자 메시지가 자동으로 영어로 바뀌었다.
🔍 스프링의 국제화 메시지 선택
- 스프링의 메시지 기능을 사용하기 위해서는 Locale 정보가 필요하다.
- 스프링은 언어 선택시 기본으로 HTTP 헤더의 Accept-Language 값을 기반으로 메시지 설정 파일을 선택한다.
🔍 LocaleResolver
- LocaleResolver는 인터페이스이며, Locale 선택 방식을 변경할 수 있도록 제공한다. (스프링 기능)
- 스프링 부트는 기본으로 Accept-Language를 활용하는 AcceptHeaderLocaleResolver를 사용한다.
- 만약에 Locale 선택 방식을 변경하려면 LocaleResolver의 구현제를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다.
- 사용 예) 고객이 직접 Locale을 선택하도록 선택권을 준다.
👀 참고 자료
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
https://pjh3749.tistory.com/241
728x90
'[ Spring ] > SpringMVC 2편' 카테고리의 다른 글
[Spring] 로그인 처리1 - 쿠키, 세션 (0) | 2022.03.12 |
---|---|
[Spring] 검증2 - Bean Validation (0) | 2022.03.11 |
[Spring] 검증1 - Validation (0) | 2022.03.07 |
[Spring] 타임리프 - 스프링 통합과 폼 (0) | 2022.03.05 |
[Spring] Thymeleaf 타임리프 - 기본 기능 (0) | 2022.03.02 |