스프링 MVC3

Single Ko·2023년 6월 2일
0

Spring 강의 정리

목록 보기
13/31

타임리프와 스프링의 통합

스프링 통합으로 추가되는 기능

  • 스프링의 SpringEL 문법 통합
  • ${@myBean.doSomething()} 처럼 스프링 빈 호출 지원
  • 편리한 폼 관리를 위한 추가 속성
    • th:obejct(기능 강화, 폼 커맨드 객체 선택)
    • th:field, th:errors, th:errorclass
  • 폼 컴포넌트 기능 : checkbox , radio button, List 등을 편리하게 사용할 수 있는 기능
  • 스프링의 메시지, 국제화 기능의 편리한 통합
  • 스프링의 검증, 오류 처리 통합
  • 스프링의 변환 서비스 통합(ConversionService)

설정 방법
타임리프를 스프링 프로젝트에 등록하고 싶다면 공식 문서에 잘 올라 와 있다. Spring+Thymeleaf를 보자.

그리고 이게 더 중요한데, Spring boot를 사용한다면 이런 설정을 할 필요가 없이 스프링 부트가 알아서 자동으로 다 등록해준다.

입력 폼 처리

타임리프가 제공하는 HTML Form 기능을 적용해보자.

타임리프가 제공하는 입력 폼 기능을 사용하려면 빈 아이템 등록 객체라도 넘겨야 한다. Model을 이용해서 빈 객체를 하나 넘기자.
(빈 객체를 하나 넘기는것이 비생산적이라 느낄 수 있지만 이렇게 빈 객체를 생성하는 것은 비용이 얼마 들지도 않고, 강력한 기능과 함께 검증도 할 수 있음.)

//기존
@GetMapping("/add")
public String addForm() {
	return "item/add"
}

//th:object 적용을 위해 해당 오브젝트 정보를 넘겨줘야됨. 
등록 폼이기에 데이터가 비어있는 빈 오브젝트를 만들어서 뷰에 전달했음.
@GetMapping("/add")
public String addForm(Model model) {
	model.addAttribute("item", new Item());
    return "item/add"
}


<form th:action th:object=${item}>
	<input type="text" id="itemName" name="itemName" >
	<input type="text" th:field="${item.itemName}" >  
    //th:field가 렌더링 되면 자동으로 id와 name을 만들어 준다.
   <input type="text" th:field="*{itemName}" >  
	// object를 선언해두면 앞에 item이라고 안붙이고 그냥 *{objectField} 로도 선언 가능
</form>

th:object=${} 를 이용한 th:field 를 사용하면 실수로 이름을 잘못 적을 시 오류가나서 잘못 된것을 알아 챌 수 있다. 그냥 id와 name으로는 잘못되어도 체크가 안됨.

th:field는 input 속성의 id, name, value 속성을 모두 자동으로 만들어준다.
(여기서는 빈 객체를 넘겼기때문에 value에 값은 빈 값이다.)

th:object,th:field 의 강력한 기능은 번거로운 개발을 많이 줄여준다.
여기서 더 강력한 진짜 기능은 validation에 있다.

체크박스 , 라디오 버튼, 셀렉트 박스

체크박스 - 단일

<div>판매 여부</div>
<div>
	<div class="form-check">
    <input type="checkbox" id="open" name="open" class="form-check-input">
 	<label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>

순수 HTML -> 단순 체크박스

  • 폼에서 체크해서 넘길 시 값이 on으로 넘어오고 스프링은 true로 타입 변환을 해준다.
  • 문제는 폼에서 체크하지 않고 넘길시 아예 필드 자체가 서버로 전송되지 않고, false도 아니고 null로 반환 된다.

상황에 따라 수정의 경우에는 이 방식이 문제가 될 수 있다. - 의도적으로 체크를 해제해도 저장시 아무값이 넘어가지 않기 때문에, 서버 구현에 따라서 값이 오지 않은것으로 판단해 기존의 값을 변경하지 않을 수 있다.

스프링의 기능 제공
hidden type 제공

<div>판매 여부</div>
<div>
	<div class="form-check">
    <input type="checkbox" id="open" name="open" class="form-check-input">
    <input type="hidden" name="_open" value="on">
 	<label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>

히든 필드를 추가 했기 때문에 체크를 안해도 _open의 값은 무조건 넘어오게 되있다. open은 넘어오지 않고 _open 만 넘어올 경우 스프링은 null이 아니고 false로 인식을 해준다.

이걸 계속 개발자가 넣어줘야된다. 이런 불편한 점을 타임리프가 해결해준다.

Thymeleaf의 기능 제공

<div>판매 여부</div>
<div>
	<div class="form-check">
	<input type="checkbox" th:field="*{open}" class="form-checkinput">
	<label for="open" class="form-check-label">판매 오픈</label>
	</div>
</div>

타임리프가 input tpye = "checkbox" 타입이라면, id, name, value 뿐만 아니라
<input type="hidden" name="_open" value="on">도 자동으로 만들어서 넣어준다. 거기다 checked 속성까지 true, false에 따라 자동으로 해준다. (만약 없었다면 if 조건문으로 true면 checked 넣고, false면 checked를 넣지 않는 식을 넣어줘야됨...)

개발자는 타임리프 덕분에 신경쓰지 않고 직관적으로 개발 가능하다.

체크박스 - 멀티

스프링의 @ModelAttribute의 특별한 기능.

@ModelAttribute("regions")
public Map<String, String> regions() {
	Map<String, String> regions = new LinkedHashMap<>();
    regions.put("SEOUL", "서울");
    regions.put("BUSAN", "부산");
    regions.put("JEJU", "제주");
	return regions;
}

위와 같이 URL 메핑이아닌 클래스에 메서드로 @ModelAttribute를 선언해 주면 어떤 것이 호출이 되든 모델에 자동으로 담기게 된다. 다만 이때 항상 새로운 객체가 생성되어 넘어간다는 것을 기억해야된다. 이런 방법을 사용하려면 어딘가에 static으로 생성해놓는게 더 효율적일 것이다.
물론 이렇게 하지 않고, 일일이 컨트롤러 메서드에서 모델에 직접 데이터를 담아 처리해도 된다.

<div>
	<div>등록 지역</div>
	<div th:each="region : ${regions}">
		<input type="checkbox" th:field="*{regions}" th:value="${region.key}">
		<label th:for="${#ids.prev('regions')}" th:text="${region.value}">서울</label>
	</div>
</div>

다른것은 다 이해가 가능할 것이다. 다만 label의 th:for="${#ids.prev('regions')}"가 특이하다.

반복적으로 html 코드를 생성할때, name은 같아도 되지만, id는 같으면 안된다. 따라서 타임리프는 동적으로 아이디가 생성이 될때 타임 리프는 #ids를 지원해준다. 이 기능을 사용하면 1,2,3이 붙어서 자동으로 아이디를 다르게 해준다.

라디오 버튼

여러 선택지 중에 하나만 선택이 가능한 체크 박스이다.

@ModelAttribute("itemTypes")
public ItemType[] itemTypes() {
	return ItemType.values();
}

<!-- radio button -->
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}">
	<input type="radio" th:field="*{itemType}" th:value="${type.name()}">
	<label th:for="${#ids.prev('itemType')}" th:text="${type.description}">BOOK</label>
</div>
</div>

타임리프에서 ENUM 직접 접근
<div th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}">

라디오 버튼은 체크를 안해서 값을 넘겨 Null로 가도 별 상관이 없다. 또한 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다.

타임리프에서 ENUM 직접 접근은 @ModelAttribute로 Model을 안넘겨 줘도, 직접 패키지를 입력해서 접근이 가능하다. 다만 이 방법은 추천하지 않음.(치는것도 어렵고, 패키지 이동시 수정의 번거로움이 크다.)

셀렉트 박스

셀렉트 박스는 여러 선택지 중에 하나를 선택할 때 사용할 수 있다

<div>
	<div>배송 방식</div>
	<select th:field="*{deliveryCode}">
		<option value="">==배송 방식 선택==</option>
		<option th:each="deliveryCode : ${deliveryCodes}" 
        		th:value="${deliveryCode.code}" 
                th:text="${deliveryCode.displayName}">FAST</option>
	</select>
</div>

참고 : 본 글은 김영한님의 스프링 강의를 정리한 것입니다.

profile
공부 정리 블로그

0개의 댓글