김영한 선생님의 스프링 MVC 2편 강의를 듣고 정리한 내용이다.
th:object
: 커맨드 객체를 지정한다.
*{...}
: 선택 변수 식이라고 한다. th:object 에서 선택한 객체에 접근한다.
th:field
: HTML 태그의 id , name , value 속성을 자동으로 처리해준다.
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="formcontrol" placeholder="이름을 입력하세요">
</div>
렌더링 전
<input type="text" th:field="*{itemName}" />
렌더링 후
<input type="text" id="itemName" name="itemName" th:value="*{itemName}" />
th:object="${item}"
: <form>
에서 사용할 객체를 지정한다. 선택 변수 식( *{...} )을 적용할 수있다.th:field="*{itemName}"
*{itemName}
는 선택 변수 식을 사용했는데, ${item.itemName}
과 같다. 앞서 th:object
로 item
을 선택했기 때문에 선택 변수 식을 적용할 수 있다.렌더링 전
<input type="text" id="itemName" th:field="*{itemName}" class="form-control"
placeholder="이름을 입력하세요">
렌더링 후
<input type="text" id="itemName" class="form-control" placeholder="이름을
입력하세요" name="itemName" value="">
체크박스는 체크시 HTML에서 open = on
이라는 값이 넘어가고 스프링 타입 컨버터가 on
은 true
타입으로 변환해준다.
다만 체크박스 선택하지 않을 시 open field
자체가 서버로 전송되지 않는다. 수정의 경우에는 이게 문제가 될 수 있다.
이를 해결 하기 위해 MVC는 히든 필드를 하나 만드는데, 체크 박스 이름 앞에 _를 붙여서 전송하면 체크를 헤제했다고 인식할 수 있다. 체크를 해제한 경우 '_open'
만 전송돼서 이를 통해 체크 해제를 판단한다.
<input type="checkbox" id="open" name="open" class="form-check-input">
<input type="hidden" name="_open" value="on" > // 히든필드
<input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
위의 체크박스를 확장해서 다중 선택이 가능케 한다.
여기서 ModelAttribute
를 기존과는 조금 다르게 활용한다.
체크 박스를 반복해서 보여주어야 하는데, 이를 위해선 각각의 컨트롤러에서 model.addAttribute(...)
를 사용해서 체크 박스를 구성하는 데이터를 반복해서 넣어주어야 한다. 이를 위해 컨트롤러에 아래와 같이 ModelAttribute
를 이용하면,
@ModelAttribute("regions")// 일반적인 ModelAttribute와 다른 기능
public Map<String, String> regions(){
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BERLIN", "베를린");
regions.put("LIVERPOOL", "리버풀");
return regions;
} // 컨트롤러 호출시 항상 ModelAttribute를 통해 regions의 반환 값이 Model에 자동으로 담긴다.
th:for="${#ids.prev('regions')}"
멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다. 그런데 문제는 이렇게 반복해서 HTML 태그를 생성할 때, 생성된 HTML 태그 속성에서 name
은 같아도 되지만, id
는 모두 달라야 한다. 따라서 타임리프는 체크박스를 each
루프 안에서 반복해서 만들 때 임의로 1 , 2 , 3 숫자를 뒤에 붙여준다.
HTML의 id
가 타임리프에 의해 동적으로 만들어지기 때문에 <label for="id 값">
으로 label
의 대상이 되는 id 값을 임의로 지정하는 것은 곤란하다. 타임리프는 ids.prev(...)
, ids.next(...)
을 제공해서 동적으로 생성되는 id 값을 사용할 수 있도록 한다.
ENUM
을 통해서 개발해본다.ModelAttribute
의 기능을 활용한다. @ModelAttribute("itemTypes")
public ItemType[] itemTypes(){
ItemType[] values = ItemType.values();
return values; // ENUM의 정보를 배열로 반환
}
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input">
<label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label">
BOOK
</label>
</div>
</div>
NULL
로 그 값을 바꿀수 없다는 특징이 있다. 그래서 별도의 히든 필드 사용의 필요성이 떨어진다.여러 선택지 중에 하나를 선택할 때에 사용한다.
먼저 자바 객체를 만들고 반환해서 ModelAttribute
를 활용한다.
<!-- SELECT -->
<div>
<div>배송 방식</div>
<select th:field="*{deliveryCode}" class="form-select">
<option value="">==배송 방식 선택==</option>
<option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}"
th:text="${deliveryCode.displayName}">FAST</option>
</select>
</div>
th:object
를 사용하는지 등의 여부를 주목한다.<select th:field="${item.deliveryCode}" class="form-select" disabled>
HTML 파일에 메시지가 하드코딩 되어있어서 바꾸기 곤란한 경우에, 다양한 메시지를 한 곳에서 관리하여 바꾸기 쉽게 할 수 있는 기능이 메시지 기능이다.
메시지에서 설명한 메시지 파일(messages.properteis)을 각 나라별로 별도로 관리하면 서비스를 국제화 할 수 있다(접속 지역에 따라 언어를 다르게 설정)
application properties에 다음과 같은 코드를 넣으면 스프링 빈에 메세지 소스가 등록이 된다.
spring.messages.basename=messages
/resources/messages.properties
hello=안녕
hello.name=안녕 {0}
/resources/messages_en.properties
hello=hello
hello.name=hello {0}
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
// 지역 설정이 default로 되어있어서 "안녕"이 실행이 됨
assertThat(result).isEqualTo("안녕");
}
@Test
void argumentMessage() {
String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
// 값을 넘겨서 치환하는데 Object[] 배열을 사용
assertThat(result).isEqualTo("안녕 Spring");
}
@Test
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
NoSuchMessageException
이 발생한다.defaultMessage
)를 사용하면 기본 메시지가 반환된다.메시지를 활용하여 $대신 #을 쓰면 된다.
messages.properties
에 label.item=상품
등록시 아래 코드는
<div th:text="#{label.item}"></h2>
<div>상품</h2>
으로 렌더링 된다.
크롬 -> 브라우저 설정 -> 언어를 검색하고, 우선 순위를 변경하면 된다.
웹 브라우저의 언어 설정 값을 변경하면 요청시 Accept-Language 의 값이 변경된다.
영어버전 메시지 파일을 수정한 후 크롬의 언어 설정에서 영어를 제일 위로 적용하면 적용되는 것을 확인 할 수 있다.