Spring boot
[Spring boot] Bank App 만들어 보기 - 18. 계좌 생성(유효성, 인증검사 중 누가 먼저 일까?)
ekkkang
2025. 3. 5. 15:43
💡 학습 목표
1. account/save.jsp 파일 생성 및 AccountController 생성 및 구현
2. AccountSaveDTO 설계 하기
3. 계좌 생성 기능 만들기
파싱이란?
💡 파싱(parsing)은 주어진 데이터(예: 문서, 파일, 문자열 등)를 분석하여 구조를 이해하고, 필요한 정보를 추출 또는 변환하는 과정을 의미합니다. 프로그래밍에서는 주로 원시 데이터를 원하는 형태로 가공하기 위해 사용되며, 예를 들어 HTML 문서에서 특정 데이터를 추출하거나, JSON 문자열을 객체로 변환하는 등의 작업이 파싱에 해당합니다.
클라이언트 측에서 데이터를 서버로 보내면 그 값을 분석해서 원하는 객체로 변경을 해 주는지 동작 흐름을 다시 한번더 조사해보세요
1. account/save.jsp 파일 생성 및 AccountController 생성 및 구현
: signUp.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(메인 영역) -->
<div class="col-sm-8">
<h2>계좌 생성(인증)</h2>
<h5>Bank App 오신 걸 환영합니다.</h5>
<form action="/account/save" method="post" >
<div class="form-group">
<label for="number">Account Number : </label>
<input type="text" name="number" class="form-control" placeholder="Enter number" id="number" value="5555">
</div>
<div class="form-group">
<label for="pwd">계좌 비밀번호 :</label>
<input type="password" name="password" class="form-control" placeholder="Enter password" id="pwd" value="1234">
</div>
<div class="form-group">
<label for="balance">Deposit Amount :</label>
<input type="number" name="balance" class="form-control" placeholder="Enter balance" id="balance" value="2000">
</div>
<div class="text-right">
<button type="submit" class="btn btn-primary">계좌 생성</button>
</div>
</form>
</div>
</div>
</div>
<!-- end of content.jsp(메인 영역) -->
<!-- footer.jsp -->
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>
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.RequestMapping;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.model.User;
import jakarta.servlet.http.HttpSession;
@Controller // IoC
@RequestMapping("/account")
public class AccountController {
// final 처리
@Autowired
private final HttpSession session;
// 생성자 의존 주입 - DI 처리
public AccountController(HttpSession session) {
this.session = session;
}
// 주소 설계 - http://localhost:8080/account/save
@GetMapping("/save")
public String savePage() {
// 1.인증 검사가 필요(account 전체 필요)
User principal = (User)session.getAttribute("principal");
if(principal == null) {
throw new UnAuthorizedException("인증된 사용자가 아닙니다",
HttpStatus.UNAUTHORIZED);
}
return "account/save";
}
}
화면 확인 - account 관련된 화면은 인증된 사용자만 접근 할 수 있습니다. 각각에 요청마다 인증 검사를 진행 하고 추후에 다른 장에서 인터셉터를 활용한 처리를 학습해 봅니다.
2. AccountSaveDTO 설계 하기
package com.tenco.bank.dto;
import com.tenco.bank.repository.model.Account;
import com.tenco.bank.repository.model.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AccountSaveDTO {
private String number;
private String password;
private Long balance; // 입금 금액
// 세션 정보에 사용자 ID를 받아서 Account 를 생성 해야 한다.
public Account toAccount(int principalId) {
return Account.builder()
.number(this.number)
.password(this.password)
.balance(this.balance)
.userId(principalId)
.build();
}
}
- @Data: Lombok 라이브러리의 @Data 어노테이션은 클래스에 대한 getter, setter, toString(), equals(), hashCode() 메서드를 자동으로 생성해줍니다.
- @NoArgsConstructor: 매개변수가 없는 기본 생성자를 생성합니다.
- @AllArgsConstructor: 모든 필드 값을 매개변수로 받는 생성자를 생성합니다.
- @Builder: 빌더 패턴을 구현합니다. 빌더 패턴을 사용하면 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서도 다른 표현 결과를 생성할 수 있습니다. 이는 코드의 가독성과 유지 보수성을 향상시킵니다.
3. 계좌 생성 기능 만들기
AccountRepository에 insert 추상 메서드가 있는지 확인 해주세요
@Mapper
public interface AccountRepository {
// 코드 확인 (계좌 생성)
public int insert(Account account);
public int updateById(Account account);
public int deleteById(Integer id);
// 계좌 조회 - 1 유저 , N 계좌
public List<Account> findAllByUserId();
public Account findByNumber(Integer id);
}
account.xml 파일 insert 쿼리 확인
<insert id="insert">
insert into account_tb(number, password, balance, user_id, created_at)
values(#{number}, #{password}, #{balance}, #{userId}, now())
</insert>
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.AccountSaveDTO;
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 {
@Autowired
private final AccountRepository accountRepository;
public AccountService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
/**
* 계좌 생성 기능
*
* @param dto
* @param pricipalId
*/
@Transactional
public void createAccount(AccountSaveDTO dto, Integer pricipalId) {
try {
accountRepository.insert(dto.toAccount(pricipalId));
} catch (DataAccessException e) {
// DB연결 및 제약 사항 위한 및 쿼리 오류
throw new DataDeliveryException("잘못된 처리 입니다", HttpStatus.INTERNAL_SERVER_ERROR);
} catch (Exception e) {
// 예외 처리 - 에러 페이지로 이동
throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
}
}
}
AccountController 에 코드 추가
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.AccountSaveDTO;
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 {
@Autowired
private final HttpSession session;
@Autowired
private final AccountService accountService;
// 생성자 의존 주입 - DI 처리
public AccountController(HttpSession session, AccountService accountService) {
this.session = session;
this.accountService = accountService;
}
// 주소 설계 - http://localhost:8080/account/save
@GetMapping("/save")
public String savePage() {
// 1.인증 검사가 필요(account 전체 필요)
User principal = (User)session.getAttribute("principal");
if(principal == null) {
throw new UnAuthorizedException("인증된 사용자가 아닙니다",
HttpStatus.UNAUTHORIZED);
}
return "account/save";
}
@PostMapping("/save")
public String saveProc(AccountSaveDTO dto) {
// 유효성 검사보다 먼저 인증검사를 먼저 하는 것이 좋습니다.
// 1. 인증검사
User principal = (User)session.getAttribute("principal");
if(principal == null) {
throw new UnAuthorizedException("로그인 먼저 해주세요",
HttpStatus.UNAUTHORIZED);
}
// 2. 유효성 검사
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);
}
accountService.createAccount(dto, principal.getId());
// TODO 추후 account/list 페이지가 만들어 지면 수정 할 예정 입니다.
return "redirect:/account/save";
}
}
결과 확인
: 동일한 번호에 계좌 생성 요청을 한 번더 해서 예외 처리가 정상적으로 동작 하는지 확인해주세요