티스토리 뷰

Spring boot

출금 기능

yoooon1212 2024. 8. 8. 14:00
작업 순서

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이 되었습니다.

 

 

이는 우리가 돈이 필요해서 은행에서 돈을 빼내는 것과 같습니다. 

 

 


출금 이전 계좌 상황

 

 

출금 이후 계좌 상황

 

'Spring boot' 카테고리의 다른 글

이체 기능  (0) 2024.08.08
입금 기능  (0) 2024.08.08
중간 리팩토링  (0) 2024.08.08
계좌 목록 만들기  (0) 2024.08.08
계좌 생성  (1) 2024.08.07
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함