Spring boot

2024.08.12 Bank App ๋งŒ๋“ค๊ธฐ ๊ณ„์ขŒ ์ƒ์„ธ๋ณด๊ธฐ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ -25

์ •ํ›ˆ5 2024. 8. 12. 17:07

 

 ๐Ÿ’ก ํ•™์Šต ๋ชฉํ‘œ

  • CSS์™€ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ํ™œ์šฉํ•œ ์ค‘์•™ ์ •๋ ฌ
    ํ”Œ๋ ‰์Šค๋ฐ•์Šค(d-flex)์™€ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์š”์†Œ๋ฅผ ์ค‘์•™์— ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•.

  • JSP์—์„œ ๋™์  ์ฝ˜ํ…์ธ  ์ฒ˜๋ฆฌ
    JSP์—์„œ ๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ํŽ˜์ด์ง•(Pagination) ๊ตฌํ˜„ 

 

 

์‚ฌ์ „ ๊ธฐ๋ฐ˜ ์ง€์‹

  • ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์˜ ๊ทธ๋ฆฌ๋“œ ์‹œ์Šคํ…œ:
    • ๊ฐœ๋…
      ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์€ ํ™”๋ฉด์„ 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 ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์‹œ๊ฐ์ ์œผ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

detail.jsp

๋”๋ณด๊ธฐ

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 content.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="/account/detail/${account.id}?type=all" class="btn btn-outline-primary" >์ „์ฒด</a>&nbsp;
			<a href="/account/detail/${account.id}?type=deposit" class="btn btn-outline-primary" >์ž…๊ธˆ</a>&nbsp;
			<a href="/account/detail/${account.id}?type=withdrawal" class="btn btn-outline-primary" >์ถœ๊ธˆ</a>&nbsp;
		</div>
		<br>
		<table class="table table-striped">
			<thead>
				<tr>
					<th>๋‚ ์งœ</th>
					<th>๋ณด๋‚ธ์ด</th>
					<th>๋ฐ›์€์ด</th>
					<th>์ž…์ถœ๊ธˆ ๊ธˆ์•ก</th>
					<th>๊ณ„์ขŒ์ž”์•ก</th>
				</tr>
			</thead>
			<tbody>
				<c:forEach var="historyAccount" items="${historyList}">
				<tr>
					<th>${historyAccount.timestampToString(historyAccount.createdAt)}</th>
					<th>${historyAccount.sender}</th>
					<th>${historyAccount.receiver}</th>
					<th>${historyAccount.formatKoreanWon(historyAccount.amount)}</th>
					<th>${historyAccount.formatKoreanWon(historyAccount.balance)}</th>
				</tr>
				
				</c:forEach>
			</tbody>
		</table>
		<br>
		<!-- Pagination -->
		<div class="d-flex justify-content-center" >
			<ul class="pagination">
				<!-- Previous Page Link -->
				<li class="page-item <c:if test='${currentPage == 1}'>disabled</c:if>">
					<a class="page-link" href="?type=${type}&page=${currentPage - 1}&size=${size}" >Previous</a>
				</li>
				
				<!-- Page Numbers -->
				<!-- [Previous]  1 2 3 4 5 6 7 8   [Next] -->
				<c:forEach begin="1" end="${totalPages}"  var="page" >
				<li class="page-item  <c:if test='${page == currentPage}'>active </c:if>">
					<a class="page-link"  href="?type=${type}&page=${page}&size=${size}" >${page}</a>
				</li>
				</c:forEach>
				
				<!-- Next Page Link  -->	
				<li class="page-item <c:if test='${currentPage == totalPages}'>disabled</c:if>" >
					<a class="page-link" href="?type=${type}&page=${currentPage + 1}&size=${size}" >Next</a>
				</li>
			</ul>
			
		</div>
	</div>
	
</div>
<!-- end of col-sm-8  -->
</div>
</div>
<!-- end of content.jsp(xxx.jsp)   -->

<!-- footer.jsp  -->
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>

 

AccountController - ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

๋”๋ณด๊ธฐ

AccountController - ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

 

 ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

/**
	 * ๊ณ„์ขŒ ์ƒ์„ธ ๋ณด๊ธฐ ํŽ˜์ด์ง€ 
	 * ๋™์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค.
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=all
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=deposit
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=withdraw 
	 * @return
	 */
	@GetMapping("/detail/{accountId}")
	public String detail(@PathVariable(name = "accountId") Integer accountId,
						 @RequestParam (required = false, name = "type") String type,
						 @RequestParam (name = "page", defaultValue = "1") int page,
						 @RequestParam (name = "size", defaultValue = "2") int size,
						 Model model)  {
		
		// localhost:8080/account/detail/${4}
		System.out.println("@PathVariable : " + accountId);
		//  localhost:8080/account/detail/${account.id}?type=all
		System.out.println("@RequestParam : " + type);
		
		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);  // ๋‹ค์šด ์บ์ŠคํŒ…
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		
		// ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
		// array ์„ ์–ธ๊ณผ ๋™์‹œ์— ์ดˆ๊ธฐํ™” ํ•˜๋Š” ๋ฉ”์„œ๋“œ
		List<String> validTypes = Arrays.asList("all", "deposit", "withdrawal"); 
		
		if(!validTypes.contains(type)) {
			throw new DataDeliveryException("์œ ํšจํ•˜์ง€ ์•Š๋Š” ์ ‘๊ทผ ์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST);
		} 
		
		// ํŽ˜์ด์ง€ ๊ฐœ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ด ํŽ˜์ด์ง€์˜ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
		int totalRecords = accountService.countHistoryByAccountIdAndType(type, accountId);
		int totalPages = (int)Math.ceil( (double)totalRecords / size ); // ์˜ฌ๋ฆผ์—ฐ์‚ฐ ์†Œ์ˆ˜์  ์žˆ์œผ๋ฉด ๋ฌด์กฐ๊ฑด ์˜ฌ๋ฆผ
		
		Account account = accountService.readAccountById(accountId);
		List<HistoryAccount> historyList = accountService.readHistoryByAccountId(type, accountId, page, size);
	
	
		model.addAttribute("account", account);
		model.addAttribute("historyList", historyList);
		
		model.addAttribute("currentPage", page); // 1;
		model.addAttribute("totalPages", totalPages);
		model.addAttribute("type", type);
		model.addAttribute("size", size); // 2
		
		
		return "account/detail";
	}

 

 ์ „์ฒด์ฝ”๋“œ

package com.tenco.bank.controller;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.tenco.bank.dto.DepositDTO;
import com.tenco.bank.dto.SaveDTO;
import com.tenco.bank.dto.TransferDTO;
import com.tenco.bank.dto.WithdrawalDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.model.Account;
import com.tenco.bank.repository.model.HistoryAccount;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.AccountService;
import com.tenco.bank.utils.Define;

import jakarta.servlet.http.HttpSession;

@Controller // IOC ๋Œ€์ƒ (์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ)
@RequestMapping("/account")
public class AccountController {

	// ๊ณ„์ขŒ ์ƒ์„ฑ ํ™”๋ฉด ์š”์ฒญ - DI ์ฒ˜๋ฆฌ
	private final HttpSession session;
	private final AccountService accountService;

	@Autowired
	public AccountController(HttpSession session, AccountService accountService) {
		this.session = session; // ์˜์กด ์ฃผ์ž…
		this.accountService = accountService; // ์˜์กด ์ฃผ์ž…
	}

	/**
	 * ๊ณ„์ขŒ ์ƒ์„ฑ ํŽ˜์ด์ง€ ์š”์ฒญ ์ฃผ์†Œ์„ค๊ณ„ : http://localhost:8080/account/save :
	 * 
	 * @return
	 */
	@GetMapping("/save")
	public String savePage() {

		// 1. ์ธ์ฆ ๊ฒ€์‚ฌ๊ฐ€ ํ•„์š” (account ์ „์ฒด๊ฐ€ ํ•„์š”ํ•จ)
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		return "account/save";
	}

	/**
	 * ๊ณ„์ขŒ ์ƒ์„ฑ ๊ธฐ๋Šฅ ์š”์ฒญ ์ฃผ์†Œ ์„ค๊ณ„ : http://localhost:8080/account/save
	 * 
	 * @retrun : ์ถ”ํ›„ ๊ณ„์ขŒ ๋ชฉ๋ก ํŽ˜์ด์ง€ ์ด๋™ ์ฒ˜๋ฆฌ
	 */

	@PostMapping("/save")
	public String saveProc(SaveDTO dto) {
		// 1. form ๋ฐ์ดํ„ฐ ์ถ”์ถœ (ํŒŒ์‹ฑ ์ „๋žต)
		// 2. ์ธ์ฆ ๊ฒ€์‚ฌ
		// 3. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
		// 4. ์„œ๋น„์Šค ํ˜ธ์ถœ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);

		// ์ธ์ฆ ๊ฒ€์‚ฌ
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		if (dto.getNumber() == null || dto.getNumber().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
		}

		if (dto.getPassword() == null || dto.getPassword().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
		}

		if (dto.getBalance() == null || dto.getBalance() <= 0) {
			throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
		}

		// ์„œ๋น„์Šค์— dto์™€ ์œ ์ €์•„์ด๋””๋ฅผ ๋ณด๋‚ธ๋‹ค.
		accountService.createAccount(dto, principal.getId());

		return "redirect:/index";
	}

	/**
	 * ๊ณ„์ขŒ ๋ชฉ๋ก ํ™”๋ฉด ์š”์ฒญ ์ฃผ์†Œ ์„ค๊ณ„ : http://localhost:8080/account/list, ..../
	 * 
	 * @return list.jsp
	 */
	// ํŽ˜์ด์ง€ ๋ฆฌํ„ดํ•ด์•ผ ๋˜์„œ string์œผ๋กœ ์ง“๋Š”๋‹ค.
	@GetMapping({ "/list", "/" })
	public String listPage(Model model) {

		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL); // ์œ ์ €์„ธ์…˜ ๊ฐ€์ ธ์˜ด
		if (principal == null) {
			// ๋กœ๊ทธ์ธ์„ ์•ˆํ•œ ์ƒํƒœ
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		// 2. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

		// 3. ์„œ๋น„์Šค ํ˜ธ์ถœ (์„œ๋น„์Šค์ปจํŠธ๋กค๋Ÿฌ : ํ•ต์‹ฌ๊ธฐ๋Šฅ )
		List<Account> accountList = accountService.readAccountListByUserId(principal.getId());
		if (accountList.isEmpty()) {
			model.addAttribute("accountList", null);
		} else {
			model.addAttribute("accountList", accountList); // ๋ชจ๋ธ์—์„œ ํ‚ค,๊ฐ’์„ ๋˜์ ธ๋ฒ„๋ฆฐ๋‹ค.
		}

		// JSP ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ์ฃผ๋Š” ๋ฐฉ๋ฒ•
		return "account/list";

	}

	/**
	 * ์ถœ๊ธˆ ํŽ˜์ด์ง€ ์š”์ฒญ
	 * 
	 * @return withdrawal.jsp
	 */
	@GetMapping("/withdrawal")
	public String withdrawalPage() {
		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		return "account/withdrawal";
	}

	@PostMapping("/withdrawal")
	public String withdrawalProc(WithdrawalDTO dto) {

		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		// ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (์ž๋ฐ” ์ฝ”๋“œ๋ฅผ ๊ฐœ๋ฐœ) --> ์Šคํ”„๋ง ๋ถ€ํŠธ @Valid ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์กด์žฌ
		if (dto.getAmount() == null) {
			throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getAmount().longValue() <= 0) { // 0์› ์ถœ๊ธˆ ์š”์ฒญ ์‹œ
			throw new DataDeliveryException(Define.W_BALANCE_VALUE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getWAccountNumber() == null) {
			throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
		}

		if (dto.getWAccountPassword() == null || dto.getWAccountPassword().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
		}

		accountService.updateAccountWithdraw(dto, principal.getId());

		return "redirect:/account/list";

	}

	// ์ž…๊ธˆ ํŽ˜์ด์ง€ ์š”์ฒญ
	@GetMapping("/deposit")
	public String depositPage() {

		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		
		return "account/deposit";
	}

	/**
	 * ์ž…๊ธˆ ํŽ˜์ด์ง€ ์š”์ฒญ
	 * 
	 * @param dto
	 * @return
	 */
	@PostMapping("/deposit")
	public String depositProc(DepositDTO dto) {
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		if (dto.getAmount() == null) {
			throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getAmount().longValue() <= 0) {
			throw new DataDeliveryException(Define.D_BALANCE_VALUE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getDAccountNumber() == null || dto.getDAccountNumber().trim().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
		}

		accountService.updateAccountDeposit(dto, principal.getId());
		return "redirect:/account/list";
	}

	// ์ด์ฒด ํŽ˜์ด์ง€ ์š”์ฒญ
	/**
	 * ๊ณ„์ขŒ ์ด์ฒด ํ™”๋ฉด ์š”์ฒญ
	 * @return
	 */
	@GetMapping("/transfer")
	public String transfer() {

		User principal = (User) session.getAttribute(Define.PRINCIPAL);

		if (principal == null) {
			throw new DataDeliveryException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}

		return "account/transfer";
	}

	// ์ด์ฒด ๊ธฐ๋Šฅ ์ฒ˜๋ฆฌ ์š”์ฒญ
	@PostMapping("/transfer")
	public String transferProc(TransferDTO dto) {
		
	// 1. ์ธ์ฆ ๊ฒ€์‚ฌ
	User principal =(User)session.getAttribute(Define.PRINCIPAL); 
		
	// 2. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
	if(dto.getAmount() == null) { // ์ถœ๊ธˆํ•˜๋Š” ๊ธˆ์•ก์ด ๊ณต๋ฐฑ์ด๋ฉด ์•ˆ๋œ๋‹ค.
		throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
	}
	
	if(dto.getAmount().longValue() <= 0) { // ์ถœ๊ธˆํ•˜๋Š” ๊ธˆ์•ก์ด 0 ์ดํ•˜์ด๋ฉด ์•ˆ๋œ๋‹ค.
		throw new DataDeliveryException(Define.D_BALANCE_VALUE, HttpStatus.BAD_REQUEST);
	}
	
	if(dto.getWAccountNumber() == null || dto.getWAccountNumber().trim().isEmpty()) {
		// ๊ณ„์ขŒ๋ฒˆํ˜ธ๊ฐ€ null ์ด๊ฑฐ๋‚˜ ๊ณต๋ฐฑ์ด๋ฉด ์•ˆ๋œ๋‹ค.
		throw new DataDeliveryException("์ถœ๊ธˆํ•˜์‹ค ๊ณ„์ขŒ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", HttpStatus.BAD_REQUEST);
	}
	
	if(dto.getDAccountNumber() == null || dto.getDAccountNumber().trim().isEmpty()) {
		// ์ž…๊ธˆ ๊ธˆ์•ก์ด null ์ด๊ฑฐ๋‚˜ ๊ณต๋ฐฑ์ด๋ฉด ์•ˆ๋œ๋‹ค.
		throw new DataDeliveryException("์ด์ฒดํ•˜์‹ค ๊ณ„์ขŒ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”", HttpStatus.BAD_REQUEST);
	}
	
	if(dto.getPassword() == null || dto.getPassword().trim().isEmpty()) {
		throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
	}
	
	// ์„œ๋น„์Šค ํ˜ธ์ถœ
	accountService.updateAccountTransfer(dto, principal.getId());
		
		return "redirect:/account/list";
		
	}
	
	/**
	 * ๊ณ„์ขŒ ์ƒ์„ธ ๋ณด๊ธฐ ํŽ˜์ด์ง€ 
	 * ๋™์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค.
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=all
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=deposit
	 * ์ฃผ์†Œ ์„ค๊ณ„ : localhost:8080/account/detail/${account.id}?type=withdraw 
	 * @return
	 */
	@GetMapping("/detail/{accountId}")
	public String detail(@PathVariable(name = "accountId") Integer accountId,
						 @RequestParam (required = false, name = "type") String type,
						 @RequestParam (name = "page", defaultValue = "1") int page,
						 @RequestParam (name = "size", defaultValue = "2") int size,
						 Model model)  {
		
		// localhost:8080/account/detail/${4}
		System.out.println("@PathVariable : " + accountId);
		//  localhost:8080/account/detail/${account.id}?type=all
		System.out.println("@RequestParam : " + type);
		
		// 1. ์ธ์ฆ๊ฒ€์‚ฌ
		User principal = (User) session.getAttribute(Define.PRINCIPAL);  // ๋‹ค์šด ์บ์ŠคํŒ…
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		
		// ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
		// array ์„ ์–ธ๊ณผ ๋™์‹œ์— ์ดˆ๊ธฐํ™” ํ•˜๋Š” ๋ฉ”์„œ๋“œ
		List<String> validTypes = Arrays.asList("all", "deposit", "withdrawal"); 
		
		if(!validTypes.contains(type)) {
			throw new DataDeliveryException("์œ ํšจํ•˜์ง€ ์•Š๋Š” ์ ‘๊ทผ ์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST);
		} 
		
		// ํŽ˜์ด์ง€ ๊ฐœ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ด ํŽ˜์ด์ง€์˜ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
		int totalRecords = accountService.countHistoryByAccountIdAndType(type, accountId);
		int totalPages = (int)Math.ceil( (double)totalRecords / size ); // ์˜ฌ๋ฆผ์—ฐ์‚ฐ ์†Œ์ˆ˜์  ์žˆ์œผ๋ฉด ๋ฌด์กฐ๊ฑด ์˜ฌ๋ฆผ
		
		Account account = accountService.readAccountById(accountId);
		List<HistoryAccount> historyList = accountService.readHistoryByAccountId(type, accountId, page, size);
	
	
		model.addAttribute("account", account);
		model.addAttribute("historyList", historyList);
		
		model.addAttribute("currentPage", page); // 1;
		model.addAttribute("totalPages", totalPages);
		model.addAttribute("type", type);
		model.addAttribute("size", size); // 2
		
		
		return "account/detail";
	}	
	
}

 

 

AccountService - ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

๋”๋ณด๊ธฐ

์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

	/**
	 * ๋‹จ์ผ ๊ณ„์ขŒ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ 
	 * @param type = [all, deposit, withdrawal]
	 * @param accountId (pk)
	 * @return ์ „์ฒด, ์ž…๊ธˆ, ์ถœ๊ธˆ ๊ฑฐ๋ž˜๋‚ด์—ญ(3๊ฐ€์ง€ ํƒ€์ž…) ๋ฐ˜ํ™˜ 
	 */
	// @Transactional
	public List<HistoryAccount> readHistoryByAccountId(String type, Integer accountId, int page, int size) {
		List<HistoryAccount> list = new ArrayList<>();
		int limit = size;
		int offset = (page - 1) * size;
		list = historyRepository.findByAccountIdAndTypeOfHistory(type, accountId, limit, offset);
		return list;
	}
	
	// ํ•ด๋‹น ๊ณ„์ขŒ์™€ ๊ฑฐ๋ž˜ ์œ ํ˜•์— ๋”ฐ๋ฅธ ์ „์ฒด ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ
	public int countHistoryByAccountIdAndType(String type, Integer accountId) {
		return historyRepository.countByAccountIdAndType(type, accountId);
	}

 

 ์ „์ฒด์ฝ”๋“œ

package com.tenco.bank.service;

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import org.h2.value.Transfer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.tenco.bank.dto.DepositDTO;
import com.tenco.bank.dto.SaveDTO;
import com.tenco.bank.dto.TransferDTO;
import com.tenco.bank.dto.WithdrawalDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.repository.interfaces.AccountRepository;
import com.tenco.bank.repository.interfaces.HistoryRepository;
import com.tenco.bank.repository.model.Account;
import com.tenco.bank.repository.model.History;
import com.tenco.bank.repository.model.HistoryAccount;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.utils.Define;

@Service // IoC ๋Œ€์ƒ( ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ) 
public class AccountService {
	
	private final AccountRepository accountRepository;
	private final HistoryRepository historyRepository;
	
	@Autowired // ์ƒ๋žต ๊ฐ€๋Šฅ - DI ์ฒ˜๋ฆฌ
	public AccountService(AccountRepository accountRepository, HistoryRepository historyRepository) {
		this.accountRepository = accountRepository; 
		this.historyRepository = historyRepository;
	}
	
	/**
	 * ๊ณ„์ขŒ ์ƒ์„ฑ ๊ธฐ๋Šฅ
	 * @param dto
	 * @param integer
	 */
	
	// ํŠธ๋žœ ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ•œ๋‹ค. (ํ•œ๋ฒˆ์— ๋ฐ˜์˜๋˜๊ฑฐ๋‚˜, ์•„์˜ˆ ๋ฐ˜์˜์•ˆ๋˜๊ฑฐ๋‚˜)  
	@Transactional
	public void createAccount(SaveDTO dto, Integer principalId) {
		
		int result = 0;
		
		try {
			result = accountRepository.insert(dto.toAccount(principalId));
			
		} catch (DataAccessException e) {
			throw new DataDeliveryException("์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch(Exception e) {
			throw new RedirectException("์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜ ์ž…๋‹ˆ๋‹ค.", HttpStatus.SERVICE_UNAVAILABLE);
		}
		
		if(result == 0) {
			throw new DataDeliveryException("์ •์ƒ ์ฒ˜๋ฆฌ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", HttpStatus.INTERNAL_SERVER_ERROR);
		}
		
	}
	
	/**
	 * ๊ณ„์ขŒ ๋ช‡๊ฐœ ์žˆ๋Š”์ง€ ํ™•์ธ
	 * @param principal
	 */
	
	@Transactional
	public List<Account> readAccountListByUserId(Integer userId) {
		// TODO Auto-generated method stub
		List<Account> accountListEntity = null;
		
		try {
			accountListEntity = accountRepository.findByUserId(userId);
		} catch (DataDeliveryException e) {
			// TODO: handle exception
			throw new DataDeliveryException("์ž˜๋ชป๋œ ์ฒ˜๋ฆฌ ์ž…๋‹ˆ๋‹ค.", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜", HttpStatus.SERVICE_UNAVAILABLE);
		}
		
		return accountListEntity; 
		
	}
	
	 // ํ•œ๋ฒˆ์— ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์€ ํž˜๋“ฌ
	 // 1. ์‚ฌ์šฉ์ž๊ฐ€ ๋˜์ง„ ๊ณ„์ขŒ๋ฒˆํ˜ธ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. --> select 
	 // 2. ๋ณธ์ธ ๊ณ„์ขŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. --> ๊ฐ์ฒด ์ƒํƒœ๊ฐ’์—์„œ ๋น„๊ตํ•œ๋‹ค.
	 // 3. ๊ณ„์ขŒ ๋น„๋ฒˆ ํ™•์ธ --> ๊ฐ์ฒด ์ƒํƒœ๊ฐ’์—์„œ ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ account์—์„œ userid๊ฐ€ ์žˆ๊ณ  principal์—์„œ๋„ ํ™•์ธ๊ฐ€๋Šฅ
	 // 4. ์ž”์•ก ์—ฌ๋ถ€ ํ™•์ธ --> ๊ฐ์ฒด ์ƒํƒœ๊ฐ’์—์„œ ํ™•์ธ
	 // 5. ์ถœ๊ธˆ ์ฒ˜๋ฆฌ --> update ์ฟผ๋ฆฌ ๋ฐœ์ƒ
	 // 6. history ํ…Œ์ด๋ธ”์— ๊ฑฐ๋ž˜๋‚ด์—ญ ๋“ฑ๋ก --> insert(history)
	 // 7. ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ex) insert ํ•˜๋‹ค๊ฐ€ ์˜ค๋ฅ˜๋‚˜๋ฉด ๋’ค๋กœ update ๊ฐ€์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋ž™์žญ์…˜ ์‚ฌ์šฉ
	
	@Transactional
	public void updateAccountWithdraw(WithdrawalDTO dto, Integer principalId) {
		
		// 1. ์‚ฌ์šฉ์ž๊ฐ€ ๋˜์ง„ ๊ณ„์ขŒ๋ฒˆํ˜ธ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. 
		// (ํผ์‹œ์Šคํ„ด์Šค ๊ณ„์ธต์—์„œ ๊ธ์–ด์„œ entity ๋ถ™์ž„)
		Account accountEntity = accountRepository.findByNumber(dto.getWAccountNumber());
		if(accountEntity == null) {
			throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.BAD_REQUEST);
		}
		
		// 2.
		accountEntity.checkOwner(principalId);
		
		// 3.
		accountEntity.checkPassword(dto.getWAccountPassword());
		
		// 4.
		accountEntity.checkBalance(dto.getAmount());
		
		// 5. ์ถœ๊ธˆ ์ฒ˜๋ฆฌ -- accountRepository ๊ฐ์ฒด์˜ ์ž”์•ก์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
		accountEntity.withdraw(dto.getAmount());
		
		// update ์ฒ˜๋ฆฌ
		accountRepository.updateById(accountEntity);
		
		// 6 - ๊ฑฐ๋ž˜ ๋‚ด์—ญ ๋“ฑ๋ก
		/**
		 * <insert id="insert">
			insert into history_tb(amount, w_Balance, d_Balance, w_Account_id, d_account_id )
			values(#{amount}, #{wBalance}, #{dBalance}, #{wAccountId}, #{dAccountId} )
			</insert>
		 */
		
		History history = new History();
		history.setAmount(dto.getAmount());
		history.setWBalance(accountEntity.getBalance()); // ๊ทธ ์‹œ์ ์— ๋Œ€ํ•œ ์ž”์•ก
		history.setDBalance(null);
		history.setWAccountId(accountEntity.getId());
		history.setDAccountId(null);
		
		int rowResultCount = historyRepository.insert(history);
		if(rowResultCount != 1) {
			throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
	
	// 1. ๊ณ„์ขŒ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ
    // 2. ๋ณธ์ธ ๊ณ„์ขŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ -- ๊ฐ์ฒด ์ƒํƒœ๊ฐ’์—์„œ ๋น„๊ต
    // 3. ์ž…๊ธˆ ์ฒ˜๋ฆฌ -- update
    // 4. ๊ฑฐ๋ž˜ ๋‚ด์—ญ ๋“ฑ๋ก -- insert(history)
    @Transactional
    public void updateAccountDeposit(DepositDTO dto, Integer principalId) {
        // 1.
        Account accountEntity = accountRepository.findByNumber(dto.getDAccountNumber());
        if (accountEntity == null) {
            throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.BAD_REQUEST);
        }
        // 2.
        accountEntity.checkOwner(principalId);
        // 3.
        accountEntity.deposit(dto.getAmount());
        accountRepository.updateById(accountEntity);
        // 4.
        History history = History.builder()
            .amount(dto.getAmount())
            .dAccountId(accountEntity.getId())
            .dBalance(accountEntity.getBalance())
            .wAccountId(null)
            .wBalance(null)
            .build();
        int rowResultCount = historyRepository.insert(history);
        if (rowResultCount != 1) {
            throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    // ์ด์ฒด ๊ธฐ๋Šฅ ๋งŒ๋“ค๊ธฐ
    // 1. ์ถœ๊ธˆ ๊ณ„์ขŒ ์กด์žฌ์—ฌ๋ถ€ ํ™•์ธ -- select (๊ฐ์ฒด ๋ฆฌํ„ด ๋ฐ›์€ ์ƒํƒœ)
    // 2. ์ž…๊ธˆ ๊ณ„์ขŒ ์กด์žฌ์—ฌ๋ถ€ ํ™•์ธ -- select (๊ฐ์ฒด ๋ฆฌํ„ด ๋ฐ›์€ ์ƒํƒœ)
    // 3. ์ถœ๊ธˆ ๊ณ„์ขŒ ๋ณธ์ธ ์†Œ์œ  ํ™•์ธ -- ๊ฐ์ฒด ์ƒํƒœ๊ฐ’๊ณผ ์„ธ์…˜ ์•„์ด๋””(ID) ๋น„๊ต
    // 4. ์ถœ๊ธˆ ๊ณ„์ขŒ ๋น„๋ฐ€ ๋ฒˆํ˜ธ ํ™•์ธ -- ๊ฐ์ฒด ์ƒํƒœ๊ฐ’๊ณผ dto ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ต
    // 5. ์ถœ๊ธˆ ๊ณ„์ขŒ ์ž”์•ก ์—ฌ๋ถ€ ํ™•์ธ -- ๊ฐ์ฒด ์ƒํƒœ๊ฐ’ ํ™•์ธ, dto์™€ ๋น„๊ต
    // 6. ์ž…๊ธˆ ๊ณ„์ขŒ ๊ฐ์ฒด ์ƒํƒœ๊ฐ’ ๋ณ€๊ฒฝ ์ฒ˜๋ฆฌ (๊ฑฐ๋ž˜๊ธˆ์•ก ์ฆ๊ฐ€์ฒ˜๋ฆฌ)
    // 7. ์ž…๊ธˆ ๊ณ„์ขŒ -- update ์ฒ˜๋ฆฌ 
    // 8. ์ถœ๊ธˆ ๊ณ„์ขŒ ๊ฐ์ฒด ์ƒํƒœ๊ฐ’ ๋ณ€๊ฒฝ ์ฒ˜๋ฆฌ (์ž”์•ก - ๊ฑฐ๋ž˜๊ธˆ์•ก)
    // 9. ์ถœ๊ธˆ ๊ณ„์ขŒ -- update ์ฒ˜๋ฆฌ 
    // 10. ๊ฑฐ๋ž˜ ๋‚ด์—ญ ๋“ฑ๋ก ์ฒ˜๋ฆฌ
    // 11. ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ
    public void updateAccountTransfer(TransferDTO dto, Integer principalId) {
    	
    	// ์ถœ๊ธˆ ๊ณ„์ขŒ ์ •๋ณด ์กฐํšŒ 
    	Account withdrawAccountEntity = accountRepository.findByNumber(dto.getWAccountNumber());
    	System.out.println("withdrawAccountEntity : " + withdrawAccountEntity);
    	
    	// ์ž…๊ธˆ ๊ณ„์ขŒ ์ •๋ณด ์กฐํšŒ
    	Account depositAccountEntity = accountRepository.findByNumber(dto.getDAccountNumber());
    	System.out.println("depositAccountEntity : " + depositAccountEntity);
    	
    	if(withdrawAccountEntity == null) {
    		throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.INTERNAL_SERVER_ERROR);
    	}
    	
    	if(depositAccountEntity == null) {
    		throw new DataDeliveryException("์ƒ๋Œ€๋ฐฉ์˜ ๊ณ„์ขŒ ๋ฒˆํ˜ธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.", HttpStatus.INTERNAL_SERVER_ERROR);
    	}
    	
    	withdrawAccountEntity.checkOwner(principalId); // ์ถœ๊ธˆํ•˜๋Š” ๋‚ด ๊ณ„์ขŒ๊ฐ€ ๋‚ด๊ป€์ง€ ํ™•์ธ
    	withdrawAccountEntity.checkPassword(dto.getPassword()); // ์ถœ๊ธˆํ•˜๋Š” ๋‚ด ๊ณ„์ขŒ์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งž๋Š”์ง€ Transfer DTO์˜ password ๋ž‘ ๋น„๊ต
    	withdrawAccountEntity.checkBalance(dto.getAmount());
    	withdrawAccountEntity.withdraw(dto.getAmount()); 
    	
    	depositAccountEntity.deposit(dto.getAmount());
    	
    	int resultRowCountWithdraw = accountRepository.updateById(withdrawAccountEntity); // ์ƒˆ๋กœ ๊ฐฑ์‹ 
    	int resultRowCountDeposit = accountRepository.updateById(depositAccountEntity); // ์ƒˆ๋กœ ๊ฐฑ์‹ 
    	
    	if(resultRowCountWithdraw != 1 && resultRowCountDeposit != 1) {
    		throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
    	}
    	
    	// TransferDTO ์— History ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“ค ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
    	History history = History.builder()
    							 .amount(dto.getAmount()) // ์ด์ฒด ๊ธˆ์•ก
    							 .wAccountId(withdrawAccountEntity.getId()) // ์ถœ๊ธˆ ๊ณ„์ขŒ
    							 .dAccountId(depositAccountEntity.getId()) // ์ž…๊ธˆ ๊ณ„์ขŒ
    							 .wBalance(withdrawAccountEntity.getBalance()) // ์ถœ๊ธˆ ๊ณ„์ขŒ ๋‚จ์€ ์ž”์•ก
    							 .dBalance(depositAccountEntity.getBalance()) // ์ž…๊ธˆ ๊ณ„์ขŒ ๋‚จ์€ ์ž”์•ก
    							 .build();
    	
    	int resultRowCountHistory = historyRepository.insert(history);
    	if(resultRowCountHistory != 1) {
    		throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
    		}
    	}  	
    
    /**
     * ๋‹จ์ผ ๊ณ„์ขŒ ์กฐํšŒ ๊ธฐ๋Šฅ (accountId ๊ธฐ์ค€)
     * @param accountId
     * @return
     */
    public Account readAccountById(Integer accountId) {
    	Account accountEntity = accountRepository.findByAccountId(accountId);
    	if(accountEntity == null) {
    		throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.INTERNAL_SERVER_ERROR);
    	}
    	return accountEntity;
    }
    
    /**
     * ๋‹จ์ผ ๊ณ„์ขŒ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ
     * @param type : [all, deposit, withdrawal]
     * @param accountId (pk)
     * @return ์ „์ฒด, ์ž…๊ธˆ, ์ถœ๊ธˆ ๊ฑฐ๋ž˜๋‚ด์—ญ(3๊ฐ€์ง€ ํƒ€์ž…) ๋ฐ˜ํ™˜
     */
   // @Transactional
    public List<HistoryAccount> readHistoryByAccountId(String type, Integer accountId, int page, int size) {
    	List<HistoryAccount> list = new ArrayList<HistoryAccount>();
    	int limit = size;
    	int offset = (page - 1) * size;
    	list = historyRepository.findByAccountIdAndTypeOfHistory(type, accountId, limit, offset);
    	return list;
    }

	// ํ•ด๋‹น ๊ณ„์ขŒ์™€ ๊ฑฐ๋ž˜ ์œ ํ˜•์— ๋”ฐ๋ฅธ ์ „์ฒด ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ
	public int countHistoryByAccountIdAndType(String type, Integer accountId) {
		return historyRepository.countByAccountIdAndType(type, accountId);
	}
 
    
}

 

 

AccountRepository - ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

๋”๋ณด๊ธฐ

์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •

// ๊ณ ๋ฏผ! - ๊ณ„์ขŒ ์กฐํšŒ 
	// --> ํ•œ ์‚ฌ๋žŒ์˜ ์œ ์ €๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ณ„์ขŒ๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. (๋ฆฌ์ŠคํŠธ๋กœ ๋ฝ‘์•„์•ผ ํ•œ๋‹ค.) 
	// @Param ์‚ฌ์šฉ์ด์œ  : interface ํŒŒ๋ผ๋ฏธํ„ฐ๋ช…๊ณผ xml์— ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ช…์„ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ๋œ๋‹ค๋ฉด
	// @param ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  2๊ฐœ ์ด์ƒ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ ์‚ฌ์šฉ.
	public List<Account> findByUserId(@Param("userId") Integer principalId);
	
	// --> account id ๊ฐ’์œผ๋กœ ๊ณ„์ขŒ์ •๋ณด ์กฐํšŒ. (ํ•„์š”ํ•˜๋‹ค.)
	public Account findByNumber(@Param("number") String id);
	
	// ์ฝ”๋“œ ์ถ”๊ฐ€ ์˜ˆ์ •
	public Account findByAccountId(Integer accountId);
	
	public int countByAccountIdAndType(@Param("type")String type, 
			@Param("accountId")Integer accountId);

 

 ์ „์ฒด์ฝ”๋“œ

package com.tenco.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.tenco.bank.repository.model.Account;

// AccountRepository ์ธํ„ฐํŽ˜์ด์Šค์™€ account.xml ํŒŒ์ผ์„ ๋งค์นญ ์‹œํ‚จ๋‹ค.
@Mapper
public interface AccountRepository {
	public int insert(Account account);
	public int updateById(Account account);
	public int deleteBtId(Integer id, String name);
	
	// ๊ณ ๋ฏผ! - ๊ณ„์ขŒ ์กฐํšŒ 
	// --> ํ•œ ์‚ฌ๋žŒ์˜ ์œ ์ €๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ณ„์ขŒ๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. (๋ฆฌ์ŠคํŠธ๋กœ ๋ฝ‘์•„์•ผ ํ•œ๋‹ค.) 
	// @Param ์‚ฌ์šฉ์ด์œ  : interface ํŒŒ๋ผ๋ฏธํ„ฐ๋ช…๊ณผ xml์— ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ช…์„ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ๋œ๋‹ค๋ฉด
	// @param ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  2๊ฐœ ์ด์ƒ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ ์‚ฌ์šฉ.
	public List<Account> findByUserId(@Param("userId") Integer principalId);
	
	// --> account id ๊ฐ’์œผ๋กœ ๊ณ„์ขŒ์ •๋ณด ์กฐํšŒ. (ํ•„์š”ํ•˜๋‹ค.)
	public Account findByNumber(@Param("number") String id);
	
	// ์ฝ”๋“œ ์ถ”๊ฐ€ ์˜ˆ์ •
	public Account findByAccountId(Integer accountId);
	
	public int countByAccountIdAndType(@Param("type")String type, 
			@Param("accountId")Integer accountId);
	
}

 

history.xml - ์ฟผ๋ฆฌ ์ˆ˜์ • ๋ฐ ์ถ”๊ฐ€

๋”๋ณด๊ธฐ

์ฟผ๋ฆฌ ์ˆ˜์ • ๋ฐ ์ถ”๊ฐ€

<select id="findByAccountIdAndTypeOfHistory" resultType="com.tenco.bank.repository.model.HistoryAccount">
		<if test="type == 'all'">
			select h.id, h.amount,
				case
					when h.w_account_id = #{accountId} then (h.w_balance) 
			        when h.d_account_id = #{accountId} then (h.d_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 as h 
			left join account_tb as wa on h.w_account_id = wa.id
			left join account_tb as da on h.d_account_id = da.id  
			where h.w_account_id = #{accountId} OR h.d_account_id = #{accountId}
			limit #{limit} offset #{offset}
		</if>
		<if test="type == 'deposit'">
			select h.id, h.amount, h.d_balance as balance, h.created_at, 
				coalesce(CAST(wa.number as CHAR(10)) , 'ATM') as sender, 
			    da.number as receiver
			from history_tb as h 
			left join account_tb as wa on wa.id = h.w_account_id
			left join account_tb as da on da.id = h.d_account_id 
			where h.d_account_id = #{accountId}
			limit #{limit} offset #{offset}
		</if>
		
		<if test="type == 'withdrawal'">
			select h.id, h.amount, h.w_balance AS balance, h.created_at, 
				   coalesce(cast(da.number as CHAR(10)), 'ATM')  as receiver, 
			       wa.number as sender 
			from history_tb as h 
			left join account_tb as wa on wa.id = h.w_account_id
			left join account_tb as da on da.id = h.d_account_id 
			where h.w_account_id = #{accountId}
			limit #{limit} offset #{offset}
		</if>
	</select>
	
	
	<select id="countByAccountIdAndType" resultType="int">
		<if test="type == 'all'">
			select count(*)
			from history_tb as h 
			where h.w_account_id = #{accountId} OR h.d_account_id = #{accountId}
		</if>
		<if test="type == 'deposit'">
			select count(*)
			from history_tb as h 
			where h.d_account_id = #{accountId}
		</if>
		
		<if test="type == 'withdrawal'">
			select count(*)
			from history_tb as h 
			where h.w_account_id = #{accountId}
		</if>
	</select>