타임리프를 사용해서 폼에서 체크박스, 라디오 버튼, 설렉트 박스를 편리하게 사용하는 방법을 알아본다.
상품 종류는 ENUM
을 사용했고, 설명을 위해 description
필드를 추가했다.
배송방식의 code
는 FAST
같은 시스템에서 전달하는 값,
displayName
은 빠른 배송
같은 고객에게 보여주는 값
판매 여부의 체크를 통한 값 전달에 대해 알아보자. 먼저 타임리프 기능 없이 일반 순수 HTML을 통해 값을 전달한다.
컨트롤러에서 값을 받으면 컨트롤러에서 로그가 찍힐 것이다.
먼저 판매 오픈에 체크를 했을 경우,
폼 데이터에서 on
이라고 입력되고 스프링은 이를 True
로 넣어준다.
그렇다면, 체크를 안하면 어떻게 될까
폼 데이터에서는 값이 없게 되고 null
이 들어가게 된다.
HTML checkbox
의 특징이다. 체크가 안됐을 경우 값 자체를 보내지 않는다. 이 특징은 수정을 할때 문제가 발생할 수도 있다. 서버에서 값이 아예 들어오지 않는 것이 의도적으로 넣지 않은 것인지 알 수 없다.
여기서 스프링MVC는 트릭을 하나 사용하는데 히든 필드를 하나 만들어서 _open
처럼 기존 체크 박스이름 앞에 _
를 붙여서 전송하면 체크를 해제했다고 인식하게 할 수 있다.
_open=on
보기와 같이 체크가 해제되었을때 _open
필드는 무조건 값이 들어가고 그 조건으로 open
값에 false
를 넣는 방식이다.
만약에 체크박스에 체크가 오게 되면
open=on&_open=on
스프링 MVC는 open
에 값이 있는 것을 확인하고 이때 _open
은 무시한다.
하지만, 체크박스를 만들때마다 이를 일일이 만드는 것도 불편하기 때문에
이를 타임리프가 해결해준다.
th:field
를 통해 값을 넣어준다.(*{open}
도 가능) 그럼 실행할때 어떻게 나올까
id, name을 넣어줄 뿐만 아니라 hidden
태그도 자동으로 만들어 준다.
그러므로 MVC 트릭이 잘 작동하게 된다.
또, 저장후 완료된 모습을 보여주기 위해 체크박스에 값을 넣어주면
이런식으로 되는데, 원래라면 checked
속성은 값이 있기만 해도 체크가 되는 특징이 있어 if
문을 통해 값이 있으면 속성 넣고 없으면 안넣는 로직을 따로 만들어야 하는데 th:field
를 넣으면 이것도 알아서 처리를 해서 값이 있으면 속성을 넣고 없으면 속성 자체를 없애준다.
요약 : th:field
는 킹왕짱이다.
지역을 넣기 위해 모델에 다음 값들을 넣는다.(LinkedHashMap
을 사용하면 순서대로 값이 들어가게 할 수 있다.) 그런데 여기서 이 값들을 아이템 추가할 때 뿐만 아니라, 아이템 세부사항, 아이템 수정에도 모두 넣어줘야한다.
그렇기 때문에 이런 공통사항을 한곳에 묶을 수 있는 기능이 있다.
이렇게 작성을 하면 이 컨트롤러 안에서 실행된 모든 메서드의 모델에 regoins
에 반환값이 자동으로 들어가게 된다. 이 방법을 이용하면 일일이 모든 모델에 값을 넣지 않아도 된다.
다만, 성능 최적화에 고민을 해볼 필요는 있다. 모든 모델에 일일이 값을 넣기 때문이다.
여기서 th:field
로 인해 id와 name
이 자동으로 생성된다. name
은 중복으로 생성되지만 id
는 다르게 생성된다. 그렇다면 이 each
문에서 label
태그를 클릭해도 체크가 되게끔 input
의 id
에 연결하고 싶은데 어떻게 해야 할까
th:for="${#ids.prev('regions')}"
타임리프의 기능중에 #ids
라는게 있다. 이는 쉽게 말해 regoins
필드의 아이디를 자동으로 가져오겠다는 의미이다.
이런 식으로 아이디를 잘 가져올 수 있다.
또, 이 경우에도 _regoins
가 히든값에 들어가기 때문에
이런식으로 빈 배열이 오게 된다. 히든값이 없다면 null
로 들어오게 된다.
요걸 쓸거다
ENUM
에서 values
를 사용하면 ENUM
의 모든 데이터를 배열로 만들어준다.
그런데 소스코드를 보면 실행했을때 hidden
태그가 없다. 체크박스의 경우에는 히든 태그를 넣지만, 라디오는 넣지 않는다.
그래서 출력은
이렇게 아무것도 선택하지 않을 경우 null
이 들어가게 된다. 라디오는 상품 상세나 상품 수정에 큰 영향을 주지 않기 때문에 특별한 트릭이 따로 없다. 또, 라디오의 경우 한 번 체크가 되면 취소가 안되기 때문에 이에 대한 트릭이 필요없는 것도 있다.
<div th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}">
스프링EL 문법으로 직접 접근할 수도 있다.
그런데 이렇게 사용하면 ENUM의 패키지 위치가 변경되거나 할때 자바 컴파일러가 타임리프까지 컴파일 오류를 잡을 수 없으므로 추천하지는 않는다.
예제에서는 new로 일일이 생성해서 넣어줬지만, 실제로는 다른곳에서 static으로 데이터를 만들고 사용하는 것이 좋다.
option이라는 태그를 이용해 저렇게 선택할 수 있는지 처음알았다.