- 입금 (Deposit): 사용자가 자신의 은행 계좌에 돈을 추가하는 기능입니다.
- 이체 (Transfer): 한 계좌에서 다른 계좌로 돈을 옮기는 기능으로, 출금 계좌의 잔액이 충분해야만 이체가 성공합니다.
이 기능들은 실제 은행 애플리케이션의 핵심 기능으로, 안전하고 신뢰할 수 있는 데이터 처리가 중요합니다.
인증: 로그인된 사용자만 기능을 사용할 수 있도록 보장합니다.
유효성 검사: 입력한 값(계좌 번호, 금액 등)이 유효한지 확인합니다.
계좌 검증: 계좌가 실제로 존재하는지, 그리고 사용자의 계좌인지 확인합니다.
비즈니스 로직 처리: 계좌의 잔액을 업데이트하고, 거래 내역을 저장합니다.
DB 업데이트: 계좌 정보를 데이터베이스에 반영하고 거래 내역을 기록합니다.
입금
1. DTO (Data Transfer Object) 정의하기
입금 요청 데이터를 저장하기 위해 DepositDTO 클래스를 만듭니다. 입금할 금액과 계좌 번호를 저장합니다.
package com.tenco.bank.dto;
import lombok.Data;
@Data
public class DepositDTO {
private Long amount; // 입금할 금액
private String dAccountNumber; // 입금할 계좌 번호
}
2. 컨트롤러에 입금 페이지 매핑
입금 페이지를 보여주기 위한 GET 매핑 메서드를 설정합니다.
@GetMapping("/deposit")
public String depositPage() {
// 1. 인증 검사: 사용자 정보가 세션에 있는지 확인합니다.
User principal = (User) session.getAttribute(Define.PRINCIPAL);
if (principal == null) {
throw new UnAuthorizedException(Define.ENTER_YOUR_LOGIN, HttpStatus.UNAUTHORIZED);
}
return "/account/deposit"; // 입금 페이지로 이동
}
3. 입금 요청 처리 메서드
사용자가 입금 버튼을 눌렀을 때 호출되는 POST 매핑 메서드입니다.
@PostMapping("/deposit")
public String depositProc(DepositDTO dto) {
// 1. 인증 검사
User principal = (User) session.getAttribute(Define.PRINCIPAL);
if (principal == null) {
throw new UnAuthorizedException(Define.ENTER_YOUR_LOGIN, HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
if (dto.getAmount() == null || dto.getAmount() <= 0) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
if (dto.getDAccountNumber() == null) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
// 3. 서비스 호출
accountService.updateAccountDeposit(dto, principal.getId());
return "redirect:/account/list"; // 입금 후 계좌 목록 페이지로 이동
}
4. 서비스 레이어에 입금 로직 추가
입금 로직은 서비스 레이어에서 처리됩니다. 트랜잭션 처리를 통해 모든 과정이 안정적으로 수행되도록 합니다.
@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.INTERNAL_SERVER_ERROR);
}
// 2. 본인 계좌 여부 확인
accountEntity.checkOwner(principalId);
// 3. 입금 처리: 객체 상태를 변경한 후 DB에 반영합니다.
accountEntity.deposit(dto.getAmount());
accountRepository.updateById(accountEntity);
// 4. 거래 내역 등록
History history = new History();
history.setAmount(dto.getAmount());
history.setWAccountId(null); // 입금이므로 출금 계좌는 없음
history.setDAccountId(accountEntity.getId());
history.setDBalance(accountEntity.getBalance());
int rowResultCount = historyRepository.insert(history);
if (rowResultCount != 1) {
throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
이체
1. DTO 정의
이체 기능에서는 출금 계좌, 입금 계좌, 이체 금액, 출금 계좌 비밀번호가 필요합니다.
package com.tenco.bank.dto;
import lombok.Data;
@Data
public class TransferDTO {
private Long amount; // 이체 금액
private String wAccountNumber; // 출금 계좌 번호
private String dAccountNumber; // 입금 계좌 번호
private String password; // 출금 계좌 비밀번호
}
2. 이체 페이지 매핑
이체 페이지를 보여주기 위한 GET 매핑 메서드입니다.
@GetMapping("/transfer")
public String transferPage() {
// 인증 검사
User principal = (User) session.getAttribute(Define.PRINCIPAL);
if (principal == null) {
throw new UnAuthorizedException(Define.ENTER_YOUR_LOGIN, HttpStatus.UNAUTHORIZED);
}
return "/account/transfer"; // 이체 페이지로 이동
}
3. 이체 처리 메서드
이체 버튼 클릭 시 호출되는 POST 매핑 메서드입니다.
@PostMapping("/transfer")
public String transferProc(TransferDTO dto) {
// 1. 인증 검사
User principal = (User) session.getAttribute(Define.PRINCIPAL);
if (principal == null) {
throw new UnAuthorizedException(Define.ENTER_YOUR_LOGIN, HttpStatus.UNAUTHORIZED);
}
// 2. 서비스 호출
accountService.updateAccountTransfer(dto, principal.getId());
return "redirect:/account/list"; // 이체 후 계좌 목록 페이지로 이동
}
4. 서비스 레이어에 이체 로직 추가
이체 로직은 복잡하므로 단계별로 처리합니다.
@Transactional
public void updateAccountTransfer(TransferDTO dto, Integer principalId) {
// 1. 출금 계좌 조회 및 검증
Account withdrawAccount = accountRepository.findByNumber(dto.getWAccountNumber());
if (withdrawAccount == null) {
throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.BAD_REQUEST);
}
withdrawAccount.checkOwner(principalId);
withdrawAccount.checkPassword(dto.getPassword());
withdrawAccount.checkBalance(dto.getAmount());
// 2. 입금 계좌 조회
Account depositAccount = accountRepository.findByNumber(dto.getDAccountNumber());
if (depositAccount == null) {
throw new DataDeliveryException("입금 계좌가 존재하지 않습니다.", HttpStatus.BAD_REQUEST);
}
// 3. 출금 처리
withdrawAccount.withdraw(dto.getAmount());
accountRepository.updateById(withdrawAccount);
// 4. 입금 처리
depositAccount.deposit(dto.getAmount());
accountRepository.updateById(depositAccount);
// 5. 거래 내역 등록
History history = new History();
history.setAmount(dto.getAmount());
history.setWAccountId(withdrawAccount.getId());
history.setDAccountId(depositAccount.getId());
history.setWBalance(withdrawAccount.getBalance());
history.setDBalance(depositAccount.getBalance());
int resultRowCount = historyRepository.insert(history);
if (resultRowCount != 1) {
throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
}
}