개발/Spring MVC

스프링 MVC 2편 - Thymeleaf 스프링 통합과 폼

Debin 2022. 1. 19.
반응형
본 게시글은 인프런 김영한 선생님 강의 스프링 MVC 2편을 완강하고 배운 것을 남기고자 적은 포스팅입니다.

 

강의 링크는 아래와 같습니다.

 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

 

타임리프 스프링 통합

타임리프는 스프링 없이도 동작하지만, 스프링과 통합을 위한 다양한 기능을 편리하게 제공한다. 그리고 이런 부분은 스프링으로 백엔드를 개발하는 개발자 입장에서 타임리프를 선택하는 하나의 이유가 된다.

 

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

 

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

 

스프링 부트는 이런 부분을 모두 자동화해준다. build.gradle 에 다음 한 줄을 넣어주면 Gradle은 타임리프와 관련된 라이브러리를 다운로드하고, 스프링 부트는 앞서 설명한 타임리프와 관련된 설정용 스프링 빈을 자동으로 등록해준다.

 

build.gradle 에 implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'를 등록하면 된다.

 

입력 폼 처리

  • th:object : 커맨드 객체를 지정한다.
  • *{...} : 선택 변수 식이라고 한다.  th:object에서 선택한 객체에 접근한다.
  • th:field
    • HTML 태그의 id , name , value 속성을 자동으로 처리해준다.

 

<!--렌더링 전-->
<input type="text" th:field="*{itemName}" />

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

 

등록 폼

 

th:object를 적용하려면 먼저 해당 오브젝트 정보를 넘겨주어야 한다.

등록 폼이기 때문에 데이터가 비어있는 빈 오브젝트를 만들어서 뷰에 전달하자.

 

폼에 대한 코드를 간단하게 살펴보겠다.

 

<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>
 
     <div>
         <label for="price">가격</label>
         <input type="text" id="price" th:field="*{price}" class="form-control"
        placeholder="가격을 입력하세요">
     </div>

     <div>
         <label for="quantity">수량</label>
         <input type="text" id="quantity" th:field="*{quantity}" class="formcontrol" placeholder="수량을 입력하세요">
     </div>
 
 </form>

 

  • th:object="${item}" : 에서 사용할 객체를 지정한다. 선택 변수 식( *{...} )을 적용할 수 있다.
  • th:field="*{itemName}
    • " *{itemName}는 선택 변수 식을 사용했는데, ${item.itemName}과 같다. 앞서 th:object로 item을 선택했기 때문에 선택 변수 식을 적용할 수 있다.
    • th:field는 id , name , value 속성을 모두 자동으로 만들어준다.
      • id : th:field 에서 지정한 변수 이름과 같다. id="itemName"
      • name : th:field 에서 지정한 변수 이름과 같다. name="itemName"
      • value : th:field 에서 지정한 변수의 값을 사용한다. value=""

 

참고로 해당 예제에서 id를 제거해도 th:field가 자동으로 만들어준다.

 

<!-- 렌더링 전-->
<input type="text" id="itemName" th:field="*{itemName}" class="form-control"placeholder="이름을 입력하세요">

<!-- 렌더링 후 -->
<input type="text" id="itemName" class="form-control" placeholder="이름을 입력하세요" name="itemName" value="">

 

th:object, th:field 덕분에 큰 편리함을 얻었다.

 

 

form 체크 박스

다음과 같은 코드가 form에 추가 되었다.

 

<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>

위에서 보면 open이라는 필드가 item에 추가되었다고 생각하자. 그러면 체크 박스로 체크를 한 후 서버에다가 요청을 전달해보자.

 

체크 박스를 선택했다면 item.open=true로 출력된다.

체크 박스를 선택하지 않았다면 item.open=false로 출력된다.

 

더 정확하게 말하면 체크 박스를 체크하면 HTML Form에서 open=on이라는 값이 넘어간다. 스프링은 on이라는 문자를 true 타입으로 변환해준다. (스프링 타입 컨버터가 이 기능을 수행한다.)

 

주의 - 체크 박스를 선택하지 않을 때 HTML에서 체크 박스를 선택하지 않고 폼을 전송하면 open이라는 필드 자체가 서버로 전송되지 않는다.

 

참고!!!

HTTP 요청 메시지를 서버에서 보고 싶으면 다음 설정을 추가하면 된다.

application.properties -> 추가 logging.level.org.apache.coyote.http11=debug

 

 

 

HTML checkbox는 선택이 안되면 클라이언트에서 서버로 값 자체를 보내지 않는다. 수정의 경우에는 상황에 따라서 이 방식이 문제가 될 수 있다. 사용자가 의도적으로 체크되어 있던 값을 체크를 해제해도 저장 시 아무 값도 넘어가지 않기 때문에, 서버 구현에 따라서 값이 오지 않은 것으로 판단해서 값을 변경하지 않을 수도 있다.

 

이런 문제를 해결하기 위해서 스프링 MVC는 약간의 트릭을 사용하는데, 히든 필드를 하나 만들어서, _open처럼 기존 체크 박스 이름 앞에 언더스코어( _ )를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다. 히든 필드는 항상 전송된다. 따라서 체크를 해제한 경우 여기에서 open 은 전송되지 않고, _open 만 전송되는데, 이 경우 스프링 MVC는 체크를 해제했다고 판단한다.

 

코드를 수정하면 아래와 같다.

 

<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>

 

그럼 이제 체크 박스를 체크한 경우와, 체크하지 않은 경우를 모두 서버에 보내보자.

 

체크박스를 체크한 경우는 item.open=true

체크박스를 체크하지 않은 경우는 item.open=false

이렇게 출력된다. null이 아니라 false가 출력되는 것을 확인할 수 있다.

 

정말 좋은 기능이지만 이렇게 개발할 때마다 히든 필드를 추가하는 것은 귀찮다..ㅎㅎㅎ..

타임리프가 제공하는 폼 기능을 이용하면 이런 부분을 자동으로 처리할 수 있다.

코드를 아래와 같이 수정하자.

 

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

 

이런 코드를 작성하고 실제로 브라우저에서 HTML 소스 코드를 확인하면 아래와 같은 결과물이 나온다.

 

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

 

타임리프를 사용하면 체크 박스의 히든 필드와 관련된 부분도 함께 해결해준다.

HTML 생성 결과를 보면 히든 필드 부분이 자동으로 생성되어 있다.

 

checked="checked"

체크 박스에서 판매 여부를 선택해서 저장하면, 조회 시에 checked 속성이 추가된 것을 확인할 수 있다. 이런 부분을 개발자가 직접 처리하려면 상당히 번거롭다. 타임리프의 th:field를 사용하면, 값이 true 인 경우 체크를 자동으로 처리해준다.

 

체크 박스 멀티 파트도 비슷하게 진행한다. 궁금하시면 강사님 강의를 들어보면 좋을 거 같다.*^^*

 

다음은 라디오 버튼이다.

라디오 버튼은 여러 선택지 중에 하나를 선택할 때 사용할 수 있다.

추가한 코드는 아래와 같다.

 

<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>

 

상품 타입을 선택하지 않는다면 아무 값도 넘어가지 않는다.

 

item.itemType=Food: 값이 있을 때

item.itemType=null:   값이 없을 때

 

체크 박스는 수정 시 체크를 해제하면 아무 값도 넘어가지 않기 때문에, 별도의 히든 필드로 이런 문제를 해결했다. 라디오 버튼은 이미 선택이 되어 있다면, 수정 시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다.

상품을 보여줄 때는 라디오 버튼이 선택되지 못하게 disabled 속성을 사용했다.

 

이상으로 포스팅을 마치겠습니다.

반응형

댓글