25. 계좌 상세보기 페이징 처리

 

목차

    1. 사전 기반 지식

    • 부트스트랩의 그리드 시스템:
      • 개념: 부트스트랩은 화면을 12개의 컬럼으로 나누어 레이아웃을 구성할 수 있도록 돕는 그리드 시스템을 제공한다. col-sm-8은 작은 화면에서 8개의 컬럼을 차지하는 레이아웃을 의미한다.
      • 사용 방법: col-sm-8, col-md-6 등의 클래스를 사용해 반응형 레이아웃을 쉽게 구성할 수 있다.
      • 예제: col-sm-8은 12개의 그리드 중 8개를 차지하며, 이는 전체 화면의 약 66.67%이다.
    • 플렉스박스(Flexbox)와 중앙 정렬:
      • 개념: 플렉스박스는 CSS의 레이아웃 모델로, 요소를 쉽게 정렬하고 배치하는 데 사용된다. 부트스트랩의 d-flexjustify-content-center는 플렉스박스를 활용해 자식 요소를 수평 중앙에 정렬하는 데 사용된다.
      • 사용 방법: d-flex를 부모 요소에 적용하고, justify-content-center를 추가하여 자식 요소를 중앙에 배치한다.
    • 페이징(Pagination) 구현:
      • 개념: 페이징은 많은 양의 데이터를 여러 페이지로 나누어 보여주는 기법이다. 사용자가 한 페이지에 표시할 데이터의 수를 지정하고, 나머지 데이터는 다음 페이지로 넘긴다.
      • 사용 방법: 현재 페이지 번호(currentPage)와 전체 페이지 수(totalPages)를 기반으로 페이징 링크를 생성한다. 부트스트랩의 pagination 클래스를 사용해 시각적으로 구성한다.

    2. 구현

    AccountController
    /**
     * 계좌 상세 보기 페이지 주소 설계 : localhost:8080/account/detail/1?type=all, deposit, withdrawal
     * 
     * @return
     */
    @GetMapping("/detail/{accountId}")
    public String detail(@PathVariable(name = "accountId") Integer accountId, //
            @RequestParam(required = false, name = "type") String type,
            @RequestParam(defaultValue = "1", name = "page") int page,
            Model model) {
        User principal = (User) session.getAttribute(Define.PRINCIPAL);
        if (principal == null) {
            throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
        }
        // 유효성 검사
        List<String> validTypes = Arrays.asList("all", "deposit", "withdrawal");
        if (!validTypes.contains(type)) {
            throw new DataDeliveryException("유효하지 않은 접근 입니다.", HttpStatus.BAD_REQUEST);
        }
        Account account = accountService.readAccountById(accountId);
        int pageSize = 2; // 한페이지에 2개
        int offset = (page - 1) * pageSize;
        int totalHistories = accountService.countHistoryByAccountIdAndType(type, accountId);
        int totalPage = (int) Math.ceil((double) totalHistories / pageSize);
        int pageBlock = 5;
        int tenCount = (int) Math.ceil(((double) page / pageBlock) - 1) * pageBlock;
        int startPage = tenCount + 1;
        int endPage = (tenCount + 5) > totalPage ? totalPage : (tenCount + pageBlock);
        List<HistoryAccount> historyList = accountService.readHistoryByAccountId(type, accountId, offset, pageSize);
        model.addAttribute("type", type);
        model.addAttribute("currentPage", page);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);
        model.addAttribute("totalPage", totalPage);
        model.addAttribute("account", account);
        model.addAttribute("historyList", historyList);
        return "account/detail";
    }
    AccountService
    public List<HistoryAccount> readHistoryByAccountId(String type, Integer accountId, Integer offset, Integer limit){
        List<HistoryAccount> list = null;
        list = historyRepository.findByAccountIdAndTypeOfHistory(type, accountId, offset, limit);
        return list;
    }
    history.xml - 쿼리 수정 및 추가
    <select id="findByAccountIdAndTypeOfHistory" resultType="com.tenco.bank.repository.model.HistoryAccount">
        <if test="type == 'all'">
            SELECT h.id, h.amount,
            CASE
            WHEN h.d_account_id = #{accountId} THEN h.d_balance
            WHEN h.w_account_id = #{accountId} THEN h.w_balance
            END AS balance,
            COALESCE(CAST(wa.number AS CHAR(10)), 'ATM') AS sender,
            COALESCE(CAST(da.number AS CHAR(10)), 'ATM') AS receiver,
            h.created_at
            FROM history_tb h
            LEFT JOIN account_tb da ON
            h.d_account_id = da.id
            LEFT JOIN account_tb wa ON h.w_account_id = wa.id
            WHERE h.d_account_id = #{accountId}
            OR h.w_account_id = #{accountId}
            ORDER BY h.created_at DESC
            LIMIT
            #{limit} OFFSET #{offset}
        </if>
        <if test="type == 'deposit'">
            SELECT h.id, h.amount, h.d_balance AS balance,
            COALESCE(CAST(wa.number AS CHAR(10)), 'ATM') AS sender,
            da.number AS receiver,
            h.created_at
            FROM history_tb AS h
            LEFT JOIN
            account_tb da ON h.d_account_id = da.id
            LEFT JOIN account_tb wa ON h.w_account_id = wa.id
            WHERE h.d_account_id = #{accountId}
            ORDER BY h.created_at DESC
            LIMIT #{limit} OFFSET
            #{offset}
        </if>
        <if test="type == 'withdrawal'">
            SELECT h.id, h.amount, h.w_balance AS balance,
            wa.number AS sender,
            COALESCE(CAST(da.number AS CHAR(10)), 'ATM') AS receiver,
            h.created_at
            FROM history_tb AS h
            LEFT JOIN
            account_tb da ON h.d_account_id = da.id
            LEFT JOIN account_tb wa ON h.w_account_id = wa.id
            WHERE h.w_account_id = #{accountId}
            ORDER BY h.created_at DESC
            LIMIT #{limit} OFFSET
            #{offset}
        </if>
    </select>
    <select id="countHistoryByAccountIdAndType" resultType="int">
        <if test="type == 'all'">
            SELECT count(*)
            FROM history_tb
            WHERE d_account_id = #{accountId}
            OR w_account_id = #{accountId}
        </if>
        <if test="type == 'deposit'">
            SELECT count(*)
            FROM history_tb
            WHERE d_account_id =
            #{accountId}
        </if>
        <if test="type == 'withdrawal'">
            SELECT count(*)
            FROM history_tb
            WHERE w_account_id =
            #{accountId}
        </if>
    </select>
    detail.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!-- header.jsp -->
    <%@ include file="/WEB-INF/view/layout/header.jsp"%>
    
    <!-- start of context.jsp(xxx.jsp) -->
    <div class="col-sm-8">
    	<h2>계좌 상세 보기(인증)</h2>
    	<h5>Bank App에 오신걸 환영합니다.</h5>
    
    	<div class="bg-light p-md-5">
    		<div class="user--box">
    			${principal.username}님 계좌<br> 계좌번호 : ${account.number}<br> 잔액 : ${account.formatKoreanWon(account.balance)}
    		</div>
    		<br>
    		<div>
    			<a href="?type=all" class="btn btn-outline-primary">전체</a> <a href="?type=deposit" class="btn btn-outline-primary">입금</a> <a href="?type=withdrawal"
    				class="btn btn-outline-primary">출금</a>
    		</div>
    		<br>
    		<table class="table table-striped">
    			<thead>
    				<tr>
    					<th>날짜</th>
    					<th>보낸이</th>
    					<th>받은이</th>
    					<th>입출금 금액</th>
    					<th>계좌잔액</th>
    				</tr>
    			</thead>
    			<tbody>
    				<c:forEach items="${historyList}" var="historyAccount">
    					<tr>
    						<td>${historyAccount.timestampToString(historyAccount.createdAt)}</td>
    						<td>${historyAccount.sender}</td>
    						<td>${historyAccount.receiver}</td>
    						<td>${historyAccount.formatKoreanWon(historyAccount.amount)}</td>
    						<td>${historyAccount.formatKoreanWon(historyAccount.balance)}</td>
    					</tr>
    				</c:forEach>
    			</tbody>
    		</table>
    		<br>
    		<div class="d-flex justify-content-center">
    			<ul class="pagination">
    				<li class="page-item ${currentPage == 1 ? 'disabled' : ''}"><a class="page-link" href="?type=${type}&page=1">First</a></li>
    				<li class="page-item ${currentPage == 1 ? 'disabled' : ''}"><a class="page-link" href="?type=${type}&page=${currentPage - 1}">Previous</a></li>
    				<c:forEach begin="${startPage}" end="${endPage}" var="page">
    					<li class="page-item ${page == currentPage ? 'active' : ''}"><a class="page-link" href="?type=${type}&page=${page}">${page}</a></li>
    				</c:forEach>
    				<li class="page-item ${currentPage == totalPage ? 'disabled' : ''}"><a class="page-link" href="?type=${type}&page=${currentPage + 1}">Next</a></li>
    				<li class="page-item ${currentPage == totalPage ? 'disabled' : ''}"><a class="page-link" href="?type=${type}&page=${totalPage}">End</a></li>
    			</ul>
    		</div>
    	</div>
    
    </div>
    <!-- end of col-sm-8 -->
    </div>
    </div>
    <!-- end of context.jsp(xxx.jsp) -->
    
    <!-- footer.jsp -->
    <%@ include file="/WEB-INF/view/layout/footer.jsp"%>