스프링 MVC 2편 - 타임리프 - 스프링 통합과 폼

링딩·2022년 8월 9일
0

스프링 MVC

목록 보기
9/18

이 글은 김영한 강사님의 강의를 참고하여 작성하였습니다.


Chap 2. 타임리프 - 스프링 통합과 폼

이번 장은 굉장히 앞 장과 유사하기에 대표적인 기능들 위주로 정리.


th:object : 커맨드 객체를 지정한다.

  • th:object

    • Controller에서 model 곧, 해당 오브젝트 정보를 넘겨주어야 한다
    • form에서 submit을 할 때, form의 데이터가 th:object설정해준 객체로 받아진다.
  • *{...} : 선택 변수 식이라고 한다. th:object 에서 선택한 객체에 접근한다.

  • th:field

    • 각각 필드들을 매핑을 해주는 역할을 한다. 설정해 준 값으로, th:object에 설정해 준 객체의 내부와 매칭해준다.
    • 이게 있으면 HTML 태그의 id , name , value 속성을 자동으로 처리
  • 렌더링 전
    <input type="text" th:field="*{itemName}" />

  • 렌더링 후
    <input type="text" id="itemName" name="itemName" th:value="*{itemName}" />

+) 추가 설명🤔

  • th:action
    form 태그 사용 시, 해당 경로로 요청을 보낼 때 사용. (url)

+)만약 th:action에 경로를 지정하지 않으면 현재 form에 지정한 action인 item.html을 호출하게 됩니다.
th:action 값이 있으면, 타임리프가 서버 렌더링 시, action 부분은 빈 값으로 변경된다.
-> 곧, 현재 있는 url과 같은 url로 html form을 method="post"로 post 방식으로 요청하는 식이다.

이 부분은 계속 헷갈리니 링크를 걸어두고 계속 보면 좋을 것 같다.


  • th:field
    각각 필드들을 매핑을 해주는 역할을 한다. 설정해 준 값으로, th:object에 설정해 준 객체의 내부와 매칭해준다.



히든필드 _ 체크박스

<!-- single checkbox -->
<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>
  • HTML checkbox는 선택이 안되면 클라이언트에서 서버로 값 자체를 보내지 않는다.
  • 수정의 경우 [문제 발생]
    - 사용자가 의도적으로 체크되어 있던 값을 체크를 해제해도 위의 상황으로 인해 저장시 아무 값도 넘어가지 않기 때문에
    =>값이 오지 않은 것으로 판단해서 수정 상황에서 값을 변경하지 않을 수도 있다.
  • [해결 법] 👍😮
    - 히든 필드

히든필드

_open 처럼 기존 체크 박스 이름 앞에 언더스코어(_)를 붙여서 전송하면 체크를 해제했다고 인식한다.

  • 그리고, 히든 필드는 항상 전송된다.
    -> (체크를 해제) _open=on => 스프링 MVC는 체크를 해제했다고 판단
    -> (체크) open=on&_open=on

또한 서버에서 Boolean 타입을 찍어보면 결과가 null 이 아니라 false 인 것을 확인할 수 있다.


<hr class="my-4">
<!-- single checkbox -->
<div class="form-check">
 <input type="checkbox" id="open" class="form-check-input" disabled
name="open" value="true"
 checked="checked">
 <label for="open" class="form-check-label">판매 오픈</label>
</div>

disabled

상품이 선택되지 않도록 막아놓는 것.

checked

이전 저장된 상태가 true이면 그 결과를 가져와야 하는데 일일히 가져오기 귀찮다. 그렇기 때문에 타임리프의 th:field 를 사용하면, 값이 true 인 경우 체크를 자동으로 처리해준다



@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;
}

등록 폼, 상세화면, 수정 폼에서 모두 서울, 부산, 제주라는 체크 박스를 반복해서 보여주어야 한다.

  • [문제]
    이렇게 각각의 컨트롤러에서 model.addAttribute(...) 을 사용해서 일일히 체크 박스를 구성하는 데이터를 반복해서 넣어주어야 한다.
  • 해당 컨트롤러를 요청할 때 regions 에서 반환한 값이 자동으로 모델( model )에 담기게 된다.
    -> 물론 이렇게 사용하지 않고, 각각의 컨트롤러 메서드에서 모델에 직접 데이터를 담아서 처리하는 방법도 있다.


th:for="${#ids.prev('regions')}"

  • 멀티 체크박스
    * 같은 이름의 '여러 체크박스'를 만들 수 있다.
    -> but 생성할 때, 생성된 HTML 태그 속성에서 name 은 같아도 되지만, id 는 모두 달라야 한다.
    => 타임리프는 체크박스를 each 루프 안에서 반복해서 만들 때 임의로 1 , 2 , 3 숫자를 뒤에 붙여준다.

<label for="id 값">

  • label에서 대상이 되는 'id 값'을 임의로 지정하는 것곤란하다.
    ->HTML의 id 가 타임리프에 의해 동적으로 만들어져서...😢
  • 그래서 타임리프는 ids.prev(...) , ids.next(...) 을 제공한다.
    -> 이 덕에 동적으로 생성되는 id 값을 사용할 수 있도록 한다👍

[결과]

<!-- multi checkbox -->
<div>
 <div>등록 지역</div>
 <div class="form-check form-check-inline">
 <input type="checkbox" value="SEOUL" class="form-check-input"
id="regions1" name="regions">
 <input type="hidden" name="_regions" value="on"/>
 <label for="regions1"
 class="form-check-label">서울</label>
 </div>
 <div class="form-check form-check-inline">
 <input type="checkbox" value="BUSAN" class="form-check-input"
id="regions2" name="regions">
 <input type="hidden" name="_regions" value="on"/>
 <label for="regions2"
 class="form-check-label">부산</label>
 </div>
 <div class="form-check form-check-inline">
 <input type="checkbox" value="JEJU" class="form-check-input"
id="regions3" name="regions">
 <input type="hidden" name="_regions" value="on"/>
 <label for="regions3"
 class="form-check-label">제주</label>
 </div>
</div>
  • id 가 checkbox 에서 동적으로 생성된 regions1 , regions2 , regions3 에 맞추어 순서대로 입력된 것을 확인할 수 있다.



Enum에 직접 접근

방법 1. 모델에 Enum 담아 전달

방법 2. 타임리프 직접 접근

어디서 많이 본 문법일 것이다. 맞다 스프링 EL에서 봤을 것이다.

${T(hello.itemservice.domain.item.ItemType).values()}
  • values()로 반환
    ->해당 ENUM의 모든 정보가 배열로 반환 [추천 x]
    -> 왜? 추천하지 않냐? Enum의 파일 위치가 변경 됐을 때, 컴파일러가 '타임리프'까지 컴파일 에러를 잡아주지는 않아서

마지막 주의사항 🤔

헷갈릴 수 있다. 그러나 th:object 가 위에 기입되어 있지 않은 상태에서 *{...} 이나, th:field 와 같이 되었을거라고 생각해서는 안된다.
꼭 확인하고 사용하자

profile
초짜 백엔드 개린이

0개의 댓글