[Spring Boot] 29. 스프링부트 뱅크 v1 (11) 계좌 상세보기2

김주희's avatar
Mar 26, 2025
[Spring Boot] 29. 스프링부트 뱅크 v1 (11) 계좌 상세보기2

1. 한 방 쿼리

1. User Account

select at.balance account_balance, at.number account_number, ut.fullname account_owner from account_tb at inner join user_tb ut on at.user_id = ut.id where at.number = 1111;
notion image
notion image
문제)이걸 담을 클래스가 없음 → A라는 오브젝트 항아리에 담아서 뿌릴 수 잇음
 
 

2. 이체 출금 내역

select substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, WITHDRAW_BALANCE balance from history_tb where WITHDRAW_NUMBER=1111;
notion image
 

3. 이체 입금 내역

select substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, DEPOSIT_BALANCE balance from history_tb where DEPOSIT_NUMBER =1111;
notion image
 

4. 입출금 전체

  1. 입출금 전체 조회 그러나 남의 잔액도 뜬다
select substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, WITHDRAW_BALANCE balance, DEPOSIT_BALANCE balance from history_tb where WITHDRAW_NUMBER=1111 or DEPOSIT_NUMBER =1111;
notion image
 
  1. 나의 잔액만 노출되도록
select substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, case when WITHDRAW_NUMBER=1111 then WITHDRAW_BALANCE else DEPOSIT_BALANCE end balance from history_tb where WITHDRAW_NUMBER=1111 or DEPOSIT_NUMBER =1111;
notion image
 
  1. 입출금 type까지
select substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, case when WITHDRAW_NUMBER=1111 then WITHDRAW_BALANCE else DEPOSIT_BALANCE end balance, case when WITHDRAW_NUMBER=1111 then '출금' else '입금' end type from history_tb where WITHDRAW_NUMBER=1111 or DEPOSIT_NUMBER =1111;
notion image
문제)이걸 담을 class도 없음 → B라는 컬렉션 오브젝트 항아리에 담는다
 
사용자에게 최종적으로 전달하는 값 response DTO
사용자에게 전달받는 값 request DTO
 
A와 List<B> 둘다를 담을 C라는 항아리가 필요한거임!!!! (DTO)
 
db에서 조회하면 tb의 row랑 똑같은게 모델
모델을 화면이랑 똑같이 생긴 dto로 담아서
 
모든 통신은 자기 Object로 바꿔내야 한다.
통신 - 중간언어필요
json
 
DTO : controller-view 사이를 위한
 

5. trick join

조인 조건을 그냥 1=1 ⇒ 전부 true니까 그냥 갖다붙는다.
w_number,d_number 둘 다와 user account를 조인해야됨 ⇒ 조인 안됨
select dt.*, substr(CREATED_AT,1,16) CREATED_AT, WITHDRAW_NUMBER w_number, DEPOSIT_NUMBER d_number, AMOUNT amount, case when WITHDRAW_NUMBER=1111 then WITHDRAW_BALANCE else DEPOSIT_BALANCE end balance, case when WITHDRAW_NUMBER=1111 then '출금' else '입금' end type from history_tb ht inner join (select at.balance account_balance, at.number account_number, ut.fullname account_owner from account_tb at inner join user_tb ut on at.user_id = ut.id where at.number = 1111) dt on 1=1 where WITHDRAW_NUMBER=1111 or DEPOSIT_NUMBER =1111;
notion image
 
이제 이거를 담는 항아리 C라고 하면 얘는 컬렉션임
 
이걸 DTO방식으로 하니까 화면에서 쌀-계좌-잔액 이 3번 뿌려짐= DTO가 아니다!
 

2. DB세상 데이터를 담은 오브젝트 만들기

package com.metacoding.bankv1.account; import lombok.AllArgsConstructor; import lombok.Data; public class AccountResponse { @AllArgsConstructor @Data public static class DetailDTO { private int accountNumber; private int accountBalance; private String accountOwner; private String createdAt; private int wNumber; private int dNumber; private int amount; private int balance; private String type; } }
 

3. Repository 함수 만들기

public List<AccountResponse.DetailDTO> findAllByNumber(int number) { String sql = """ select dt.account_number, dt.account_balance, dt.account_owner, substr(created_at, 1, 16) created_at, withdraw_number w_number, deposit_number d_number, amount amount, case when withdraw_number = ? then withdraw_balance else deposit_balance end balance, case when withdraw_number = ? then '출금' else '입금' end type from history_tb ht inner join (select at.number account_number, at.balance account_balance, ut.fullname account_owner from account_tb at inner join user_tb ut on at.user_id = ut.id where at.number = ?) dt on 1=1 where deposit_number = ? or withdraw_number = ?; """; Query query = em.createNativeQuery(sql); query.setParameter(1, number); query.setParameter(2, number); query.setParameter(3, number); query.setParameter(4, number); query.setParameter(5, number); List<Object[]> obsList = query.getResultList(); List<AccountResponse.DetailDTO> detailList = new ArrayList<>(); for (Object[] obs : obsList) { AccountResponse.DetailDTO detail = new AccountResponse.DetailDTO( (int) obs[0], (int) obs[1], (String) obs[2], (String) obs[3], (int) obs[4], (int) obs[5], (int) obs[6], (int) obs[7], (String) obs[8] ); detailList.add(detail); } return detailList; }
 
 

4. 복잡한 쿼리 테스트 하기

package com.metacoding.bankv1.account; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; import java.util.List; @Import(AccountRepository.class) @DataJpaTest public class AccountRepositoryTest { @Autowired private AccountRepository accountRepository; @Test public void findAllByNumber_test() { int number = 1111; List<AccountResponse.DetailDTO> detailList = accountRepository.findAllByNumber(number); for (AccountResponse.DetailDTO detail : detailList) { System.out.println(detail); } } }
notion image
 

5. Service 구현

public List<AccountResponse.DetailDTO> 계좌상세보기(int number, String type, Integer sessionUserId) { // 1. 계좌 존재 확인 Account account = accountRepository.findByNumber(number); if (account == null) throw new RuntimeException("계좌가 존재하지 않습니다."); // 2. 계좌 주인 확인 if (!(account.getUserId().equals(sessionUserId))) { throw new RuntimeException("해당 계좌에 대한 권한이 없습니다."); } // 3. 조회해서 주기 return accountRepository.findAllByNumber(number); }
 

6. Controller 구현

@GetMapping("/account/{number}") public String detail(@PathVariable("number") int number, @RequestParam(value = "type", required = false, defaultValue = "전체") String type, HttpServletRequest request) { // 인증 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); // 로그인X시 아예 못들어오도록 List<AccountResponse.DetailDTO> detailList = accountService.계좌상세보기(number, type, sessionUser.getId()); // sessionUser.getId() : 권한 체크 request.setAttribute("models", detailList); // System.out.println("number = " + number + ", type = " + type); return "account/detail"; }
 

7. 가방에 있는 것을 화면에 출력하기

{{>layout/header}} <!--마진 : mt, mr, ml, mb (1~5) ex) mt-5--> <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <p>{{models.0.accountOwner}}님 계좌</p> <p>계좌번호 : {{models.0.accountNumber}}</p> <p>계좌잔액 : {{models.0.balance}}원</p> </div> <div class="mt-3 mb-3"> <button type="button" class="btn btn-outline-primary">전체</button> <button type="button" class="btn btn-outline-primary">입금</button> <button type="button" class="btn btn-outline-primary">출금</button> </div> <table class="table table-hover"> <thead> <tr> <th>날짜</th> <th>출금계좌</th> <th>입금계좌</th> <th>금액</th> <th>계좌잔액</th> <th>입금/출금</th> </tr> </thead> <tbody> {{#models}} <tr> <td>{{createdAt}}</td> <td>{{wNumber}}</td> <td>{{dNumber}}</td> <td>{{amount}}원</td> <td>{{balance}}원</td> <td>{{type}}</td> </tr> {{/models}} </tbody> </table> </div> {{>layout/footer}}
Share article

jay0628