1.  컨트롤러와 화면 처리

프로젝트 controller 패키지에 BoardController 구성

 

    🚀  가장 우선적으로 구현해야 하는 기능은 목록 기능이므로 list() 메서드를 추가하고 PageRequestDTO를 이용해서 페이징 처리와 검색에 이용

@Controller
@RequestMapping("/board")
@Log4j2
@RequiredArgsConstructor
public class BoardController {
    private final BoardService boardService;

    @GetMapping("/list")
    public void list(PageRequestDTO pageRequestDTO, Model model) { }
}

 


1)  화면 구성을 위한 준비

화면 구성은 Thymeleaf를 이용해서 레이아웃을 적용할 수 있도록 준비. build.gradle 파일에 Thymeleaf의 레이아웃 관련 라이브러리의 존재 여부를 먼저 확인

implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.1.0'

 


2)  템플릿 디자인 적용

Thymeleaf에는 레이아웃 기능을 적용할 수 있어서 본격적인 디자인을 적용. 부트스트랩의 여러 무료 디자인 템플릿 중 'Simple Sidebar'를 이용

https://startbootstrap.com/template/simple-sidebar

 

Start Bootstrap

 

startbootstrap.com

 

  • 무료로 이용할 수 있으므로 내려받은 후에 압축을 해제하고 모든 파일을 resources 의 static 폴더에 복사
  • 프로젝트를 실행하고 브라우저에 'http://localhost:8080/'를 실행했을 때 index.html 파일이 실행되는지 확인
  • 적용한 템플릿이 반응형이라 브라우저의 크기에 따라 다르게 보임
Thymeleaf 레이아웃으로 변경하기

 

    ✓  레이아웃을 적용하려면 layout 폴더에 basic.html을 추가하고 index.html 내용을 그대로 복사해서 추가

 

레이아웃 적용

 

    ✓  basic.html의 상단에는 Thymeleaf의 네임스페이스들을 추가

<html lang="en"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org">

 

    ✓  <head> 태그에는 링크가 존재하므로 이를 Thymeleaf 스타일로 변경. 경로를 '@{/..}'와 같은 형태로 처리. ('/'로 경로가 시작하는 것을 주의)

    <link rel="icon" type="image/x-icon" th:href="@{/assets/favicon.ico}" />
    <!-- Core theme CSS (includes Bootstrap)-->
    <link th:href="@{/css/styles.css}" rel="stylesheet" />
</head>

 

    ✓  'Page content' 부분에 layout:fragment를 적용

<!-- Page content-->
<div class="container-fluid" layout:fragment="content">

 

    ✓  파일의 마지막 부분에 자바 스크립트를 위한 <th:block>을 추가하고 링크를 수정

<script th:src="@{/js/scripts.js}"></script>
    <th:block layout:fragment="script">
    
    </th:block>

 

컨트롤러를 통한 확인

 

    ✓  BoardController에는 list() 기능을 작성해 두었으므로 이를 활용해서 레이아웃까지 적용된 화면을 구성
    ✓  templates 폴더에 board 폴더를 생성하고 list.html을 추가

    ✓  list.html은 레이아웃 적용여부를 확인할 수 있도록 작성.

<html lang="en"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org"
      layout:decorate="~{layout/basic.html}">

<div layout:fragment="content">
    <h1>Board List</h1>
</div>

<script layout:fragment="script" th:inline="javascript">
    console.log('script...');
</script>

   


2.  목록 화면 개발

1)  BoardController

  🚀  BoardController의 list()에 화면에 목록 데이터를 출력하는 코드 작성

  🚀  list()가 실행되면 PageRequestDTO와 PageResponseDTO 객체가 화면으로 전달

@GetMapping("/list")
public void list(PageRequestDTO pageRequestDTO, Model model) {
    PageResponseDTO<BoardDTO> responseDTO = boardService.list(pageRequestDTO);

    log.info(responseDTO);

    model.addAttribute("responseDTO", responseDTO);
}

 


2)  list.html 수정

  🚀  화면 상단에는 여러개의 <div>를 이용해서 화면 구성을 처리

  🚀  실제 내용물의 출력은 <tbody>를 이용하고 Thymeleaf의 반복문을 이용해서 처리

<div layout:fragment="content">
    <div clsss="col">
        <div class="card">
            <div class="card-header">
                Board List
            </div>
            <div class="card-body">
                <h5 class="card-title">Board List</h5>

                <table class="table">
                    <thead>
                    <tr>
                        <th scope="col">Bno</th>
                        <th scope="col">Title</th>
                        <th scope="col">Writer</th>
                        <th scope="col">RegDate</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr th:each="dto:${responseDTO.list}">
                        <th scope="col">[[${dto.bno}]]</th>
                        <td>[[${dto.title}]]</td>
                        <td>[[${dto.writer}]]</td>
                        <td>[[${dto.regDate}]]</td>
                    </tr>
                    </tbody>
                </table>
            </div><!-- end card body -->
        </div><!-- end card -->
    </div><!-- end col -->
</div><!-- end row -->

 


3)  날짜 포멧팅 처리

  🚀  화면의 등록일 regDate 이 너무 길고 상세하게 나오는 것을 날짜만 나오도록 처리
  🚀  Thymeleaf의 #temporals라는 유틸리티 객체를 이용해서 처리

<td>[[${#temporals.format(dto.regDate, 'yyyy-MM-dd')}]]</td>


4)  페이지 목록의 출력

  🚀  <table> 태그가 끝나는 부분과 이어지게 <div>를 구성해서 페이지 번호들을 화면에 출력
  🚀  PageResponseDTO는 시작 번호 start와 끝 번호 end 만을 가지고 있으므로 특정 범위의 숫자를 만들기 위해서 Thymeleaf의 numbers를 이용

<div class="float-end">
    <ul class="pagination flex-wrap">
        <li class="page-item" th:if="${responseDTO.prev}">
            <a class="page-link" th:data-num="${responseDTO.start -1}">Previous</a>
        </li>
        <th:block th:each="i: ${#numbers.sequence(responseDTO.start,responseDTO.end)}">
            <li th:class="${responseDTO.page == i} ? 'page-item active' : 'page-item'">
                <a class="page-link" th:data-num="${i}">[[${i}]]</a>
            </li>
        </th:block>
        <li class="page-item" th:if="${responseDTO.next}">
            <a class="page-link" th:data-num="${responseDTO.end + 1}">Next</a>
        </li>
    </ul>
</div>

 

  📍  코드에서 중요한 부분은 #numbers.sequence()로 특정한 범위의 연속된 숫자를 만드는 부분과 <a> 태그에 'data-num'이라는 속성으로 페이지 처리하는 부분

  📍  브라우저에서 직접 URL을 변경하는 방법으로 '/board/list?page=2'와 같이 페이지 번호를 쿼리 스트링으로 추가해서 페이지 변경되는 것을 확인

 


5) 검색 화면 추가

  🚀  list.html 페이지에는 검색이 이루어질수 있도록 <table> 위에 별도의 card 영역을 만들어 검색 조건을 선택할 수 있도록 구성.

  🚀  검색 조건은 페이지 이동과 함께 처리될 수 있도록 <form> 태그로 감싸서 처리

<div class="row mt-3">
    <form action="/board/list" method="get">
        <div class="col">
            <input type="hidden" name="size" th:value="${pageRequestDTO.size}">
            <div class="input-group">
                <div class="input-group-prepend">
                    <select class="form-select" name="type">
                        <option value="">---</option>
                        <option value="t" th:selected="${pageRequestDTO.type =='t'}">제목</option>
                        <option value="c" th:selected="${pageRequestDTO.type =='c'}">내용</option>
                        <option value="w" th:selected="${pageRequestDTO.type =='w'}">작성자</option>
                        <option value="tc" th:selected="${pageRequestDTO.type =='tc'}">제목 내용</option>
                        <option value="tcw" th:selected="${pageRequestDTO.type =='tcw'}">제목 내용 작성자</option>
                    </select>
                </div>
                <input type="text" class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
                <div class="input-group-append">
                    <button class="btn btn-outline-secondary searchBtn" type="submit">Search</button>
                    <button class="btn btn-outline-secondary clearBtn" type="button">Clear</button>
                </div>
            </div>
        </div>
    </form>
</div>

 

  📍  브라우저에 /board/list 뒤에 type과 keyword를 지정하면 화면에서 검색 항목과 키워드 부분으로 처리 되는것 확인

 


6)  이벤트 처리

  🚀  페이지 번호를 클릭하거나 검색/필터링 조건을 눌렀을 때 이벤트 처리를 추가

  • 페이지 번호를 클릭하면 검색 창에 있는 <form> 태그에 <input type='hidden'>으로 page를 추가한 후에 submit
  • Clear 버튼을 누르면 검색 조건 없이 /board/list 호출

  🚀  JSP에서 자바스크립트의 문자열을 템플릿으로 적용하고자 EL과 구분하기 위해 \${} 와 같은 방식을 사용했지만, Thymeleaf는 \ 없이 적용한다는 점을 제외하면 기존 코드를 그대로 사용

list.html의 마지막 부분에 <script> 영역을 작업
<script layout:fragment="script" th:inline="javascript">
    document.querySelector(".pagination").addEventListener("click", function(e) {
        e.preventDefault(); // a 태그일 경우 기본 이벤트 제거
        e.stopPropagation();

        const target = e.target;

        if(target.tagName !== 'A') { // a 태그가 아니면 종료
            return;
        }

        const num = target.getAttribute("data-num"); // 클릭한 a 태그의 data-num 속성(이동해야할 페이지번호)를 가져옴.

        const formObj = document.querySelector("form");

        formObj.innerHTML += `<input type='hidden' name='page' value='${num}'>`; // form 태그에 page 태그 추가

        formObj.submit();

    }, false)

    document.querySelector('.clearBtn').addEventListener("click", function(e){
        e.preventDefault();
        e.stopPropagation();

        self.location = '/board/list';

    }, false)
</script>

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts