Thymeleaf 과 spring boot 연동하기

Thymeleaf는 웹서비스 제작을 위한 서버사이드의 자바 템플릿 엔진이다. WAS 혹은 웹서버가 구동되지 않더라도 HTML 파일이 브라우저에 올바르게 표시되도록 하여 개발과정에서 더 강력한 협업을 가능하게 한다. 보통 Spring Framework 과 같은 HTML5 JVM 웹 개발에 이상적이며, 홈페이지에서도 Thymeleaf + spring 문서가 별도로 존재한다.

대략 아래와 같은 회원입력 폼을 만들어보려 한다. bootstrap, spring boot, Thymeleaf 를 사용했으며, 기본적으로 제공된 예시를 바탕으로 진행했으니 자세한 사항은 여기를 클릭해서 참고한다.

Model

html 파일로 뷰(view)를 먼저 만들어야 할거 같지만, 화면과 웹 컨트롤러간에 주고받을 데이터의 구조를 설계하는것을 먼저 만드는 편이다. 화면에서 입력받거나 조회해서 보여줘야 할 데이터를 정의하고, 단건인지 다건인지도 고려해서 정의한다. MemberControllerVo 가 Thymeleaf 뷰에서 매핑될 객체이 구조를 잘 눈여겨 봐야 한다.

public class MemberControllerVo {

    private MemberBsc member;
    private List<ContactBsc> contacts;

    //...get/set 메소드 생략..
}

public class ContactBsc {

    private String memberId; // 고객ID
    private String relationCd; // 관계구분
    private String contactName; // 연락처명
    private String phoneNumber; // 연락처번호

    //...get/set 메소드 생략..
}

public class MemberBsc {

    private String memberId; // 고객ID
    private String memberName;    // 이름
    private String genderCd;  // 성별
    private String birthDt;   // 생년월일
    private String signupDt; // 가입일자
    private String signupReasonCd; // 가입사유
    private String information; // 비고

    //...get/set 메소드 생략..
}

Controller

이렇게 정의한 모델들을 기반으로 웹페이지에 데이터를 보내기 위해서 컨트롤러에서 객체를 만든다. 지금 직접 DB에 연결해서 조회하는 부분까지는 구현하지 않고, 빈 폼(form)을 만들기 위한 최소한의 객체 생성만 진행한다. memberInfo 이름으로 속성값을 지정한 걸 눈여겨보자. 그리고 test05 는 springboot 에서 템플릿 파일인 test05.html 을 의미한다.

@GetMapping(value = "/signup")
public String signupForm(String id, Model model) {

    MemberControllerVo memberControllerVo = 
        new MemberControllerVo();
    memberControllerVo.setMember(new MemberBsc());

    List<ContactBsc> contactBscs = new ArrayList<ContactBsc>();
    contactBscs.add(new ContactBsc());
    memberControllerVo.setContacts(contactBscs);

    model.addAttribute("memberInfo", memberControllerVo);
    return "test05";
}

View

보통 Thymeleaf 에서 값을 바인딩 하기 위해서는, 기본적으로 th:value, th:text 대략 이런 문법을 사용해서 값을 연결할 수 있는데, html 상의 form 내에서의 input, select, textarea 등의 입력부에는 th:field 를 사용하는게 좋다. th:field 속성은 input, select, textarea 각 태그에 연결되어 있는지 여부에 따라 최적으로 셋팅되고, input 태그의 타입유형에 따라도 다르게 작동된다고 한다.

아까 컨트롤러에서 정의한 memberInfo를 form 태그내에 th:object 로 정의한다. 그리고 그 내부에 있는 input 태그에는 th:field*{..} 표현식으로 속성을 매핑한다. * 는 이미 상위에 정의된 th:object 기준으로 하위 속성을 정의할 때 사용한다.

<form action="#" method="post"
    th:action="@{/signup}"
    th:object="${memberInfo}">

    <input id="name" type="text"
        th:field="*{member.memberName}">
</form>

성별을 입력받는 select 태그에도 동일하게 th:field="*{member.genderCd} 로 셋팅한다.

<select id="gender" th:field="*{member.genderCd}">
    <option disabled value="">-</option>
    <option value="M"></option>
    <option value="F"></option>
</select>

loop 로 동적인 input 태그를 생성하는 예시이다. + 버튼으로 입력하는란을 동적으로 행추가 할 수 있도록 기능을 만들었다. th:field 를 사용할 때 주의할 점은 th:object 정의된 명칭을 기준으로 th:field 에 바인딩해야 한다. th:field=${ctt.phoneNumber} 로 하게되면 에러가 발생하니, loop 변수를 직접사용하지말고, index 만 사용해서 직접 접근하도록 한다.

<div th:each="ctt,iterStat : *{contacts}">
    <input class="form-control" id="parentsTel1" type="text"
        th:field="*{contacts[__${iterStat.index}__].phoneNumber}">
</div>

이 두번째 변수인 iterStat 은 위치가 중요한거지 본인이 원하는 이름으로 정하면 된다. 이 변수를 가지고 index, count, size, current, even, odd, first, last 속성을 사용할 수 있게 되며, 간결한 loop 처리를 할 수 있도록 도와준다. 자세한 내용은 여기를 클릭해서 확인한다.


더 보면 좋을 글들