5 분 소요

1. < spring:message> 태그 사용하는 이유

사용자 화면에 보일 문자열은 JSP에 직접 코딩한다. 예를 들어 로그인 폼을 보여줄 떄 ‘아이디’, ‘비밀번호’ 등의 문자열을 다음과 같이 뷰 코드에 직접 삽입한다.

1
2
<label>이메일</label>
<input type="text" name="email>

1.1 수정시 불편함

‘이메일’과 같은 문자열은 로그인 폼, 회원가입 폼, 회원 정보 수정 폼에서 반복해서 사용된다. 이렇게 문자열을 직접 하드 코딩하면 동일 문자열을 변경할 때 문제가 있다. 예를 들어 폼에서 사용할 ‘이메일’을 ‘이메일 주소’로 변경하기로 했다면 각 폼을 출력하는 JSP를 찾아서 모두 변경해야 한다.

1.2 다국어 지원

문자열이 뷰 코드에 하드 코딩 되어 있을 때의 또 다른 문제점은 다국어 지원에 있다. 전 세계를 대상으로 서비스를 제공해야 하는 경우 사용자의 언어 설정에 따라 ‘이메일’, ‘E-mail’과 같이 각 언어에 맞게 문자열을 표시해야 한다. 그런데 뷰 코드에 ‘이메일’이라고 문자열이 하드 코딩되어 있으면 언어별로 뷰 코드를 따로 만드는 상황이 발생한다.

두 가지 문제를 해결하는 방법은 뷰 코드에서 사용할 문자열을 언어별로 파일에 보관하고 뷰 코드는 언어에 따라 알맞은 파일에서 문자열을 읽어와 출력하는 것이다. 스프링은 자체적으로 이 기능을 제공하고 있다.

2. < spring:message> 태그로 메시지 출력하기

문자열을 별도 파일에 작성하고 JSP 코드에서 이를 사용하려면 다음 작업을 하면 된다.

  • 문자열을 담은 메시지 파일을 작성한다.
  • 메시지 파일에서 값을 읽어오는 MessageSource 빈을 설정한다.
  • JSP 코드에서 < spring:message> 태그를 사용해서 메시지를 출력한다.

2.1 메시지 파일 작성

매시지 파일은 자바의 프로퍼티 파일 형식으로 작성한다. 메시지 파일을 보관하기 위해 src/main/resources에 message 폴더를 생성하고 이 폴더에 label.properties 파일을 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
member.register=회원가입

term=약관
term.agree=약관동의
next.btn=다음단계

member.info=회원정보
email=이메일
name=이름
password=비밀번호
password.confirm=비밀번호 확인
register.btn=가입 완료

registrer.done=<strong>{0}님</strong>, 회원 가입을 완료했습니다.

go.main=메인으로 이동

2.2 MessageSource 빈 설정

스프링 설정 중 한 곳에 추가하면 된다. 난 MvcConfig 설정 클래스에 추가하겠다. 다음과 같이 코드를 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer{

    ...

    @Bean
    public MessageSource messageSource(){
        ResourceBundleMessageSource ms =
                new ResourceBundleMessageSource();
        ms.setBasenames("message.label");
        ms.setDefaultEncoding("UTF-8");
        return ms;
    }
}

basenames 프로퍼티 값으로 “message.label”을 주었다. 이는 message 패키지에 속한 label 프로퍼티 파일로부터 메시지를 읽어온다고 설정한 것이다. src/main/resources 폴더도 클래스 패스에 포함되고 message 폴더는 message 패키지에 대응한다. 따라서 이 설정은 앞서 작성한 label.properties 파일로부터 메시지를 읽어온다. setBasenames() 메서드는 가변 인자이므로 사용할 메시지 프로퍼티 목록을 전달할 수 있다.

앞서 작성한 label.properties 파일은 UTF-8 인코딩을 사용하므로 defualtEncoding 속성의 값으로 “UTF-8”을 사용했다.

위 코드에서 주의할 점은 빈의 아이디를 “messageSource”로 지정해야 한다는 것이다. 다른 이름을 사용할 경우 정상적으로 동작하지 않는다.

MessageSource를 사용해서 메시지를 출력하도록 JSP 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <p>
        <spring:message code="register.done"
            arguments="${registerRequest.name}"/>
    </p>
    <p>
        <a href="<c:url value='/main'/>">
            [<spring:message code="go.main"/>]
        </a>
    </p>
</body>
</html>

코드를 보면 다음과 같은 코드가 있다.

  • < spring:message> 커스텀 태그를 사용하기 위해 태그 라이브러리 설정 추가 :
    <%@ taglib prefix=”spring” uri=”http://www.springframework.org/tags” %>
  • < spring:message> 태그를 이용해서 메시지 출력

< spring:message> 태그의 code값은 앞서 작성한 프로퍼티 파일의 프로퍼티 이름과 일치한다. < spring:message> 태그는 code와 일치하는 값을 가진 프로퍼티 값을 출력한다.

< spring:message> 태그는 MessageSource 코드에 해당하는 메시지를 읽어온다. 앞서 작성한 MessageSource는 label.properties 파일로부터 메시지를 읽어오므로 < spring:message> 태그의 위치에 label.properties에 설정한 프로퍼티의 값이 출력된다.

다국어 지원 위한 메시지 파일
다국어 메시지 를 지원하려면 각 프로퍼티 파일 이름에 언어에 해당하는 로케일 문자를 추가한다. 예를 들어 한국어와 영어에 대한 메시지를 지원하려면 다음의 두 프로퍼티 파일을 사용하면 된다.

  • label_ko.properties
  • label_en.properties

label 뒤에 ‘_언어’ 형식의 접미사가 붙었다. 언어는 두 글자 구분자로 한국어 ‘ko’, 영어는 ‘en’이다. 각 언어를 위한 두 글자 구분자가 존재한다. 특정 언어에 해당하는 메시지 파일이 존재하지 않으면 언어 구분이 없는 label.properties 파일의 메시지를 사용한다.

브라우저는 서버에 요청을 전송할 대 Accept-Language 헤더에 언어 정보를 담아 전송한다. 예를 들어 브라우저의 언어 설정이 한글인 경우 브라우저는 Accept-Language 헤더의 값으로 “ko”를 전송한다.

스프링 MVC는 웹 브라우저가 전송한 Accept-Language 헤더를 이용해서 Locale을 구한다. 이 Locale을 MessageSource에서 메시지를 구할 때 사용한다.

2.3 메시지 처리를 위한 MessageSource와 < spring:message> 태그

스프링은 로케일(지역)에 상관없이 일관된 방법으로 문자열(메시지)을 관리할 수 있는 MessageSource 인터페이스를 정의하고 있다. MessageSource 인터페이스는 다음과 같이 정의되어 있다.

특정 로케일에 해당하는 메시지가 필요한 코드는 MessageSource의 getMessage()메시지를 이용해서 필요한 메시지를 가져와서 사용하는 식이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.springframework.context;

import java.util.Locale;

public interface MessageSource {
    String getMessage(String code, Object[] args,
        String defaultMessage, Locale locale);
    
    String getMessage(String code, Object[] args, Locale locale)
        throws NoSuchMessageException;

    ....// 일부 메서드 생략
}

getMessage() 메서드는 code 파라미터는 메시지를 구분하기 위한 코드이고 locale 파라미터는 지역을 구분하기 위한 Locale 이다. 같은 코드라 하더라도 지역에 따라 다른 메시지를 제공할 수 있도록 MessageSource를 설계했다. 이 기능을 사용하면 국내에서 접근함녀 한국어로 메시지를 보여주고 해외에서 접근하면 영어로 메시지를 보여주는 처리를 할 수 있다.

MessageSource의 구현체로는 자바의 프로퍼티 파일로부터 메시지를 읽어오는 ResourceBundleMessageSource 클래스를 사용한다. 이 클래스는 메시지 코드와 일치하는 이름을 가진 프로퍼티의 값을 메시지로 제공한다.

ResourceBundleMessageSource 클래스는 자바의 리소스번들(ResourceBundle)을 사용하기 때문에 해당 프로퍼티 파일이 클래스 패스에 위치해야 한다. 앞서 메시지 파일을 파일을 클래스 패스에 포함되는 src/main/resources에 프로퍼티 파일을 위치시켰다. 보통 관리 편의성을 위해 프로퍼티 파일을 한곳에 모은다. 예제에서는 message라는 패키지에 프로퍼티 파일을 위치시켰다.

< spring:message> 태그는 스프링 설정에 등록된 “messageSoure” 빈을 이용해서 메시지를 구한다. 즉 < spring:message> 태그를 실행하면 내부적으로 MessageSource의 getMessage() 메서드를 실행해서 필요한 메시지를 구한다.

< spring:message> 태그의 code 속성에 지정한 메시지가 존재하지 않으면 익셉션이 발생한다.

2.4 < spring:message> 태그의 메시지 인자 처리

앞서 작성한 label.properties 파일을 보면 다음과 같은 프로퍼티를 포함하고 있다.

1
register.done=<strong>{0}님</strong>, 회원 가입을 완료했습니다.

이 프로퍼티는 값 부분에 {0}을 포함한다. {0}은 인덱스 기반 변수 중 0번 인덱스의 값으로 대치되는 부분을 표시한 것이다. MessageSource의 getMessage() 메서드는 인덱스 기반 변수를 전달하기 위해 다음과 같이 Object 배열 타입의 파라미터를 사용한다.

1
2
String getMessage(String code, Object[] args,
    String defaultMessage, Locale locale);

위 메서드를 사용해서 MessageSource 빈을 직접 실헹한다면 다음과 같이 Object 배열을 생성해서 인덱스 기반 변수값을 전달할 수 있당,

1
2
3
Object args = new Object[1];
args[0] = "자바";
messageSource.getMessage("register.done", args, Locale.KOREA);

< spring:message> 태그를 사용할 때에는 arguments 속성을 사용해서 인데스 기반 변수값을 전달한다. 다음과 같이 arguments 속성을 사용해서 register.done 메시지의 {0} 위치에 삽입할 값을 설정한다.

1
<spring:message code="register.done" arguments="${registerRequest.name}" />

2.4.1 복수개 인자

label.properties 파일의 register.done 프로퍼티에 {1}을 추가해보자

1
register.done=<strong>{0}님 ({1})</strong>, 회원 가입을 완료했습니다.

이 메시지를 사용하려면 두 개의 인자를 전달해야 한다. 두 개 이상의 값을 전달해야 할 경우 다음 방법 중 하나를 사용한다.

  • 콤마로 구분한 문자열
  • 객체 배열
  • < spring:argument> 태그 사용
  1. 콤마로 구분한 문자열
1
2
<spring:message code="register.done"
    arguments="${registerRequest.name}, ${registerRequest.email}"/>
  1. < spring:argument> 태그 사용
1
2
3
4
<spring:message code="register.done">
    <spring:argument value="${registerRequest.name}"/>
    <spring:argument value="${registerRequest.email}"/>
</spring:message>

Ref.

  • 최범균, 스프링프로그래밍입문5, 가메출판사.

카테고리:

업데이트: