1. depositForm.jsp 주소 확인 및 name 속성 확인 
2. depositForm 코드 확인 
3. AccountController 입금 페이지 인증 검사 추가 
4. AccountController 입금 기능 데이터 파싱 및 인증,유효성 검사 처리  
5. AccountService 입금 기능 처리 구현 
6. 거래 내역 등록 처리

depositForm.jsp 코드 수정 및 name 속성 확인

<%@ 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">
		<form action="/account/deposit-proc" method="post">
			<div class="form-group">
				<label for="amount">입금 금액:</label>
				<input type="text" class="form-control" placeholder="입금금액을 입력하시오" id="amount" name="amount">
			</div>
			<div class="form-group">
				<label for="dAccountNumber">입금계좌번호:</label>
				<input type="text" class="form-control" placeholder="입금계좌번호" id="dAccountNumber" name="dAccountNumber">
			</div>
			<button type="submit" class="btn btn-primary">입금</button>
		</form>
	</div>
	<br>
</div>

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

입금 페이지 인증 검사 추가

AccountController

// 입금 페이지
@GetMapping("/deposit")
public String deposit() {
	
	if((User)session.getAttribute(Define.PRINCIPAL) == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	return "/account/depositForm";
}

DepositFormDto 코드 확인

package com.tenco.bank.dto;

import lombok.Data;

@Data
public class DepositFormDto {
	private Long amount;
	private String dAccountNumber; 
}

AccountController 입금 기능 추가

@PostMapping("/deposit-proc")
public String depositProc(DepositFormDto depositFormDto) {
	
	User principal = (User)session.getAttribute(Define.PRINCIPAL);
	if(principal == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	
	if(depositFormDto.getAmount() == null) {
		throw new CustomRestfullException("금액을 입력해 주세요", HttpStatus.BAD_REQUEST);
	}
	
	if(depositFormDto.getAmount().longValue() <= 0) {
		throw new CustomRestfullException("입금 금액이 0원 이하일 수 없습니다.", HttpStatus.BAD_REQUEST);
	}
	
	if(depositFormDto.getDAccountNumber() == null || 
			depositFormDto.getDAccountNumber().isEmpty()) {
		throw new CustomRestfullException("계좌 번호를 입력하세요", HttpStatus.BAD_REQUEST);
	}
	
	accountService.updateAccountDeposit(depositFormDto); 
	return "redirect:/account/list";
}

Account 모델 클래스 확인

package com.tenco.bank.repository.model;

import java.sql.Timestamp;
import lombok.Data;

/**
 * 모델 클래스 (Value Object 역할만 하는것은 아니다) 
 */
@Data
public class Account {
	
	private Integer id;
	private String number; 
	private String password; 
	private Long balance; 
	private Integer userId; 
	private Timestamp createdAt; 
	
	public void withdraw(Long amount) {
		this.balance -= amount;
	}
	
	public void deposit(Long amount) {
		this.balance += amount;
	}
	
}

AccountService 입금 기능 추가

// 입금 처리 기능 
// 트랜잭션 처리 
// 1 계좌 존재 여부 확인 -> select 
// 2 입금 처리 -> update 
// 3 거래 내역 등록 처리 -> insert 
@Transactional
public void updateAccountDeposit(DepositFormDto depositFormDto) {
	
	Account accountEntity = accountRepository.findByNumber(depositFormDto.getDAccountNumber());
	if(accountEntity == null) {
		throw new CustomRestfullException("해당 계좌가 존재하지 않습니다", HttpStatus.INTERNAL_SERVER_ERROR);
	}
	
	// 객체 상태값 변경 
	accountEntity.deposit(depositFormDto.getAmount());
	accountRepository.updateById(accountEntity);
	
	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 CustomRestfullException("정상 처리가 되지 않았습니다", HttpStatus.INTERNAL_SERVER_ERROR);
	}
}

MyBatis 확인

package com.tenco.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

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

@Mapper // mybatis 연결 처리 
public interface AccountRepository {
	
	public int insert(Account account);
	public int updateById(Account account); 
	public int deleteById(int id); 
	public List<Account> findAll();   
	public Account findById(int id);
	public List<Account> findByUserId(Integer userId);
	public Account findByNumber(String number); 
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tenco.bank.repository.interfaces.AccountRepository"> 
	
	<insert id="insert">
		insert into account_tb(number, password, balance, user_id, created_at)
		values(#{number}, #{password}, #{balance}, #{userId}, now())
	</insert>	
	
	<update id="updateById">
		update account_tb set number = #{number}, password = #{password},
			balance = #{balance}, user_id = #{userId} where id = #{id}
	</update>
	
	<delete id="deleteById">
		delete from account_tb where id = #{id}
	</delete>
	
	<select id="findById" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where id = #{id}
	</select>
	
	<select id="findAll" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb 
	</select>
	
	<select id="findByUserId" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where user_id = #{userId} 
	</select>
	
	<select id="findByNumber"  resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where number = #{number}
	</select>
	
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
	namespace="com.tenco.bank.repository.interfaces.HistoryRepository">

	<insert id="insert"
		parameterType="com.tenco.bank.repository.model.History">
		insert into
		history_tb(
		amount, w_balance, d_balance,
		w_account_id, d_account_id
		)
		values(
		#{amount}, #{wBalance}, #{dBalance},
		#{wAccountId}, #{dAccountId}
		)
	</insert>

	<update id="updateById"
		parameterType="com.tenco.bank.repository.model.History">
		update history_tb
		set amount = #{amount},
		w_balance =	#{wBalance},
		d_balace = #{dBalance},
		w_account_id = #{wAccountId},
		d_account_id = #{dAccountId}
		where id = #{id}
		
	</update>

	<delete id="deleteById" parameterType="int">
		delete from history_tb
		where id = #{id}
	</delete>
	
	<select id="findById" resultType="com.tenco.bank.repository.model.History">
		select * from history_tb where id = #{id}
	</select>

	<select id="findAll" resultType="com.tenco.bank.repository.model.History">
		select * from history_tb
	</select>


</mapper>
1. 초기 데이터 수정 처리 
2. withdrawForm.jsp 주소 설계 및 name 속성 확인 
3. AccountController 출금 인증 검사, 유효성 검사 추가 
4. AccountService 출금 기능 구현 및 트랜잭션 처리 
5. MyBatis 맵퍼 확인 및 쿼리 확인

출금 페이지 화면

데이터 초기화 값 수정

INSERT INTO user_tb(username, password, fullname, created_at) values('길동', '1234',
'고', now());
INSERT INTO user_tb(username, password, fullname, created_at) values('둘리', '1234',
'애기공룡', now());
INSERT INTO user_tb(username, password, fullname, created_at) values('콜', '1234',
'마이', now());
INSERT INTO user_tb(username, password, fullname, created_at) values('홍아', '1234',
'항아', now());


INSERT INTO account_tb(number, password, balance, user_id, created_at)
values('1111', '1234', 1200, 1, now());
INSERT INTO account_tb(number, password, balance, user_id, created_at)
values('2222', '1234', 2200, 2, now());
INSERT INTO account_tb(number, password, balance, user_id, created_at)
values('3333', '1234', 0, 3, now());



-- 이체 내역을 기록 ( 1번 계좌에서 2번 계좌로 100원을 이체 한다) 
INSERT INTO history_tb(amount, w_balance, d_balance, 
					   w_account_id, d_account_id, created_at)
VALUES (100, 800, 1200, 1, 2, now());

-- 출금 내역 ( 1번계좌에서 100원을 출금 처리 )
INSERT INTO history_tb(amount, w_balance, d_balance, 
					   w_account_id, d_account_id, created_at)
VALUES (100, 700, null, 1, null, now());


-- 입금 내역 (1번 계좌에 500원 입금 처리 )
INSERT INTO history_tb(amount, w_balance, d_balance, 
					   w_account_id, d_account_id, created_at)
VALUES (500, null, 1200, null, 1, now());

-- 입금 내역 (2번 계좌에 1000원 입금 처리 )
INSERT INTO history_tb(amount, w_balance, d_balance, 
					   w_account_id, d_account_id, created_at)
VALUES (100, null, 2200, null, 2, now());

출금 페이지 인증 처리 코드 추가

AccountController

// 출금 페이지 
@GetMapping("/withdraw")
public String withdraw() {
	
	User principal = (User)session.getAttribute(Define.PRINCIPAL);
	if(principal == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	
	return "/account/withdrawForm";
}

AccountController

... 생략 
	
// 출금 처리 기능
@PostMapping("/withdraw-proc")
public String withdrawProc(WithdrawFormDto withdrawFormDto) {
	
	User principal = (User)session.getAttribute(Define.PRINCIPAL);
	if(principal == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	if(withdrawFormDto.getAmount() == null) {
		throw new CustomRestfullException("금액을 입력 하세요", HttpStatus.BAD_REQUEST);
	}
	
	if( withdrawFormDto.getAmount().longValue() <= 0) {
		throw new CustomRestfullException("출금액이 0원 이하일 수는 없습니다", HttpStatus.BAD_REQUEST);
	}
	
	if(withdrawFormDto.getWAccountNumber() == null || 
			withdrawFormDto.getWAccountNumber().isEmpty()) {
		throw new CustomRestfullException("계좌 번호를 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	
	if(withdrawFormDto.getWAccountPassword() == null || 
			withdrawFormDto.getWAccountPassword().isEmpty()) {
		throw new CustomRestfullException("계좌 비밀 번호를 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	
	accountService.updateAccountWithdraw(withdrawFormDto, principal.getId());
	
	return "redirect:/account/list";
}
	
}

AccountService 출금 기능 추가

... 생략 
	
// 출금 기능 로직 고민해 보기  
// 1. 계좌 존재 여부 확인 -> select query
// 2. 본인 계좌 여부 확인 ->
// 3. 계좌 비번 확인 
// 4. 잔액 여부 확인 
// 5. 출금 처리 -> update query 
// 6. 거래 내역 등록 -> insert query
// 7. 트랜잭션 처리 
@Transactional
public void updateAccountWithdraw(WithdrawFormDto withdrawFormDto, Integer principalId) {
	Account accountEntity = accountRepository.findByNumber(withdrawFormDto.getWAccountNumber());
	// 1 
	if(accountEntity == null) {
		throw new CustomRestfullException("해당 계좌가 없습니다", HttpStatus.BAD_REQUEST);
	}
	// 2
	if(accountEntity.getUserId() != principalId) {
		throw new CustomRestfullException("본인 소유 계좌가 아닙니다", HttpStatus.UNAUTHORIZED);
	}
	// 3   
	if(accountEntity.getPassword().equals(withdrawFormDto.getWAccountPassword()) == false) {
		throw new CustomRestfullException("출금 계좌 비밀번호가 틀렸습니다", HttpStatus.UNAUTHORIZED);
	}
	// 4 
	if(accountEntity.getBalance() < withdrawFormDto.getAmount()) {
		throw new CustomRestfullException("계좌 잔액이 부족 합니다", HttpStatus.BAD_REQUEST);
	}
	// 5 (모델 객체 상태값 변경 처리) 
	accountEntity.withdraw(withdrawFormDto.getAmount());
	accountRepository.updateById(accountEntity);
	// 6 거래 내역 등록 
	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 CustomRestfullException("정상 처리 되지 않았습니다", HttpStatus.INTERNAL_SERVER_ERROR);
	}
}

... 생략

Account 모델 클래스 확인

package com.tenco.bank.repository.model;

import java.sql.Timestamp;

import lombok.Data;

/**
 * 모델 클래스 (Value Object 역할만 하는것은 아니다) 
 */
@Data
public class Account {
	
	private Integer id;
	private String number; 
	private String password; 
	private Long balance; 
	private Integer userId; 
	private Timestamp createdAt; 
	
	public void withdraw(Long amount) {
		this.balance -= amount;
	}
	
	public void deposit(Long amount) {
		this.balance += amount;
	}
	
	// 패스워드 체크
	// 잔액 여부 확인 (출금시) 
	// 계좌 소유자 확인 
}

History 모델 클래스 확인

package com.tenco.bank.repository.model;

import java.sql.Timestamp;

import lombok.Data;

@Data
public class History {
	
	private Integer id;
	private Long amount;
	private Long wBalance;
	private Long dBalance;
	private Integer wAccountId;
	private Integer dAccountId;
	private Timestamp createdAt;
}

AccountRepository 기능 추가

@Mapper // mybatis 연결 처리 
public interface AccountRepository {
	
	... 생략 
	public Account findByNumber(String number); 
}

HistoryRepository 코드 확인

@Mapper // 반드시 지정하기 ! 
public interface HistoryRepository {
	
	public int insert(History history); 
	... 생략 	
}

account.xml

<select id="findByNumber"  resultType="com.tenco.bank.repository.model.Account">
	select * from account_tb where number = #{number}
</select>

history.xml

<insert id="insert"
	parameterType="com.tenco.bank.repository.model.History">
	insert into
	history_tb(
	amount, w_balance, d_balance,
	w_account_id, d_account_id
	)
	values(
	#{amount}, #{wBalance}, #{dBalance},
	#{wAccountId}, #{dAccountId}
	)
</insert>

: 로직 검사 확인

 

1. 계좌 목록 쿼리 확인 및 생성  
2. AccountRepository 코드 추가 
3. AccountService 코드 추가 
4. AccountController 코드 추가 
5. account/list.jsp 코드 수정

account.xml 파일 쿼리 확인 및 추가

<select id="findByUserId" resultType="com.tenco.bank.repository.model.Account">
	select * from account_tb where user_id = #{userId} 
</select>

AccountRepository 코드 추가

package com.tenco.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

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

@Mapper // mybatis 연결 처리 
public interface AccountRepository {
	
	public int insert(Account account); 
	public int updateById(Account account); 
	public int deleteById(int id); 
	
	public List<Account> findAll();   
	public Account findById(int id);
	// 코드 추가 
	public List<Account> findByUserId(Integer userId);
}

AccountService 코드 추가

// 계좌 목록 보기 기능
@Transactional
public List<Account> readAccountList(Integer userId) {
	List<Account> list = accountRepository.findByUserId(userId);
	return list; 
}

AccountController 코드 추가

/**
 * 계좌 목록 페이지 
 * @return 목록 페이지 이동
 */
@GetMapping({"/list", "/"})
public String list(Model model) {
	
	//인증검사 처리
	User principal = (User)session.getAttribute(Define.PRINCIPAL);
	if(principal == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	
	// View 화면으로 데이터를 내려 주는 기술 
	// Model 과 ModelAndView 
	List<Account> accountList = accountService.readAccountList(principal.getId());
	if(accountList.isEmpty()) {
		model.addAttribute("accountList", null);
	} else {
		model.addAttribute("accountList", accountList);
	}
	
	return "/account/list";
}

account/list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/view/layout/header.jsp"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<div class="col-sm-8">
	<h2>나의 계좌 목록 </h2>
	<h5>어서오세요 환영합니다</h5>
	<div class="bg-light p-md-5 h-75">
	<c:choose>
		<c:when test="${accountList != null}">
			<table class="table">
			<thead>
				<tr>
					<th>계좌 번호</th>
					<th>잔액</th>
				</tr>
			</thead>
			<tbody>
				<c:forEach var="account" items="${accountList}">
				<tr>
					<td>${account.number}</td>
					<td>${account.balance}</td>
				</tr>
				</c:forEach>
			</tbody>
		</table>
		</c:when>
		<c:otherwise>
			<p>아직 생성된 계좌가 없습니다</p>
		</c:otherwise>
	</c:choose>
	</div>
	<br>
</div>

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

결과 화면

1 계좌 생성 form 확인하기 
2 계좌 생성 페이지 요청시 인증 로직 구현 
3 SaveFormDto 설계 하기 
4 AccountService 만들기 
5 account.xml 설계 
6 utils/Define 클래스 생성 코드 수정
<%@ 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">
		<form action="/account/save-proc" method="post">
			<div class="form-group">
				<label for="number">계좌 번호:</label>
				<input type="text" class="form-control" placeholder="생성 계좌 번호 입력" id="number" name="number" value="5555">
			</div>
			<div class="form-group">
				<label for="password">계좌 비밀번호:</label>
				<input type="password" class="form-control" placeholder="계좌 비밀번호 입력" id="password" name="password" value="1234">
			</div>
			<div class="form-group">
				<label for="balance">입금 금액:</label>
				<input type="text" class="form-control" placeholder="입금 금액" id="balance" name="balance" value="2000">
			</div>
			
			<button type="submit" class="btn btn-primary">계좌 생성</button>
		</form>
	</div>
	<br>
</div>

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

AccountController 기능 추가

// 계좌 생성 페이지 
@GetMapping("/save")
public String save() {
	// 인증 검사 처리 
	User user = (User)session.getAttribute("principal");
	if(user == null) {
		throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
	}
	return "/account/saveForm";
}

AccountService 생성

package com.tenco.bank.service;

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.tenco.bank.dto.SaveFormDto;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.repository.interfaces.AccountRepository;
import com.tenco.bank.repository.model.Account;

@Service // Ioc 대상 + 싱글톤으로 관리 
public class AccountService {
	
	@Autowired // DI 
	private AccountRepository accountRepository;
	
	/**
	 * 계좌 생성 기능 
	 * @param saveFormDto
	 * @param principalId
	 */
	@Transactional
	public void createAccount(SaveFormDto saveFormDto, Integer principalId) {
		
		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 CustomRestfullException("계좌 생성 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

AccountRepository 확인

@Mapper // mybatis 연결 처리 
public interface AccountRepository {
	
	public int insert(Account account); 
	public int updateById(Account account); 
	public int deleteById(int id); 
	
	public List<Account> findAll();   
	public Account findById(int id); 
}

account.xml 파일 확인

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tenco.bank.repository.interfaces.AccountRepository"> 
	
	<insert id="insert">
		insert into account_tb(number, password, balance, user_id, created_at)
		values(#{number}, #{password}, #{balance}, #{userId}, now())
	</insert>	
	
	<update id="updateById">
		update account_tb set number = #{number}, password = #{password},
			balance = #{balance}, user_id = #{userId} where id = #{id}
	</update>
	
	<delete id="deleteById">
		delete from account_tb where id = #{id}
	</delete>
	
	<select id="findById" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where id = #{id}
	</select>
	
	<select id="findAll" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb 
	</select>
	
	
</mapper>

AccountController 코드 완성

package com.tenco.bank.controller;

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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tenco.bank.dto.SaveFormDto;
import com.tenco.bank.handler.exception.CustomPageException;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.AccountService;
import com.tenco.bank.utils.Define;

@Controller
@RequestMapping("/account")
public class AccountController {
	
	@Autowired
	private HttpSession session;
	
	@Autowired 
	private AccountService accountService;
	
	/**
	 * 계좌 목록 페이지 
	 * @return 목록 페이지 이동
	 */
	@GetMapping({"/list", "/"})
	public String list() {
		
		//인증검사 처리
		User principal = (User)session.getAttribute(Define.PRINCIPAL);
		if(principal == null) {
			throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
		}
		return "/account/list";
	}
	
	// 출금 페이지 
	@GetMapping("/withdraw")
	public String withdraw() {
		return "/account/withdrawForm";
	}
	
	
	// 입금 페이지
	@GetMapping("/deposit")
	public String deposit() {
		return "/account/depositForm";
	}
	
	// 이체 페이지 
	@GetMapping("/transfer")
	public String transfer() {
		return "/account/transferForm";
	}
	
	// 계좌 생성 페이지 
	@GetMapping("/save")
	public String save() {
		// 인증 검사 처리 
		User user = (User)session.getAttribute(Define.PRINCIPAL);
		if(user == null) {
			throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
		}
		return "/account/saveForm";
	}
	
	/**
	 * 계좌 생성 
	 * 인증검사
	 * 유효성 검사 처리 - 0원 입력 가능, 마이너스 입력 불가  
	 * @param saveFormDto
	 * @return 계좌 목록 페이지 
	 */
	@PostMapping("/save-proc")
	public String saveProc(SaveFormDto saveFormDto) {
		
		User user = (User)session.getAttribute(Define.PRINCIPAL);
		if(user == null) {
			throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
		}
		
		// 유효성 검사 하기  
		if(saveFormDto.getNumber() == null || 
				saveFormDto.getNumber().isEmpty()) {
			throw new CustomRestfullException("계좌번호를 입력해주세요", HttpStatus.BAD_REQUEST);
		}
		
		if(saveFormDto.getPassword() == null || 
				saveFormDto.getPassword().isEmpty()) {
			throw new CustomRestfullException("계좌 비밀번호를 입력해주세요", HttpStatus.BAD_REQUEST);
		}
		
		if(saveFormDto.getBalance() == null || 
				saveFormDto.getBalance() < 0 ) {
			throw new CustomRestfullException("잘못된 금액 입니다", HttpStatus.BAD_REQUEST);
		}
		
		accountService.createAccount(saveFormDto, user.getId());
		
		return "redirect:/account/list";
	}
	
	
	// 계좌 상세 보기 페이지 
	@GetMapping("/detail")
	public String detail() {
		return "";
	}
	
	
}

현재 계좌번호 유니크 제약 해결 하지 않은 상태

Define 클래스 생성하기

package com.tenco.bank.utils;

public class Define {
	// 서버 프로그램에서 상태값을 변경할 수 있는 변수는 절대 지양
	public final static String PRINCIPAL = "principal";
}
1. signIn.jsp 파일 확인 
2. UserController 로그인 처리 기능 확인 
3. UserService 기능 추가 
4. UserRepository 메소드 추가 
5. user.xml 쿼리 추가 
6. account/list 인증 처리 해보기  
7. header.jsp 메뉴 처리 (jstl 선언 및 사용) 
8. 로그아웃 기능 구현 
9. 사용자 정의 클래스 추가 연습 
10. ExceptionHandler 코드 추가

signIn.jsp 파일 확인

<%@ 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">
		<form action="/user/sign-in" method="post">
			<div class="form-group">
				<label for="username">User name:</label>
				<input type="text" class="form-control" placeholder="Enter username" id="username" name="username" value="길동">
			</div>
			<div class="form-group">
				<label for="password">Password:</label>
				<input type="password" class="form-control" placeholder="Enter password" id="password" name="password" value="1234">
			</div>
			<button type="submit" class="btn btn-primary">로그인</button>
		</form>
	</div>
	<br>
</div>

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

UserController 로그인 기능 추가

/**
	 * 로그인 처리 
	 * @param signInFormDto
	 * @return 메인 페이지 이동 (수정 예정)
	 * 생각해보기 
	 * GET 방식 처리는 브라우저 히스토리에 남겨지기 때문에 
	 * 예외적으로 로그인 POST 방식으로 처리 한다. (보안) 
	 */
	@PostMapping("/sign-in")
	public String signInProc(SignInFormDto signInFormDto) {
		// 1. 유효성 검사 (인증 검사가 더 우선)
		if(signInFormDto.getUsername() == null || 
				signInFormDto.getUsername().isEmpty()) {
			throw new CustomRestfullException("username을 입력하시오", HttpStatus.BAD_REQUEST);
		}
		if(signInFormDto.getPassword() == null || 
				signInFormDto.getPassword().isEmpty()) {
			throw new CustomRestfullException("password을 입력하시오", HttpStatus.BAD_REQUEST);
		}
		User principal = userService.signIn(signInFormDto);
		session.setAttribute("principal", principal);
		
		return "/account/list";
	}

UserService 기능 추가

/**
	 * 로그인 서비스 처리 
	 * @param signInFormDto
	 * @return userEntity 응답
	 */
	public User signIn(SignInFormDto signInFormDto) {
		User userEntity = userRepository.findByUsernameAndPassword(signInFormDto);
		if(userEntity == null) {
			throw new CustomRestfullException("아이디 혹은 비번이 틀렸습니다",
					HttpStatus.INTERNAL_SERVER_ERROR);
		}
		return userEntity;
	}​

기능 추가

@Mapper  // MyBatis 의존 설정 함(build.gradle 파일) 
public interface UserRepository {
	// 코드 수정하기 
	public int insert(SignUpFormDto signUpFormDto);
	public int updateById(User user); 
	public int deleteById(Integer id);
	public User findById(Integer id); 
	public List<User> findAll();
	
	// 추가 작업 
	public User findByUsernameAndPassword(SignInFormDto signInFormDto);
}

mapper설정
user.xml 기능 구현

<select id="findByUsernameAndPassword" 
	resultType="com.tenco.bank.repository.model.User">
	select * from user_tb 
	where username = #{username} 
	and password = #{password}
</select>

account/list 인증 처리 해보기

package com.tenco.bank.controller;

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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tenco.bank.handler.exception.CustomPageException;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.repository.model.User;

@Controller
@RequestMapping("/account")
public class AccountController {
	
	@Autowired
	private HttpSession session;
	
	/**
	 * 계좌 목록 페이지 
	 * @return 목록 페이지 이동
	 */
	@GetMapping({"/list", "/"})
	public String list() {
		
		//인증검사 처리
		User principal = (User)session.getAttribute("principal");
		if(principal == null) {
			throw new CustomRestfullException("인증된 사용자가 아닙니다.", 
							HttpStatus.UNAUTHORIZED);
		}
		return "/account/list";
	}
	
	// 출금 페이지 
	@GetMapping("/withdraw")
	public String withdraw() {
		return "/account/withdrawForm";
	}
	
	
	// 입금 페이지
	@GetMapping("/deposit")
	public String deposit() {
		return "/account/depositForm";
	}
	
	// 이체 페이지 
	@GetMapping("/transfer")
	public String transfer() {
		return "/account/transferForm";
	}
	
	// 계좌 생성 페이지 
	@GetMapping("/save")
	public String save() {
		return "/account/saveForm";
	}
	
	// 계좌 상세 보기 페이지 
	@GetMapping("/detail")
	public String detail() {
		return "";
	}
}

account/list 인증 처리 해보기

package com.tenco.bank.controller;

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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tenco.bank.handler.exception.CustomPageException;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.repository.model.User;

@Controller
@RequestMapping("/account")
public class AccountController {
	
	@Autowired
	private HttpSession session;
	
	/**
	 * 계좌 목록 페이지 
	 * @return 목록 페이지 이동
	 */
	@GetMapping({"/list", "/"})
	public String list() {
		
		//인증검사 처리
		User principal = (User)session.getAttribute("principal");
		if(principal == null) {
			throw new CustomRestfullException("인증된 사용자가 아닙니다.", 
							HttpStatus.UNAUTHORIZED);
		}
		return "/account/list";
	}
	
	// 출금 페이지 
	@GetMapping("/withdraw")
	public String withdraw() {
		return "/account/withdrawForm";
	}
	
	
	// 입금 페이지
	@GetMapping("/deposit")
	public String deposit() {
		return "/account/depositForm";
	}
	
	// 이체 페이지 
	@GetMapping("/transfer")
	public String transfer() {
		return "/account/transferForm";
	}
	
	// 계좌 생성 페이지 
	@GetMapping("/save")
	public String save() {
		return "/account/saveForm";
	}
	
	// 계좌 상세 보기 페이지 
	@GetMapping("/detail")
	public String detail() {
		return "";
	}
}

header.jsp 메뉴 처리 (jstl 선언 및 사용)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>	
<html lang="en">
<head>
<title>My Bank</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script
	src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script
	src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="/css/styles.css">
<style>

</style>
</head>
<body>

	<div class="jumbotron text-center banner--img" style="margin-bottom: 0">
		<h1 class="m--title">My Bank</h1>
		<img alt="sample" src="https://picsum.photos/300/200">
	</div>

	<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
		<a class="navbar-brand" href="#">MENU</a>
		<button class="navbar-toggler" type="button" data-toggle="collapse"
			data-target="#collapsibleNavbar">
			<span class="navbar-toggler-icon"></span>
		</button>
		<div class="collapse navbar-collapse" id="collapsibleNavbar">
			<ul class="navbar-nav">
				<li class="nav-item"><a class="nav-link" href="#">Home</a></li>
				<c:choose>
					<c:when test="${principal != null}">
						<li class="nav-item"><a class="nav-link" href="/user/logout">로그아웃</a></li>						
					</c:when>
					<c:otherwise>
						<li class="nav-item"><a class="nav-link" href="/user/sign-in">로그인</a></li>
						<li class="nav-item"><a class="nav-link" href="/user/sign-up">회원가입</a></li>
					</c:otherwise>
				</c:choose>
			</ul>
		</div>
	</nav>

	<div class="container" style="margin-top: 30px">
		<div class="row">
			<div class="col-sm-4">
				<h2>About Me</h2>
				<h5>Photo of me:</h5>
				<div class="m--profile"></div>
				<p style="padding: 8px 0">자라나는 코린이에 은행 관리 시스템 입니다</p>
				<h3>Some Links</h3>
				<p>Lorem ipsum dolor sit ame.</p>
				<ul class="nav nav-pills flex-column">
					<li class="nav-item"><a class="nav-link" href="/account/save">계좌생성</a></li>
					<li class="nav-item"><a class="nav-link"  href="/account/list">계좌목록</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/withdraw">출금</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/deposit">입금</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/transfer">이체</a></li>
					<li class="nav-item"><a class="nav-link disabled" href="#">My Info</a>
					</li>
				</ul>
				<hr class="d-sm-none">
			</div>

로그아웃 처리

@GetMapping("/logout")
public String logout() {
	session.invalidate();
	return "redirect:/user/sign-in";
}

사용자 정의 클래스 만들기 추가 연습

package com.tenco.bank.handler.exception;

import org.springframework.http.HttpStatus;
import lombok.Getter;

@Getter
public class UnAuthorizedException extends RuntimeException {

	private HttpStatus status;

	public UnAuthorizedException(String message, HttpStatus status) {
		super(message);
		this.status = status;
	}
}

ExceptionHandler 처리

package com.tenco.bank.handler;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.handler.exception.UnAuthorizedException;

/**
 * 예외 발생 시 (Json, XML)  
 * 데이터를 내려 줄 수 있다
 */
@RestControllerAdvice // IoC 대상 + AOP 기반 
public class MyRestfullExceptionHandler {
	
	@ExceptionHandler(Exception.class)
	public void exception(Exception e) {
		System.out.println(e.getClass().getName());
		System.out.println(e.getMessage());
	}
	
	// 사용자 정의 예외 클래스 활용
	@ExceptionHandler(CustomRestfullException.class)
	public String basicException(CustomRestfullException e) {
		StringBuffer sb = new StringBuffer();
		sb.append("<script>");
		// 반드시 마지막에 ; 콜론을 붙어서 사용하자.
		sb.append("alert('"+ e.getMessage() +"');"); 
		sb.append("history.back();");
		sb.append("</script>");
		return sb.toString();
	}
	
	@ExceptionHandler(UnAuthorizedException.class)
	public String unAuthorizedException(UnAuthorizedException e) {
		StringBuffer sb = new StringBuffer();
		sb.append("<script>");
		// 반드시 마지막에 ; 콜론을 붙어서 사용하자.
		sb.append("alert('"+ e.getMessage() +"');"); 
		sb.append("location.href='/user/sign-in';");
		sb.append("</script>");
		return sb.toString();
	}
	
}

 

1. dto 설계 하기 및 개념 확인
2. UserController 설계 및 유효성 검사
3. UserService 설계와 DI 사용 
4. service layer에 이해 
5. 트랜잭션에 이해 (insert, update, delete 트랜잭션 처리 , 복잡한 select 트랜잭션 처리)

DTO 패키지와 Model 패키지를 분리하는 것이 바람직하다.

DTO(Data Transfer Object)와 모델 클래스를 분리하여 패키지를 만드는 것이 좋습니다. 
그 이유는 다음과 같습니다:

1. **코드의 가독성 및 유지 보수성**: DTO와 모델 클래스를 별도의 패키지로 구분함으로써 
코드의 구조가 명확해지고, 관련 클래스를 찾기 쉬워집니다. 이를 통해 유지 보수성이 향상됩니다.

2. **객체의 역할 구분**: 모델 클래스는 데이터베이스의 테이블 구조를 표현하는 반면, 
DTO는 클라이언트와 서버 간의 데이터 전송을 담당합니다. 이 두 객체의 역할이 다르기 때문에, 
별도의 패키지로 구분하는 것이 좋습니다.
3. **유연한 변경**: 애플리케이션의 요구 사항이 변경되면 DTO와 모델 클래스의 변경이 독립적으로 이루어질 수 있습니다. 
이렇게 구조를 분리해 놓으면, 한쪽의 변경이 다른 쪽에 영향을 미치는 것을 최소화할 수 있습니다.

따라서, 코드의 가독성, 유지 보수성 및 유연성을 높이기 위해 DTO 패키지와 모델 패키지를 따로 구성하는 것이 좋습니다.

서비스레이어를 만드는게 좋습니다.(만드는 이유)

코드 결합도를 낮추고 재 사용성을 높이기 위함 ( 트랜잭션 처리 ) - 서비스를 사용하는 이유

Web Layer : Rest API를 제공하며, Client 중심의 로직 적용
Business Layer : 내부 정책에 따른 logic를 개발하며, 주로 핵심업무 부분을 개발
Data Layer : 데이터 베이스 및 외부와의 연동 처리

**서비스는 일반적으로 비즈니스 로직을 수행하는 클래스입니다.**

비즈니스 로직(Business Logic)이란, 애플리케이션에서 수행되는 비즈니스 규칙이나 
프로세스 등을 의미합니다. 즉, 애플리케이션의 핵심적인 업무 처리를 담당하는 로직을 말합니다

예를 들어, 온라인 쇼핑몰에서 주문을 처리하는 기능은 주문 상태를 변경하거나, 
재고 수량을 업데이트하는 등의 처리 과정을 수행합니다. 이러한 과정들이 바로 비즈니스 로직에 해당됩니다.
또한, 비즈니스 로직은 애플리케이션에서 수행되는 모든 로직 중에서 가장 중요한 부분으로, 
애플리케이션의 성격과 특성에 따라 다양한 형태로 구현될 수 있습니다.

Spring Boot에서 비즈니스 로직은 주로 서비스(Service) 레이어에서 구현됩니다. 
이를 통해 비즈니스 로직을 분리하여 컨트롤러(Controller)나 뷰(View)와 같은 다른 레이어와 분리하여 개발하고,
애플리케이션의 유지 보수성과 확장성을 높일 수 있고 트랜잭션 관리도 가능 합니다.

트랜잭션에 이해

트랜잭션(Transaction)은 데이터베이스(Database)에서 수행되는 작업의 단위를 의미합니다. 
즉, 데이터베이스에서 데이터를 읽거나 쓰는 작업을 수행할 때, 한 번에 실행되어야 하는 일련의 작업을 의미합니다.


트랜잭션은 ACID라는 성질을 갖습니다. ACID는 원자성(Atomicity), 일관성(Consistency), 
고립성(Isolation), 지속성(Durability)의 약어입니다. 
이 중에서 원자성은 트랜잭션이 성공하거나 실패할 때, 모든 작업이 반영되거나, 
아무 것도 반영되지 않아야 한다는 것을 의미합니다. 
즉, 트랜잭션에서 한 번에 처리되어야 하는 작업들은 모두 완전히 수행되거나, 
아예 수행되지 않아야 합니다.

예를 들어, 은행에서 계좌 이체를 처리하는 작업을 수행할 때, 계좌에서 출금하는 작업과 입금하는 작업은
원자성을 가져야 합니다. 즉, 출금 작업이 완전히 수행되지 않았을 경우, 입금 작업도 반영되지 않아야 합니다.

Spring Boot에서는 트랜잭션을 처리하기 위해 @Transactional 어노테이션을 제공합니다.
이 어노테이션을 사용하면, 트랜잭션 범위 내에서 실행되는 모든 작업이 원자성을 갖도록 보장할 수 있습니다. 
또한, 트랜잭션의 고립성, 일관성, 지속성 등의 ACID 성질을 보장하기 위해 다양한 설정 옵션을 제공합니다.

UserController

/**
	 * 회원 가입 처리 
	 * @param signUpFormDto
	 * @return 리다이렉트 로그인 페이지
	 */
@PostMapping("/sign-up")
public String signUpProc(SignUpFormDto signUpFormDto) {
	
	// 1. 유효성 검사  
	if(signUpFormDto.getUsername() == null 
			|| signUpFormDto.getUsername().isEmpty()) {
		throw new CustomRestfullException("username을 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	if(signUpFormDto.getPassword() == null 
			|| signUpFormDto.getPassword().isEmpty()) {
		throw new CustomRestfullException("password을 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	if(signUpFormDto.getFullname() == null 
			|| signUpFormDto.getFullname().isEmpty()) {
		throw new CustomRestfullException("fullname을 입력해주세요", HttpStatus.BAD_REQUEST);
	} 
	// 서비스 호출 
	userService.signUp(signUpFormDto);
	
	return "redirect:/user/sign-in";
}
package com.tenco.bank.service;

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.tenco.bank.dto.SignUpFormDto;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.repository.interfaces.UserRepository;

@Service // IoC 대상 
public class UserService {
	
	@Autowired //DI 처리 (객체 생성시 의존 주의 처리)
	private UserRepository userRepository;
	
	@Transactional 
	// 메서드 호출이 시작될 때 트랜잭션에 시작
	// 메스드 종료시 트랜잭션 종료 (commit) 
	public void signUp(SignUpFormDto signUpFormDto) {
		// SignUpFormDto 
		// User 
		int result = userRepository.insert(signUpFormDto);
		if(result != 1) {
			throw new CustomRestfullException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

코드 수정 1차 - (추후 수정 예정)

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.tenco.bank.dto.SignUpFormDto;
import com.tenco.bank.repository.model.User;

@Mapper  // MyBatis 의존 설정 함(build.gradle 파일) 
public interface UserRepository {
	// 코드 수정하기 
	public int insert(SignUpFormDto signUpFormDto);
	public int updateById(User user); 
	public int deleteById(Integer id);
	public User findById(Integer id); 
	public List<User> findAll();
}

트랜잭션 1차 연습

START TRANSACTION;

insert into user_tb(username, password, fullname)
values ('john', '1234', 'kim');

insert into user_tb(username, password, fullname)
values ('Mike', '222', 'Lee');

ROLLBACK;

트랜잭션 2차 연습

/*
	계좌 간 이체 
    계좌 A의 잔액은 3,000원 입니다. 
    계좌 B의 잔액은 0원 입니다. 
    계좌 A에서 B로 3000원 이체 하기 
*/
-- 테스트를 위한 설정 
UPDATE account_tb SET balance = 3000 where id = 1; 
UPDATE account_tb SET balance = 0 where id = 2; 

select * from account_tb;
select * from history_tb; 

START TRANSACTION;
UPDATE account_tb set balance = balance - 3000 WHERE id = 1; 
UPDATE account_tb set balance = balance + 3000 WHERE id = 2; 

INSERT INTO history_tb(amount, w_balance, 
	d_balance, w_account_id, d_account_id)
VALUES(3000, 
	(SELECT balance from account_tb where id = 1),
    (SELECT balance from account_tb where id = 2),
    1, 2);    

COMMIT;

 

'Spring boot > spring boot 앱 만들어 보기 2 단원' 카테고리의 다른 글

bank app - 계좌 생성하기  (0) 2023.04.19
bank app - 로그인 처리  (0) 2023.04.19
bank app - Exception Handler  (0) 2023.04.18
bank app - 화면 구현(2)  (0) 2023.04.17
bank app 화면 구현(1)  (0) 2023.04.17
1. Exception Handler 처리 
2. @ControllerAdvice 와 @RestControllerAdvice 에 차이점을 이해 하자 
3. 사용자 정의 예외 클래스를 만들어 보자. 
4. 직접 예외 발생 시켜 보기 (사용자 정의 예외 클래스 활용)
5. 예외 페이지 생성 및 활용 (errorPage.jsp 생성)

Exception Handler 

스프링 MVC 의 중요한 구성 요소 중에 하나로 AOP 기반에 개념 입니다. 
구현하는 방법 중 하나는 @ExcetptionHandler 어노테이션을 사용하고 이 어노테이션은 
예외가 발생한 메서드를 구현하고, 처리할 예외 타입을 지정합니다.

@ControllerAdvice 와 @RestControllerAdvice 에 차이점

@ControllerAdvice와 @RestControllerAdvice는 모두 예외 처리를 담당하는 클래스에 
붙는 어노테이션으로 스프링에서 예외 처리를 담당하는 핵심적인 요소 중 하나입니다. 
그러나 두 어노테이션에 차이점은 반환 타입과 기본 응답 형태 입니다. 

@ControllerAdvice 어노테이션은 View 렌더링을 위해 ModelAndView 형태로 객체를 반환하도록 
기본 설정이 되어 있습니다. 즉 요청에 대한 응답이 view 형태로 전달되며, 
JSP 와 같은 템플릿 엔진을 사용할 수 있습니다. 


반면에 @RestControllerAdvice RESTfull 웹 서비스에서 사용하기 적합한 형태로 
응답 처리가 되며 응답에 형태는 JSON, XML 형태로 반환이 됩니다.

패키지 구조 

사용자 정의 클래스 만들어 보기

package com.tenco.bank.handler.exception;

import org.springframework.http.HttpStatus;

import lombok.Getter;

// Ioc 대상이 아님 (필요할 때 직접 new 처리) 
@Getter
public class CustomRestfullException extends RuntimeException {
	
	private HttpStatus status;
	// throw new CustomRestfullException('페이지 못 찾음', 404);
	public CustomRestfullException(String message, HttpStatus status) {
		super(message);
		this.status = status;
	}
	
}

 

package com.tenco.bank.handler.exception;

import org.springframework.http.HttpStatus;

public class CustomPageException extends RuntimeException {
	
	private HttpStatus status;
	
	public CustomPageException(String message, HttpStatus status) {
		super(message);
		this.status = status;
	}
	
}

ExceptionHandler 처리 하기 - @RestControllerAdvice 사용

package com.tenco.bank.handler;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.tenco.bank.handler.exception.CustomRestfullException;

/**
 * 예외 발생 시 (Json, XML)  
 * 데이터를 내려 줄 수 있다
 */
@RestControllerAdvice // IoC 대상 + AOP 기반 
public class MyRestfullExceptionHandler {
	
	@ExceptionHandler(Exception.class)
	public void exception(Exception e) {
		System.out.println(e.getClass().getName());
		System.out.println(e.getMessage());
	}
	
	// 사용자 정의 예외 클래스 활용
	@ExceptionHandler(CustomRestfullException.class)
	public String basicException(CustomRestfullException e) {
		StringBuffer sb = new StringBuffer();
		sb.append("<script>");
		// 반드시 마지막에 ; 콜론을 붙어서 사용하자.
		sb.append("alert('"+ e.getMessage() +"');"); 
		sb.append("history.back();");
		sb.append("</script>");
		return sb.toString();
	}
}

ExceptionHandler 처리 하기 - @ControllerAdvice 사용

package com.tenco.bank.handler;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import com.tenco.bank.handler.exception.CustomPageException;

/**
 * 
 * View 렌더링을 위해 ModelAndView 
 * 객체를 반환하도록 기본 설정 되어 있다. 
 * 예외 page를 리턴하도록 활용 예정 
 */
@ControllerAdvice
public class MyPageExceptionHandler {
	
	// 사용자 정의 클래스 활용
	@ExceptionHandler(CustomPageException.class)
	public ModelAndView handleRuntimePageException(CustomPageException e) {
		// ModelAndView 활용 방법 
		ModelAndView modelAndView = new ModelAndView("errorPage");
		modelAndView.addObject("statusCode", HttpStatus.NOT_FOUND.value());
		modelAndView.addObject("message", e.getMessage());
		return modelAndView;
	}
}

예외 발생 시켜 보기 1,2

public class AccountController {

	/**
	 * 계좌 목록 페이지 
	 * @return 목록 페이지 이동
	 */
	@GetMapping({"/list", "/"})
	public void list() {		
		// todo 예외 테스트 - 삭제 예정  
		//throw new CustomRestfullException("인증되지 않은 사용자 입니다.", HttpStatus.UNAUTHORIZED);
		throw new CustomPageException("페이지를 찾을 수 없습니다", HttpStatus.NOT_FOUND);
		
		// return "/account/list";
	}
... 생략 
}

예외 페이지 생성 및 활용 (errorPage.jsp 생성) /WEB-INF/view/errorPage.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isErrorPage="true" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>에러 페이지</h1>
	<p>에러 코드 : ${statusCode}</p>
	<p>에러 메서지 : ${message}</p>
	
</body>
</html>
1. UserController 생성 
2. 회원 가입, 로그인 화면 구현하기 
3. AccountController 생성 
4. 계좌목록,출금,입금,계좌생성,계좌상세보기 페지이 만들기

UserController 파일 생성

package com.tenco.bank.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tenco.bank.dto.SignInFormDto;
import com.tenco.bank.dto.SignUpFormDto;

@Controller
@RequestMapping("/user")
public class UserController {
	
	// http://localhost:8080/user/sign-up
	@GetMapping("/sign-up")
	public String signUp() {
		// prefix 
		// subfix 
		return "/user/signUp";
	}
	

	/**
	 * 회원 가입 처리 
	 * @param signUpFormDto
	 * @return 리다이렉트 로그인 페이지
	 */
	@PostMapping("/sign-up")
	public String signUpProc(SignUpFormDto signUpFormDto) {
		
		return "redirect:/user/sign-in";
	}
	
	/**
	 * 로그인 폼
	 * @return 로그인 페이지 
	 */
	@GetMapping("/sign-in")
	public String signIn() {
		
		return "/user/signIn";
	}
	
	/**
	 * 로그인 처리 
	 * @param signInFormDto
	 * @return 메인 페이지 이동 (수정 예정)
	 */
	@PostMapping("/sign-in")
	public String signInProc(SignInFormDto signInFormDto) {
		
		// todo 변경 예정 
		return "/test/main";
	}
	

}

회원 가입 화면 구현 (signUp.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/view/layout/header.jsp"%>

<!-- todo main 영역으로 지정 예정  -->
<div class="col-sm-8">
	<h2>회원 가입</h2>
	<h5>어서오세요 환영합니다</h5>
	<div class="bg-light p-md-5 h-75">
		<form action="/user/sign-up" method="post">
			<div class="form-group">
				<label for="username">User name:</label>
				<input type="text" class="form-control" placeholder="Enter username" id="username" name="username">
			</div>
			<div class="form-group">
				<label for="password">Password:</label>
				<input type="password" class="form-control" placeholder="Enter password" id="password" name="password">
			</div>
			<div class="form-group">
				<label for="fullname">Fullname:</label>
				<input type="text" class="form-control" placeholder="Enter fullname" id="fullname" name="fullname">
			</div>
			<button type="submit" class="btn btn-primary">Submit</button>
		</form>
	</div>
	<br>
</div>

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

로그인 화면 구현 처리 (signIn.jsp )

<%@ 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">
		<form action="/user/sign-in" method="post">
			<div class="form-group">
				<label for="username">User name:</label>
				<input type="text" class="form-control" placeholder="Enter username" id="username" name="username">
			</div>
			<div class="form-group">
				<label for="password">Password:</label>
				<input type="password" class="form-control" placeholder="Enter password" id="password" name="password">
			</div>
			<button type="submit" class="btn btn-primary">Submit</button>
		</form>
	</div>
	<br>
</div>

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

AccountController 파일 생성

package com.tenco.bank.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/account")
public class AccountController {

	/**
	 * 계좌 목록 페이지 
	 * @return 목록 페이지 이동
	 */
	@GetMapping({"/list", "/"})
	public String list() {
		return "/account/list";
	}
	
	// 출금 페이지 
	@GetMapping("/withdraw")
	public String withdraw() {
		return "/account/withdrawForm";
	}
	
	
	// 입금 페이지
	@GetMapping("/deposit")
	public String deposit() {
		return "/account/depositForm";
	}
	
	// 이체 페이지 
	@GetMapping("/transfer")
	public String transfer() {
		return "/account/transferForm";
	}
	
	// 계좌 생성 페이지 
	@GetMapping("/save")
	public String save() {
		return "/account/saveForm";
	}
	
	// 계좌 상세 보기 페이지 
	@GetMapping("/detail")
	public String detail() {
		return "";
	}
	
	
}

계좌 목록 페이지 구성 (list.jsp)

<%@ 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">
		<table class="table">
			<thead>
				<tr>
					<th>계좌 번호</th>
					<th>잔액</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td>1111</td>
					<td>500원</td>
				</tr>
				<tr>
					<td>2222</td>
					<td>200원</td>
				</tr>
			</tbody>
		</table>
	</div>
	<br>
</div>

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

출금 계좌 화면 구현 (withdraw.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ include file="/WEB-INF/view/layout/header.jsp"%>
<!-- todo main 영역으로 지정 예정 -->
<div class="col-sm-8">
	<h2>출금 페이지(인증)</h2>
	<h5>어서오세요 환영합니다.</h5>
	<div class="bg-light p-md-5 h-75">
		<form action="#" method="post">
			<div class="form-group">
				<label for="amount">출금 금액:</label> 
				<input type="text" class="form-control" placeholder="출금 금액을 입력하시오" id="amount" name="amount">
			</div>
			<div class="form-group">
				<label for="wAccountNumber">출금 계좌 번호:</label> 
				<input type="text" class="form-control" placeholder="출금 계좌번호" id="wAccountNumber" name="wAccountNumber">
			</div>				
			<div class="form-group">
				<label for="wAccountPassword">출금 계좌 비밀번호:</label> 
				<input type="password" class="form-control" placeholder="출금 계좌비밀번호" id="wAccountPassword" name="wAccountPassword">
			</div>				
			<button type="submit" class="btn btn-primary">출금</button>
		</form>
	</div>
	<br>
	<br>
</div>

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

입금 계좌 화면 구성(depositForm.jsp)

<%@ 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">
		<form action="#" method="post">
			<div class="form-group">
				<label for="number">계좌 번호:</label>
				<input type="text" class="form-control" placeholder="생성 계좌 번호 입력" id="number" name="number">
			</div>
			<div class="form-group">
				<label for="password">계좌 비밀번호:</label>
				<input type="password" class="form-control" placeholder="계좌 비밀번호 입력" id="password" name="password">
			</div>
			<div class="form-group">
				<label for="balance">입금 금액:</label>
				<input type="text" class="form-control" placeholder="출금계좌비밀번호" id="balance" name="balance">
			</div>
			
			<button type="submit" class="btn btn-primary">계좌 생성</button>
		</form>
	</div>
	<br>
</div>

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

이체 계좌 화면 구성 (transferForm.jsp )

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ include file="/WEB-INF/view/layout/header.jsp"%>
<!-- todo main 영역으로 지정 예정 -->
<div class="col-sm-8">
	<h2>이체 페이지(인증)</h2>
	<h5>어서오세요 환영합니다.</h5>
	<div class="bg-light p-md-5 h-75">
		<form action="#" method="post">
			<div class="form-group">
				<label for="amount">이체 금액:</label> 
				<input type="text" class="form-control" placeholder="이체 금액을 입력하시오" id="amount" name="amount">
			</div>
			<div class="form-group">
				<label for="wAccountNumber">출금 계좌 번호:</label> 
				<input type="text" class="form-control" placeholder="출금 계좌번호" id="wAccountNumber" name="wAccountNumber">
			</div>				
			<div class="form-group">
				<label for="dAccountNumber">이체할 계좌 번호:</label> 
				<input type="text" class="form-control" placeholder="이체 계좌번호" id="dAccountNumber" name="dAccountNumber">
			</div>				
			<div class="form-group">
				<label for="wAccountPassword">출금 계좌 비밀번호:</label> 
				<input type="password" class="form-control" placeholder="출금 계좌비밀번호" id="wAccountPassword" name="wAccountPassword">
			</div>				
			<button type="submit" class="btn btn-primary">이체</button>
		</form>
	</div>
	<br>
	<br>
</div>

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

계좌 생성(saveForm.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ include file="/WEB-INF/view/layout/header.jsp"%>
<!-- todo main 영역으로 지정 예정 -->
<div class="col-sm-8">
	<h2>계좌 생성 페이지(인증)</h2>
	<h5>어서오세요 환영합니다.</h5>
	<div class="bg-light p-md-5 h-75">
		<form action="#" method="post">
			<div class="form-group">
				<label for="number">계좌 번호:</label> 
				<input type="text" class="form-control" placeholder="생성할 계좌 번호 입력" id="number" name="number">
			</div>
			<div class="form-group">
				<label for="password">계좌 비밀번호:</label> 
				<input type="password" class="form-control" placeholder="계좌 비밀번호 입력" id="password" name="password">
			</div>				
			<div class="form-group">
				<label for="balance">입금 금액:</label> 
				<input type="text" class="form-control" placeholder="입금 금액" id="balance" name="balance">
			</div>				
			<button type="submit" class="btn btn-primary">계좌 생성</button>
		</form>
	</div>
	<br>
	<br>
</div>

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

header.jsp 주소 연결

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<html lang="en">
<head>
<title>My Bank</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script
	src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script
	src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="/css/styles.css">
<style>

</style>
</head>
<body>

	<div class="jumbotron text-center banner--img" style="margin-bottom: 0">
		<h1 class="m--title">My Bank</h1>
		<img alt="sample" src="https://picsum.photos/300/200">
	</div>

	<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
		<a class="navbar-brand" href="#">MENU</a>
		<button class="navbar-toggler" type="button" data-toggle="collapse"
			data-target="#collapsibleNavbar">
			<span class="navbar-toggler-icon"></span>
		</button>
		<div class="collapse navbar-collapse" id="collapsibleNavbar">
			<ul class="navbar-nav">
				<li class="nav-item"><a class="nav-link" href="#">Home</a></li>
				<li class="nav-item"><a class="nav-link" href="/user/sign-in">로그인</a></li>
				<li class="nav-item"><a class="nav-link" href="/user/sign-up">회원가입</a></li>
			</ul>
		</div>
	</nav>

	<div class="container" style="margin-top: 30px">
		<div class="row">
			<div class="col-sm-4">
				<h2>About Me</h2>
				<h5>Photo of me:</h5>
				<div class="m--profile"></div>
				<p style="padding: 8px 0">자라나는 코린이에 은행 관리 시스템 입니다</p>
				<h3>Some Links</h3>
				<p>Lorem ipsum dolor sit ame.</p>
				<ul class="nav nav-pills flex-column">
					<li class="nav-item"><a class="nav-link" href="/account/save">계좌생성</a></li>
					<li class="nav-item"><a class="nav-link"  href="/account/list">계좌목록</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/withdraw">출금</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/deposit">입금</a></li>
					<li class="nav-item"><a class="nav-link" href="/account/transfer">이체</a></li>
					<li class="nav-item"><a class="nav-link disabled" href="#">My Info</a>
					</li>
				</ul>
				<hr class="d-sm-none">
			</div>

 

+ Recent posts