목차
1. 사전 기반 지식
- 부트스트랩의 그리드 시스템:
- 개념: 부트스트랩은 화면을 12개의 컬럼으로 나누어 레이아웃을 구성할 수 있도록 돕는 그리드 시스템을 제공한다. col-sm-8은 작은 화면에서 8개의 컬럼을 차지하는 레이아웃을 의미한다.
- 사용 방법: col-sm-8, col-md-6 등의 클래스를 사용해 반응형 레이아웃을 쉽게 구성할 수 있다.
- 예제: col-sm-8은 12개의 그리드 중 8개를 차지하며, 이는 전체 화면의 약 66.67%이다.
- 플렉스박스(Flexbox)와 중앙 정렬:
- 개념: 플렉스박스는 CSS의 레이아웃 모델로, 요소를 쉽게 정렬하고 배치하는 데 사용된다. 부트스트랩의 d-flex와 justify-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"%>
'Spring Boot > Bank App 만들기 (deployment)' 카테고리의 다른 글
27. 사용자 비밀번호 암호화 처리 (0) | 2024.08.13 |
---|---|
26. intercepter 활용(인증검사 공통 처리) (0) | 2024.08.13 |
24. 간단한 유틸 클래스 만들어 보기 (0) | 2024.08.12 |
23. 계좌 상세보기 - 2단계(기능, 동적쿼리 구현) (0) | 2024.08.12 |
22. 계좌 상세보기 - 1단계(쿼리 학습) (0) | 2024.08.09 |