티스토리 뷰
작업 순서
1. withdrawal.jsp 파일 생성 및 코드 추가
2. 출금 화면 요청 및 기능 구현
3. 전체 코드 확인
withdrawal.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>
<!-- 예외적으로 로그인은 보안 때문에 post로 던지자 -->
<!--
insert into account_tb(number, password, balance, user_id, created_at)
-->
<form action="/account/withdrawal" method="post">
<div class="form-group">
<label for="amount">출금 금액:</label>
<input type="number" class="form-control" placeholder="Enter amount" id="amount" name="amount" value="1000" >
</div>
<div class="form-group">
<label for="wAccountNumber">출금 계좌 번호:</label>
<input type="text" class="form-control" placeholder="Enter account number" id="wAccountNumber" name="wAccountNumber" value="1111">
</div>
<div class="form-group">
<label for="pwd">출금 계좌 비밀 번호 :</label>
<input type="password" class="form-control" placeholder="Enter password" id="pwd" name="wAccountPassword" value="1234" >
</div>
<div class="text-right">
<button type="submit" class="btn btn-primary">출금 요청</button>
</div>
</form>
</div>
<!-- end of col-sm-8 -->
</div>
</div>
<!-- end of content.jsp(xxx.jsp) -->
<!-- footer.jsp -->
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>
결과 화면
WithdrawalDTO 생성
package com.tenco.bank.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class WithdrawalDTO {
private Long amount;
private String wAccountNumber;
private String wAccountPassword;
}
AccountController 코드 추가
/**
* 출금 페이지 요청
* @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) {
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";
}
}
AccountService 코드 추가
// 출금 기능 만들기
@Transactional
public void updateAccountWithdraw(WithdrawalDTO dto, Integer principalId) {
// 1. 계좌 존재 여부를 확인
Account accoutEntity = accountRepository.findByNumber(dto.getWAccountNumber());
if(accoutEntity == null) {
throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.BAD_REQUEST);
}
// 2. 본인 계좌 여부를 확인
accoutEntity.checkOwner(principalId);
// 3. 계좌 비번 확인
accoutEntity.checkPassword(dto.getWAccountPassword());
// 4. 잔액 여부 확인
accoutEntity.checkBalance(dto.getAmount());
// 5. 출금 처리
// accoutEntity 객체의 잔액을 변경하고 업데이트 처리해야 한다.
accoutEntity.withdraw(dto.getAmount());
// update 처리
accountRepository.updateById(accoutEntity);
// 6. history 테이블에 거래 내역 등록
History history = new History();
history.setAmount(dto.getAmount());
history.setWBalance(accoutEntity.getBalance());
history.setDBalance(null);
history.setWAccountId(accoutEntity.getId());
history.setDAccountId(null);
int rowResultCount = historyRepository.insert(history);
if(rowResultCount != 1) {
throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
출금 기능 흐름
1. 계좌 존재 여부를 확인 -- select --> Account 객체를 받을 수 있음(userId 포함 -- principalId와 비교)
2. 본인 계좌 여부를 확인 -- 객체 상태값에서 비교
3. 계좌 비번 확인 -- 객체 상태값에서 Account와 dto 일치 여부 확인
4. 잔액 여부 확인 -- 객체 상태값에서 확인
5. 출금 처리 -- update
6. history 테이블에 거래 내역 등록 - insert(history)
7. 트랜잭션 처리(insert하다가 오류 발생 시 update로 rollback)
History 등록 시 입금, 출금, 이체 3가지 형태가 존재 합니다.
이력의 형태를 따로 컬럼을 추가해서 생성하지 않고 ROW 들어간 값에 형태로 구분해 낼 수 있습니다.
여기서 // 2는
if(accountEntity.getUserId() != principalId) {
throw new DataDeliveryException(Define.NOT_ACCOUNT_OWNER, HttpStatus.BAD_REQUEST);
}
이 코드를 model 패키지의 Account.java에 checkOwner 메서드를 생성하여 계좌 소유자를 확인하였습니다.
여기서 // 3은
if(!accountEntity.getPassword().equals(dto.getWAccountPassword())) {
throw new DataDeliveryException(Define.FAIL_ACCOUNT_PASSWORD, HttpStatus.BAD_REQUEST);
}
이 코드를 model 패키지의 Account.java에 checkPassword 메서드를 생성하여 패스워드를 체크 확인하였습니다.
여기서 // 4는
if(accountEntity.getBalance() < dto.getAmount()) {
throw new DataDeliveryException(Define.LACK_Of_BALANCE, HttpStatus.BAD_REQUEST);
}
이 코드를 model 패키지의 Account.java에 checkBalance 메서드를 생성하여 잔액 여부를 확인하였습니다.
Account
// 패스워드 체크
public void checkPassword(String password) {
// f == f 일때 ---> true
if(this.password.equals(password) == false) {
throw new DataDeliveryException(Define.FAIL_ACCOUNT_PASSWROD, HttpStatus.BAD_REQUEST);
}
}
// 잔액 여부 확인 - checkBalance
public void checkBalance(Long amount) {
if(this.balance < amount) {
throw new DataDeliveryException(Define.LACK_Of_BALANCE, HttpStatus.BAD_REQUEST);
}
}
// 계좌 소유자 확인 기능 - checkOwner
public void checkOwner(Integer principalId) {
if(this.userId != principalId) {
throw new DataDeliveryException(Define.NOT_ACCOUNT_OWNER, HttpStatus.BAD_REQUEST);
}
}
}
실행 결과
로그인 성공 시 계좌목록으로 이동되어 잔액을 확인할 수 있습니다.
이때 출금하기를 누르면
아래 그림처럼 withdrawal.jsp 화면이 뜹니다.
출금 금액, 계좌번호, 비밀번호를 입력하여
출금 요청 버튼을 누르면
잔액이 1300에서 300이 출금되어 1000이 된 것을 볼 수 있습니다.
DB를 보시면
AMOUNT(거래금액) 1000
W_ACCOUNT_ID(출금 계좌 ID) 1
D_ACCOUNT_ID(입금 계좌 ID) null
W_BALANCE(출금 계좌 잔액) 300
D_BALANCE(입금 계좌 잔액) null
값이 들어온 것을 확인할 수 있습니다.
코드를 해석하자면
W_ACCOUNT_ID(출금 계좌 ID) 가 1인 길동이
자신의 계좌에서
AMOUNT(거래금액) 1000를 출금해서
W_BALANCE(출금 계좌 잔액)이 300이 되었습니다.
이는 우리가 돈이 필요해서 은행에서 돈을 빼내는 것과 같습니다.
출금 이전 계좌 상황
출금 이후 계좌 상황