💡 학습 목표
1. transfer.jsp 코드 확인 및 주소 설계
2. transferDto 코드 확인
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/view/layout/header.jsp"%>
<div class="col-sm-8">
<h2>이체 페이지(인증)</h2>
<h5>어서오세요 환영합니다.</h5>
<div class="bg-light p-md-5 h-75">
<div class="form-group">
<form action="/account/transfer" method="post">
<div class="form-group">
<label for="amount">이체 금액</label>
<input type="text" id="amount" class="form-control" placeholder="이체 금액을 입력하세요" name="amount">
</div>
<div class="form-group">
<label for="wAccountNumber">출금 계좌번호</label>
<input type="text" id="wAccountNumber" class="form-control" placeholder="출금 계좌번호를 입력하세요" name="wAccountNumber">
</div>
<div class="form-group">
<label for="dAccountNumber">이체 계좌번호</label>
<input type="text" id="dAccountNumber" class="form-control" placeholder="입금 계좌번호를 입력하세요" name="dAccountNumber">
</div>
<div class="form-group">
<label for="wAccountPassword">출금 계좌 비밀번호</label>
<input type="password" id="wAccountPassword" class="form-control" placeholder="출금 계좌 비밀번호를 입력하세요" name="wAccountPassword">
</div>
<button type="submit" class="btn btn-primary">이체</button>
</form>
</div>
</div>
</div>
</div>
</div>
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>
package com.tencoding.bank.dto;
import lombok.Data;
@Data
public class TransferFormDto {
// 화면 name태그 기준
private Long amount;
private String wAccountNumber;
private String dAccountNumber;
private String wAccountPassword;
}
AccountController.java
package com.tencoding.bank.controller;
import java.util.List;
import javax.servlet.http.HttpSession;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tencoding.bank.dto.DepositFormDto;
import com.tencoding.bank.dto.SaveFormDto;
import com.tencoding.bank.dto.TransferFormDto;
import com.tencoding.bank.dto.WithDrawFormDto;
import com.tencoding.bank.handler.exception.CustomRestfulException;
import com.tencoding.bank.handler.exception.UnAuthorizedException;
import com.tencoding.bank.repository.model.Account;
import com.tencoding.bank.repository.model.User;
import com.tencoding.bank.service.AccountService;
import com.tencoding.bank.util.Define;
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private HttpSession session;
@Autowired
private AccountService accountService;
// 계좌 목록 페이지
// http://localhost:80/account/list
@GetMapping("/list")
public String list(Model model) {
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
List<Account> accountList = accountService.readAccountList(user.getId());
if(accountList.isEmpty()) {
model.addAttribute("accountList", null);
} else {
model.addAttribute("accountList", accountList);
}
return "account/list";
}
// 계좌 생성 페이지
// http://localhost:80/account/save
// /account/save - 화면 이동
/**
* 계좌 생성 페이지 이동
*/
@GetMapping("/save")
public String save() {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
return "account/save";
}
/**
* 계좌 생성 로직 구현
* @return
*/
@PostMapping("/save")
public String saveProc(SaveFormDto saveFormDto) {
// 1. 인증 검사
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
if(saveFormDto.getNumber() == null
|| saveFormDto.getNumber().isEmpty()) {
throw new CustomRestfulException("계좌번호를 입력해주세요.", HttpStatus.BAD_REQUEST);
}
if(saveFormDto.getPassword() == null
|| saveFormDto.getPassword().isEmpty()) {
throw new CustomRestfulException("비밀번호를 입력해주세요.", HttpStatus.BAD_REQUEST);
}
if(saveFormDto.getBalance() == null
|| saveFormDto.getBalance() < 0) {
throw new CustomRestfulException("잘못된 입력입니다.", HttpStatus.BAD_REQUEST);
}
// 3. 서비스 호출
accountService.creatAccount(saveFormDto, user.getId());
return "redirect:/account/list";
}
// 출금 페이지
// http://localhost:80/account/withdraw
@GetMapping("/withdraw")
public String withdraw() {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
return "account/withdraw";
}
// body -> String --> amount=1000&wAccountId=10&......
@PostMapping("/withdraw")
public String withdrawProc(WithDrawFormDto withDrawFormDto) {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
if(withDrawFormDto.getAmount() == null) {
throw new CustomRestfulException("금액을 입력해주세요.", HttpStatus.BAD_REQUEST);
}
if(withDrawFormDto.getAmount() <= 0) {
throw new CustomRestfulException("잘못된 금액입니다.", HttpStatus.BAD_REQUEST);
}
if(withDrawFormDto.getWAccountNumber() == null
|| withDrawFormDto.getWAccountNumber().isEmpty()) {
throw new CustomRestfulException("출금 계좌번호를 확인해주세요.", HttpStatus.BAD_REQUEST);
}
if(withDrawFormDto.getWAccountPassword() == null
|| withDrawFormDto.getWAccountPassword().isEmpty()) {
throw new CustomRestfulException("출금 계좌 비밀번호를 확인해주세요.", HttpStatus.BAD_REQUEST);
}
accountService.updateAccountWithdraw(withDrawFormDto, user.getId());
return "redirect:/account/list";
}
// 입금 페이지
// http://localhost:80/account/deposit
@GetMapping("/deposit")
public String deposit() {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
return "account/deposit";
}
@PostMapping("/deposit")
public String depositProc(DepositFormDto depositFormDto) {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
if(depositFormDto.getAmount() == null) {
throw new CustomRestfulException("금액을 입력해주세요.", HttpStatus.BAD_REQUEST);
}
if(depositFormDto.getAmount() <= 0) {
throw new CustomRestfulException("잘못된 금액입니다.", HttpStatus.BAD_REQUEST);
}
if(depositFormDto.getDAccountNumber() == null
|| depositFormDto.getDAccountNumber().isEmpty()) {
throw new CustomRestfulException("입금 계좌번호를 입력해주세요.", HttpStatus.BAD_REQUEST);
}
accountService.updateAccountDeposit(depositFormDto);
return "redirect:/account/list";
}
// 이체 페이지
// http://localhost:80/account/transfer
@GetMapping("/transfer")
public String transfer() {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
return "account/transfer";
}
// 1. 이체 금액 0원 이상 입력 여부 확인
// 2. 출금 계좌 번호 입력 여부 확인
// 3. 입금 계좌 번호 입력 여부 확인
// 4. 출금 계좌 비밀 번호 입력 여부 확인
@PostMapping("/transfer")
public String transferProc(TransferFormDto transferFormDto) {
// 1. 인증 여부 확인
User user = (User)session.getAttribute(Define.PRINCIPAL);;
if(user == null) {
throw new UnAuthorizedException("로그인을 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
if(transferFormDto.getAmount() == null) {
throw new CustomRestfulException("이체 금액을 입력해주세요.", HttpStatus.BAD_REQUEST);
}
if(transferFormDto.getAmount() <= 0) {
throw new CustomRestfulException("이체 금액이 0원 이하일 수 없습니다.", HttpStatus.BAD_REQUEST);
}
if(transferFormDto.getWAccountNumber() == null
|| transferFormDto.getWAccountNumber().isEmpty()) {
throw new CustomRestfulException("출금 계좌번호를 확인해주세요.", HttpStatus.BAD_REQUEST);
}
if(transferFormDto.getDAccountNumber() == null
|| transferFormDto.getDAccountNumber().isEmpty()) {
throw new CustomRestfulException("입금 계좌번호를 확인해주세요.", HttpStatus.BAD_REQUEST);
}
if(transferFormDto.getWAccountPassword() == null
|| transferFormDto.getWAccountPassword().isEmpty()) {
throw new CustomRestfulException("출금 계좌 비밀번호를 확인해주세요.", HttpStatus.BAD_REQUEST);
}
// 3. 서비스 호출
accountService.updateAccountTransfer(transferFormDto, user.getId());
return "redirect:/account/list";
}
// TODO - 수정하기
// 상세 보기 페이지
// http://localhost:80/account/detail/1
@GetMapping("/detail")
public String detail() {
return "account/detail";
}
}
AccountService.java
package com.tencoding.bank.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.tencoding.bank.dto.DepositFormDto;
import com.tencoding.bank.dto.SaveFormDto;
import com.tencoding.bank.dto.TransferFormDto;
import com.tencoding.bank.dto.WithDrawFormDto;
import com.tencoding.bank.handler.exception.CustomRestfulException;
import com.tencoding.bank.repository.interfaces.AccountRepository;
import com.tencoding.bank.repository.interfaces.HistoryRepository;
import com.tencoding.bank.repository.model.Account;
import com.tencoding.bank.repository.model.History;
@Service // IoC 대상 + 싱글톤 패턴으로 -> 스프링 컨테이너 메모리에 객체가 생성
public class AccountService {
@Autowired
private AccountRepository accountRepository;
@Autowired
private HistoryRepository historyRepository;
@Transactional
public void creatAccount(SaveFormDto saveFormDto, Integer principalId) {
// 등록 처리 - insert
Account account = new Account();
account.setNumber(saveFormDto.getNumber());
account.setPassword(saveFormDto.getPassword());
account.setBalance(saveFormDto.getBalance());
account.setUserId(principalId);
int resultRowCount = accountRepository.insert(account);
if(resultRowCount != 1) {
throw new CustomRestfulException("계좌 생성 실패", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 계좌 목록 보기 ( 로그인 된 사용자 )
* @param userId
* @return
*/
@Transactional
public List<Account> readAccountList(Integer userId) {
List<Account> list = accountRepository.findByUserId(userId);
return list;
}
// 출금 기능 로직을 고민해보기
// 1. 계좌 존재 여부 확인 - select query
// 2. 본인 계좌 여부 확인 - select query
// 3. 계좌 비밀번호 확인 - select query
// 4. 계좌 잔액 여부 확인 - select query
// 5. 출금 처리 --> update query
// 6. 거래 내역 등록 -> insert query - history_tb
// 7. 트랜잭션 처리
@Transactional
public void updateAccountWithdraw(WithDrawFormDto withDrawFormDto, Integer id) {
Account accountEntity = accountRepository.findByNumber(withDrawFormDto.getWAccountNumber());
// 1
if(accountEntity == null) {
throw new CustomRestfulException("해당 계좌가 없습니다.", HttpStatus.BAD_REQUEST);
}
// 2
if(accountEntity.getUserId() != id) {
throw new CustomRestfulException("본인 소유 계좌가 아닙니다.", HttpStatus.BAD_REQUEST);
}
// 3
if(accountEntity.getPassword().equals(withDrawFormDto.getWAccountPassword()) == false) {
throw new CustomRestfulException("출금 계좌 비밀번호가 일치하지 않습니다.", HttpStatus.BAD_REQUEST);
}
// 4
if(accountEntity.getBalance() < withDrawFormDto.getAmount()) {
throw new CustomRestfulException("계좌 잔액이 부족합니다.", HttpStatus.BAD_REQUEST);
}
// 5 -> update query (모델 객체 상태 변경 -> 객체 다시 던지기)
accountEntity.withdraw(withDrawFormDto.getAmount());
// 쿼리 던지기
accountRepository.updateById(accountEntity);
// 6 - 거래 내역 등록 History 객체 생성
History history = new History();
history.setAmount(withDrawFormDto.getAmount());
// 출금 시점에 해당 계좌에 잔액을 입력
history.setWBalance(accountEntity.getBalance());
history.setDBalance(null);
history.setWAccountId(accountEntity.getId());
history.setDAccountId(null);
int resultRowCount = historyRepository.insert(history);
if(resultRowCount != 1) {
throw new CustomRestfulException("정상적으로 처리되지 않았습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 입금 기능 로직 생각해보기
@Transactional
public void updateAccountDeposit(DepositFormDto depositFormDto) {
// 1. 계좌 존재 여부 확인 --> select query
// 2. 입금 처리 --> update query
// 3. 거래 내역 등록 --> insert query history_tb
// 4. 트랜잭션 처리
Account accountEntity = accountRepository.findByNumber(depositFormDto.getDAccountNumber());
// 1. 계좌 존재 여부 확인
if(accountEntity == null) {
throw new CustomRestfulException("해당 계좌가 없습니다.", HttpStatus.BAD_REQUEST);
}
// 2. 입금 처리
// 객체 상태값 변경 처리
accountEntity.deposit(depositFormDto.getAmount());
accountRepository.updateById(accountEntity);
// 3. 거래 내역 등록
History history = new History();
history.setAmount(depositFormDto.getAmount());
history.setWBalance(null);
// 현재 입금 되었을 때 잔액을 기록
history.setDBalance(accountEntity.getBalance());
history.setWAccountId(null);
history.setDAccountId(accountEntity.getId());
int resultRowCount = historyRepository.insert(history);
if(resultRowCount != 1) {
throw new CustomRestfulException("정상적으로 처리되지 않았습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 이체 로직
// 1. 출금 계좌 존재 여부 확인 --> select query
// 2. 입금 계좌 존재 여부 확인 --> select query
// 3. 출금 계좌 본인 소유 확인 --> 객체 선택값 확인
// 4. 출금 계좌 비밀번호 확인 --> transferFormDto(비밀번호) / 모델(비밀번호)
// 5. 출금 계좌 잔액 여부 확인 --> DTO / 모델
// 6. 출금 계좌 잔액 - update
// 7. 이체 계좌 잔액 - update
// 8. 거래 내역 등록 - insert
// 9. 트랙잭션 처리
@Transactional
public void updateAccountTransfer(TransferFormDto transferFormDto, Integer id) {
// 1
Account withdrawAccountEntity = accountRepository.findByNumber(transferFormDto.getWAccountNumber());
if(withdrawAccountEntity == null) {
throw new CustomRestfulException("출금 계좌가 존재하지 않습니다", HttpStatus.INTERNAL_SERVER_ERROR);
}
// 2
Account depositAccountEntity = accountRepository.findByNumber(transferFormDto.getDAccountNumber());
if(depositAccountEntity == null) {
throw new CustomRestfulException("입금 계좌가 존재하지 않습니다", HttpStatus.INTERNAL_SERVER_ERROR);
}
// 3
withdrawAccountEntity.checkOwner(id);
// 4
withdrawAccountEntity.checkPassword(transferFormDto.getWAccountPassword());
// 5
withdrawAccountEntity.checkBalance(transferFormDto.getAmount());
// 6
withdrawAccountEntity.withdraw(transferFormDto.getAmount());
// 7
depositAccountEntity.deposit(transferFormDto.getAmount());
// 8
accountRepository.updateById(withdrawAccountEntity);
accountRepository.updateById(depositAccountEntity);
History history = new History();
history.setAmount(transferFormDto.getAmount());
history.setWAccountId(withdrawAccountEntity.getId());
history.setDAccountId(depositAccountEntity.getId());
history.setWBalance(withdrawAccountEntity.getBalance());
history.setDBalance(depositAccountEntity.getBalance());
int resultRowCount = historyRepository.insert(history);
if(resultRowCount != 1) {
throw new CustomRestfulException("정상 처리 되지 않았습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}