스프링 공부 (인프런 김영한 선생님)/스프링 MVC 2편

[스프링 웹 MVC 2편] 004. 국제화 메세지

2023. 8. 11. 17:59

특정 단어를 모두 다른 단어로 바꾸고 싶으면 모든 곳을 수정해야하는 난관에 봉착할 수도 있다?!

수십개의 파일을 모두 고치려면 머리아프다

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
'스프링 공부 (인프런 김영한 선생님)/스프링 MVC 2편' 카테고리의 다른 글
  • [스프링 웹 MVC 2편] 006. Validation (2)
  • [스프링 웹 MVC 2편] 005. Validation (1)
  • [스프링 웹 MVC 2편] 003. 스프링 통합과 폼
  • [스프링 웹 MVC 2편] 002. 타임리프 기본기능(2)
ProgYun.
ProgYun.
인내, 일관성, 그리고 꾸준함을 담습니다.
ProgYun.
Perseverance, Consistency, Continuity
ProgYun.
전체
오늘
어제
  • 분류 전체보기
    • 칼럼
    • 일상생활
      • 월별 회고
      • 인생 이야기 (대학생활)
      • 취준
      • 운동인증
      • 제품 사용 후기와 추천
    • 스프링 공부 (인프런 김영한 선생님)
      • 스프링 핵심원리
      • 스프링 MVC 1편
      • 스프링 MVC 2편
    • 면접 준비
    • 전공
      • OOP 정리
      • Design Pattern
    • 스터디
    • English
      • Electronics(Laptop)
      • 1일 1단어

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • NVME
  • 오미크론
  • ssd
  • 포맷
  • 대학생
  • 피로그래밍
  • p31
  • 자가격리
  • 윈도우10
  • 일상
  • 코로나
  • 하이닉스
  • 윈도우재설치
  • 컴공
  • 해외직구
  • 자존감
  • 코로나19
  • mason
  • 편입생
  • 확진자

최근 댓글

최근 글

hELLO · Designed By 정상우.
ProgYun.
[스프링 웹 MVC 2편] 004. 국제화 메세지
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.