메시지가 뭐고 왜 필요하냐면요
예를 들어 이런 필드가 있다고 할 때, 상품명 이라는 라벨을 모두 상품 이름으로 변경해달라고 하면 어떻게 할 것인가?
모든 코드를 까서 ctrl F 로 라벨을 찾아 수정하는 방법이 가장 단순한 방법일 것이다.
근데 만약 수정해야하는 파일의 개수가 1000개면 하루종일 그것만 하고 있을 것인가?
기존 코드는 다음과 같이 상품명 이라는 텍스트가 하드코딩 되어 있기 때문에, 모두 까서 수정하기 힘들다.
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control" placeholder="이름을 입력하세요">
</div>
그래서 이러한 메시지를 Key - value 형태로 저장해두고 관리하도록 하는 기능을 메시지 기능이라고 한다.
hello=안녕
hello.name=안녕 {0}
label.item=상품
label.item.id=상품 ID
label.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
국제화는 모야 모야
프로젝트를 국제로 보내려고 하는게 국제화다.(진짜임)
위에서 설명했던 메시지 파일을 다양한 언어로 만들어두고, 해당 국가나 웹 브라우저가 설정한 HTTP 요청의 accept-header 헤더를 확인하여 사용자가 원하는 언어로 웹을 표시할 수 있도록 한다.
hello=hello
hello.name=hello {0}
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=Price
label.item.quantity=Quantity
스프링이 기본적으로 메시지와 국제화 기능을 모두 제공하기 때문에, 편리하게 통합하여 사용할 수 있다.
스프링 메시지 소스 설정
메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource를 스프링빈으로 등록해야하는데, 예를 들면 다음과 같다.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("message");
messageSource.setBasename("error");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
근데 스프링 부트가 자동으로 위 빈을 등록해주기 때문에 따로 수동 빈으로 등록하지 않아도 된다.
이후, application.properties에 기본 값을 다음과 같이 설정해주면, messages.properties 파일들이 자동으로 인식된다.
spring.message.basename=messages
메시지 파일 만들기
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=취소
text.saved=저장 완료
placeholder.name=이름을 입력하세요
placeholder.price=가격을 입력하세요
placeholder.quantity=수량을 입력하세
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
text.saved=Saved Complete
placeholder.name=Enter Name
placeholder.price=Enter Price
placeholder.quantity=Enter Quantity
한국어, 영어를 고려하여 두가지 메시지 파일을 만들었다.
타임 리프에 적용해봅시다
편의상 addForm의 코드 일부분에만 적용한 것을 설명하겠다.
타임리프에서 메시지 표현식 #{} 을 쓰면 메시지를 편리하게 조회할 수 있다.
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control"
th:placeholder="#{placeholder.name}" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control"
th:placeholder="#{placeholder.price}" placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control"
th:placeholder="#{placeholder.quantity}" placeholder="수량을 입력하세요">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit"
th:text="#{button.save}">저장</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/message/items}'|"
type="button" th:text="#{button.cancel}">취소</button>
</div>
</div>
</form>
#{} 부분은 모두 저장해둔 메시지로 대체 될 부분들이다.
서버를 실행하여 확인해보면, 우리가 메시지로 저장해뒀던 부분으로 대체 되어 인코딩 되는 것을 확인할 수 있다.
국제화는 어떻게 일어나나요
이전에 우리는 영어로 된 메시지 파일도 만들어두었다. 사실 우리가 할건 끝났다.
MessageSource는 국가 즉 Locale 파라미터가 반드시 있어야한다. (없으면 basename으로 설정했던 것이 실행됨)
결국 스프링도 이 Loacle 파라미터가 필요한데, 자동으로 헤더를 파싱후에 Accept-language 헤더의 값을 사용한다.
순수 나의 궁금증 - Locale 자동 사용까진 OK, 어떻게 메시지 파일을 자동으로 선택하는지?
뭐 대충 정리하자면, 파일 이름의 suffix를 보고 Locale과 비교하여 알잘딱 선택해주는 것 같다.
'Spring > MVC' 카테고리의 다른 글
[Spring] @ModelAndAttribute 의 동작 과정 (0) | 2025.06.24 |
---|---|
[Spring] Spring MVC가 제공하는 Request 매핑 기능 (0) | 2025.06.24 |
[Spring] Spring MVC - 스프링이 제공하는 컨트롤러 (0) | 2025.06.23 |
[Spring] 내가 만든 MVC 와 Spring MVC 차이 (1) | 2025.06.23 |
[Spring] FrontController V5(完) - 다양한 인터페이스 구현체 처리하기 (2) | 2025.06.23 |