스프링 데이터로 데이터 영속성 구현(2)

김우진·2026년 2월 23일

Spring

목록 보기
17/17
post-thumbnail

13장의 입출금 시스템 구현 확장

개요

  1. 발신인 계좌에서 지정된 금액을 인출한다.
  2. 수취인 계좌에 해당 금액을 입금한다.

Model

public class Account {

    @Id
    private long id;

    private String name;
    private BigDecimal amount;

    // getter, setter 생략
}

Repository

public interface AccountRepository extends CrudRepository<Account, Long> {

    @Query("SELECT * FROM account WHERE name = :name")
    List<Account> findAccountsByName(String name);

    @Modifying
    @Query("UPDATE account SET amount = :amount WHERE id = :id")
    void changeAmount(long id, BigDecimal amount);
}

Service

  • 변동성 있는 서비스 메서드에 @Transactional 애너테이션을 붙여 애스펙트를 사용한다.
  • Spring Data에서 기본적으로 findById(), findAll() 등의 메서드를 제공한다.
  • IterableList의 상위 타입이다.
@Service
public class TransferService {

    private final AccountRepository accountRepository;

    public TransferService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    @Transactional
    public void transferMoney(
            long idSender,
            long idReceiver,
            BigDecimal amount
    ) {
        Account sender = accountRepository.findById(idSender)
                .orElseThrow(() -> new AccountNotFoundException());

        Account receiver = accountRepository.findById(idReceiver)
                .orElseThrow(() -> new AccountNotFoundException());

        BigDecimal senderNewAmount = sender.getAmount().subtract(amount);
        BigDecimal receiverNewAmount = receiver.getAmount().add(amount);

        accountRepository.changeAmount(idSender, senderNewAmount);
        accountRepository.changeAmount(idReceiver, receiverNewAmount);
    }

    public Iterable<Account> getAllAccounts() {
        return accountRepository.findAll();
    }

    public List<Account> findAccountsByName(String name) {
        return accountRepository.findAccountsByName(name);
    }
}

Exception

public class AccountNotFoundException extends RuntimeException {
}

Controller

  • 계좌 상세 정보를 반환할 때 비필수 매개변수(required = false)로 이름을 받아 조건부로 조회한다.
@RestController
public class AccountController {

    private final TransferService transferService;

    public AccountController(TransferService transferService) {
        this.transferService = transferService;
    }

    @PostMapping("/transfer")
    public void transferMoney(
            @RequestBody TransferRequest request
    ) {
        transferService.transferMoney(
                request.getSenderAccountId(),
                request.getReceiverAccountId(),
                request.getAmount()
        );
    }

    @GetMapping("/accounts")
    public Iterable<Account> getAllAccounts(
            @RequestParam(required = false) String name
    ) {
        if (name == null) return transferService.getAllAccounts(); // 모든 계좌 반환
        else return transferService.findAccountsByName(name);     // 이름으로 필터링
    }
}

DTO

public class TransferRequest {
    private long senderAccountId;
    private long receiverAccountId;
    private BigDecimal amount;

    // getter 생략
}

0개의 댓글