Spring boot

Bank App 만들기 - 계좌 생성(유효성, 인증검사 중 무엇이 우선인가)

ryeonng 2024. 8. 13. 17:14

 

파싱이란

파싱(parsing)은 주어진 데이터(예 : 문서, 파일, 문자열 등)을 분석하여 구조를 이해하고, 필요한 정보를 추출 또는 변환하는 과정을 의미한다. 프로그래밍에서는 주로 원시 데이터를 원하는 형태로 가공하기 위해 사용되며, 예를 들어 HTML 문서에서 특정 데이터를 추출하거나, JSON 문자열을 객체로 변환하는 등의 작업이 파싱에 해당한다.

클라이언트 측에서 데이터를 서버로 보내면 그 값을 분석해서 원하는 객체로 변경을 해 주는지 동작 흐름을 다시 한 번 더 조사해보자.

 

 

account/save.jsp 파일 생성 및 AccountCountroller 생성 및 구현

<%@ 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>
	<!-- 자원의 요청은 get 방식을 이용하지만 -->
	<!-- 로그인 페이지는 보안처리 때문에 예외적으로 post방식으로 던지는 것이 좋다. -->
	<!-- 
	insert into account_tb(number, password, balance, user_id, created_at)
	 -->
	<form action="/account/save"  method="post">
		<div class="form-group">
			<label for="number">number:</label> 
			<input type="text" class="form-control" placeholder="Enter number" id="number" name="number" value="1002-1234">
		</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>
		<div class="form-group">
			<label for="balance">balance:</label> 
			<input type="number" class="form-control" placeholder="Enter balance" id="balance" name="balance" value="10000">
		</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"%>

 

SaveDTO 생성
package com.tenco.bank.dto;

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

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class SaveDTO {

	private String number;
	private String password;
	private Long balance;
	private Integer userId;
	
	public Account toAccount(Integer userId) {
			return Account.builder()
						  .number(this.number)
						  .password(this.password)
						  .balance(this.balance)
						  .userId(userId)
						  .build();
	}
	
}

 

AccountController.java 파일 생성 및 구현
package com.tenco.bank.controller;

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.SaveDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.AccountService;

import jakarta.servlet.http.HttpSession;

@Controller // IoC대상 (싱글톤으로 관리)
@RequestMapping("/account") // 대문열기 
public class AccountController {

	// 계좌 생성 화면 요청 - DI 처리
	private final HttpSession session;
	private final AccountService accountService; // 멤버변수 선언 시 final 사용하면 성능적으로 더 낫다.
	
	// @Autowired
	public AccountController(HttpSession session, AccountService accountService) {
		this.session = session;
		this.accountService = accountService;
	}
	
	/**
	 * 계좌 생성 페이지 요청
	 * 주소설계 - http://localhost:8080/account/save
	 * @return save.jsp
	 */
	@GetMapping("/save")
	public String savePage() {
		
		// 1. 인증 검사 필요(account 전체가 필요하다.)
		User principal = (User)session.getAttribute("principal");
		if(principal == null) { // 로그인 하지 않았다면
			throw new UnAuthorizedException("인증되지 않은 사용자 입니다.", HttpStatus.UNAUTHORIZED);
		} 
		return "account/save";
	}

	/**
	 * 계좌 생성 기능 요청
	 * 주소설계 - http://localhost:8080/account/save
	 * @return : 추후 계좌 목록 페이지로 이동 처리
	 */
	@PostMapping("/save")
	public String saveProc(SaveDTO dto) {
		// 1. form 데이터 추출 (파싱 전략)
		// 2. 인증 검사
		User principal = (User)session.getAttribute("principal");
		if(principal == null) { // 로그인 하지 않았다면
			throw new UnAuthorizedException("인증되지 않은 사용자 입니다.", HttpStatus.UNAUTHORIZED);
		}
		
		// 3. 유효성 검사
		if(dto.getNumber() == null || dto.getNumber().isEmpty()) {
			throw new DataDeliveryException("계좌번호를 입력해주세요", HttpStatus.BAD_REQUEST);
		}
		
		if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
			throw new DataDeliveryException("비밀번호를 입력해주세요", HttpStatus.BAD_REQUEST);
		}
		
		if(dto.getBalance() == null || dto.getBalance() <= 0) {
			throw new DataDeliveryException("계좌 잔액을 입력하세요.", HttpStatus.BAD_REQUEST);
		}
		
		// 4. 서비스 호출
		accountService.createAccount(dto, principal.getId());
		
		return "redirect:/index";
	}
	
	
}

 

AccountService 생성
package com.tenco.bank.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.tenco.bank.dto.SaveDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.repository.interfaces.AccountRepository;

@Service
public class AccountService {

	private final AccountRepository accountRepository;
	
	@Autowired // 생략 가능 - DI 처리
	public AccountService(AccountRepository accountRepository) {
		this.accountRepository = accountRepository;
	}
	
	/**
	 * 계좌 생성 기능
	 * @param dto
	 * @param id
	 */
	@Transactional // 트랜잭션 처리
	public void createAccount(SaveDTO dto, Integer principalId) {
		
		int result = 0;
		try {
			result = accountRepository.insert(dto.toAccount(principalId));
		} catch (DataAccessException e) {
			throw new DataDeliveryException("잘못된 요청입니다.", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
		}
		
		if(result == 0) {
			throw new DataDeliveryException("정상적으로 처리되지 않았습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}

	
	
}

 

결과 확인

: 동일한 번호에 계좌 생성 요청을 한번더 요청해서 예외 처리가 정상적으로 동작 하는지 확인하자.