실습환경
개발환경
windows10(64)
spring4
java1.8
tomcat9
들어가기 이 글은 spring 개인 프로젝트에 thymeleaf를 적용함에 있어서, spring controller와 thymeleaf간의 데이터 전달 방식을 정리하기 위한 글이다. 게시판 crud 예제를 통해서 설명할 것이다.
게시판 글 리스트 조회
1.controller
1 2 3 4 5 6 7 8 9 @RequestMapping (value = "/listAll" , method = RequestMethod.GET)public String listAll (Locale locale, Model model) throws Exception { logger.info("show all list........" ); model.addAttribute("list" , service.listAll()); return "/samples/board/list" ;}
컨트롤러에서는 게시판글을 조회해서 글 vo를 List로 담아 model에 담는다.
2.list.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" xmlns:layout ="http://www.ultraq.net.nz/thymeleaf/layout" data-layout-decorate ="~{samples/layout/sampleLayout}" > <th:block layout:fragment ="css" > </th:block > <th:block layout:fragment ="script" > <script th:inline ="javascript" > $(function ( ) { var result = 'default' ; if (result == 'success' ){ alert("처리가 완료되었습니다." ); } $('#example1' ).DataTable({ 'ordering' : false , }); /* $('#example2').DataTable({ 'paging' : true , 'lengthChange' : false , 'searching' : false , 'ordering' : true , 'info' : true , 'autoWidth' : false }); */ }); </script > </th:block > <div layout:fragment ="content" > <div class ="content-wrapper" > <section class ="content-header" > <h1 > 게시판 리스트 <small > advanced tables</small > </h1 > <ol class ="breadcrumb" > <li > <a href ="#" > <i class ="fa fa-dashboard" > </i > Home</a > </li > <li > <a href ="#" > Tables</a > </li > <li class ="active" > Data tables</li > </ol > </section > <section class ="content" > <div class ="row" > <div class ="col-xs-12" > <div class ="box" > <div class ="box-header" > <h3 class ="box-title" > List All Page</h3 > </div > <div class ="box-body" > <table id ="example1" class ="table table-bordered table-striped" > <thead > <tr > <th > BNO</th > <th > TITLE</th > <th > WRITER</th > <th > REGDATE</th > <th > VIEWCNT</th > </tr > </thead > <tbody > <tr th:each ="boardVO : ${list}" > <td th:text ="${boardVO.bno}" > BNO</td > <td > <a th:href ="@{/samplehome/board/read(bno=${boardVO.bno})}" th:text ="${boardVO.title}" > TITLE</a > </td > <td th:text ="${boardVO.writer}" > WRITER</td > <td th:text ="${#dates.format(boardVO.regdate, 'yyyy-MM-dd HH:mm')}" > REGDATE</td > <td th:text ="${boardVO.viewcnt}" > VIEWCNT</td > </tr > </tbody > <tfoot > <tr > <th > BNO</th > <th > TITLE</th > <th > WRITER</th > <th > REGDATE</th > <th > VIEWCNT</th > </tr > </tfoot > </table > </div > </div > </div > </div > </section > </div > </div > </html >
위 html은 부트스트랩 테마때문에 좀 길긴 하다. 중요한 부분을 추리면 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 <tbody > <tr th:each ="boardVO : ${list}" > <td th:text ="${boardVO.bno}" > BNO</td > <td > <a th:href ="@{/samplehome/board/read(bno=${boardVO.bno})}" th:text ="${boardVO.title}" > TITLE</a > </td > <td th:text ="${boardVO.writer}" > WRITER</td > <td th:text ="${#dates.format(boardVO.regdate, 'yyyy-MM-dd HH:mm')}" > REGDATE</td > <td th:text ="${boardVO.viewcnt}" > VIEWCNT</td > </tr > </tbody >
게시판 글 한개 조회화면 게시판 글 리스트에서 특정 글을 선택했을때 나오는 화면.
1.controller
1 2 3 4 5 6 7 8 @RequestMapping (value = "/read" , method = RequestMethod.GET)public String read (@RequestParam("bno" ) int bno, Model model) throws Exception { logger.info("sampleboard read bno:" + bno); model.addAttribute("boardVO" , service.read(bno)); return "/samples/board/read" ;}
특정 게시글 하나를 db에서 조회하여 model에 boardVO라는 이름으로 담아 넘긴다.
2.read.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" xmlns:layout ="http://www.ultraq.net.nz/thymeleaf/layout" data-layout-decorate ="~{samples/layout/sampleLayout}" > <th:block layout:fragment ="css" > </th:block > <th:block layout:fragment ="script" > <script th:inline ="javascript" > $(function ( ) { var formObj = $("form[role='form']" ); console .log(formObj); $(".btn-warning" ).on("click" , function ( ) { formObj.attr("action" , "/samplehome/board/modify" ); formObj.attr("method" , "get" ); formObj.submit(); }); $(".btn-danger" ).on("click" , function ( ) { formObj.attr("action" , "/samplehome/board/remove" ); formObj.submit(); }); $(".btn-primary" ).on("click" , function ( ) { formObj.attr("action" , "/samplehome/board/listAll" ); formObj.attr("method" , "get" ); formObj.submit(); }); }); </script > </th:block > <div layout:fragment ="content" > <div class ="content-wrapper" > <section class ="content-header" > <h1 > 게시판 읽기 <small > Optional description</small > </h1 > <ol class ="breadcrumb" > <li > <a href ="#" > <i class ="fa fa-dashboard" > </i > 게시판</a > </li > <li class ="active" > read</li > </ol > </section > <section class ="content container-fluid" > <div class ="box box-primary" > <div class ="box-header with-border" > <h3 class ="box-title" > READ BOARD</h3 > </div > <form role ="form" method ="post" > <div class ="box-body" th:object ="${boardVO}" > <input th:field ="*{bno}" type ="hidden" > <div class ="form-group" > <label for ="exampleInputBoardTitle" > Title</label > <input th:value ="*{title}" type ="text" class ="form-control" id ="exampleInputBoardTitle" readonly ="readonly" > </div > <div class ="form-group" > <label > Content</label > <textarea th:inline ="text" class ="form-control" rows ="3" readonly ="readonly" > [[*{content}]]</textarea > </div > <div class ="form-group" > <label for ="exampleInputBoardWriter" > Writer</label > <input th:value ="*{writer}" type ="text" class ="form-control" id ="exampleInputBoardWriter" readonly ="readonly" > </div > </div > <div class ="box-footer" > <button type ="submit" class ="btn btn-warning" > Modify</button > <button type ="submit" class ="btn btn-danger" > Remove</button > <button type ="submit" class ="btn btn-primary" > ListAll</button > </div > </form > </div > </section > </div > </div > </html >
역시 부트스트랩 테마 때문에 좀 길다. 데이터가 맵핑되는 부분을 추려보겠다. form 태그 내부를 보면 아래처럼 controller으로 부터 전달되는 데이터를 받는 부분이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div class ="box-body" th:object ="${boardVO}" > <input th:field ="*{bno}" type ="hidden" > <div class ="form-group" > <label for ="exampleInputBoardTitle" > Title</label > <input th:value ="*{title}" type ="text" class ="form-control" id ="exampleInputBoardTitle" readonly ="readonly" > </div > <div class ="form-group" > <label > Content</label > <textarea th:inline ="text" class ="form-control" rows ="3" readonly ="readonly" > [[*{content}]]</textarea > </div > <div class ="form-group" > <label for ="exampleInputBoardWriter" > Writer</label > <input th:value ="*{writer}" type ="text" class ="form-control" id ="exampleInputBoardWriter" readonly ="readonly" > </div > </div >
게시글 수정 화면
1.controller
게시글 수정화면 (GET)이동 함수와, 실제 게시글 수정(POST)을 해주는 함수이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RequestMapping (value = "/modify" , method = RequestMethod.GET)public String modifyGET (@RequestParam("bno" ) int bno, Model model) throws Exception { logger.info("sampleboard read bno:" + bno); model.addAttribute("boardVO" , service.read(bno)); return "/samples/board/modify" ;} @RequestMapping (value = "/modify" , method = RequestMethod.POST)public String modifyPOST (BoardVO board, RedirectAttributes rttr) throws Exception {logger.info("modify post......" ); logger.info(board.toString()); service.modify(board); rttr.addFlashAttribute("msg" , "success" ); return "redirect:/samplehome/board/listAll" ;}
2.modify.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" xmlns:layout ="http://www.ultraq.net.nz/thymeleaf/layout" data-layout-decorate ="~{samples/layout/sampleLayout}" > <th:block layout:fragment ="css" > </th:block > <th:block layout:fragment ="script" > <script th:inline ="javascript" > $(function ( ) { var formObj = $("form[role='form']" ); console .log(formObj); $(".btn-primary" ).on("click" , function ( ) { formObj.attr("action" , "/samplehome/board/modify" ); formObj.attr("method" , "post" ); formObj.submit(); }); $(".btn-warning" ).on("click" , function ( ) { formObj.attr("action" , "/samplehome/board/listAll" ); formObj.attr("method" , "get" ); formObj.submit(); }); }); </script > </th:block > <div layout:fragment ="content" > <div class ="content-wrapper" > <section class ="content-header" > <h1 > 게시판 수정 <small > Optional description</small > </h1 > <ol class ="breadcrumb" > <li > <a href ="#" > <i class ="fa fa-dashboard" > </i > 게시판</a > </li > <li class ="active" > modify</li > </ol > </section > <section class ="content container-fluid" > <div class ="box box-primary" > <div class ="box-header with-border" > <h3 class ="box-title" > MODIFY BOARD</h3 > </div > <form role ="form" method ="post" > <div class ="box-body" th:object ="${boardVO}" > <input th:field ="*{bno}" type ="hidden" > <div class ="form-group" > <label for ="exampleInputBoardTitle" > Title</label > <input th:field ="*{title}" type ="text" class ="form-control" id ="exampleInputBoardTitle" > </div > <div class ="form-group" > <label > Content</label > <textarea th:field ="*{content}" class ="form-control" rows ="3" > </textarea > </div > <div class ="form-group" > <label for ="exampleInputBoardWriter" > Writer</label > <input th:field ="*{writer}" type ="text" class ="form-control" id ="exampleInputBoardWriter" > </div > </div > <div class ="box-footer" > <button type ="submit" class ="btn btn-primary" > Submit</button > <button type ="submit" class ="btn btn-warning" > Cancle</button > </div > </form > </div > </section > </div > </div > </html >
게시글 수정화면은 read와 달리 모든 값을 다 서버로 전달해야 하므로 th:field로 모든 값을 처리한 것을 확인 할수 있다.
마무리 정리 일단 html 태그에 name 어트리뷰트가 불 필요 하다. name 어트리뷰트를 사용할수 있지만, 햇갈리니 안쓰는게 좋아보인다. 서버에서 단순히 thymeleaf 뷰 html 파일에 값을 전달할때 th:value, th:field를 사용 할수 있다.name 어트리뷰트가 없는경우 form 액션발생시 th:value는 컨트롤러에 값을 전달하지 않지만, th:field는 자동으로 값을 컨트롤러에 전달한다.