풀스택 개발자 과정 25일차

너구·2026년 6월 11일

풀스택 성장과정

목록 보기
25/79

다른 버전의 Bank Account

Account

// 잔액, 입/출금, 송금을 처리합니다

public class Account {
    
    // 잔액 (초기값 0) 정수형은 값을 할당하지 않으면 기본적으로 0이 적용
    private int balance = 0;

    // 잔액 반환
    public int getBalance() {
        return balance;
    }

    // 입금 처리 (amount: 입금액)
    public void deposit(int amount) {
        if (amount < 0) { // 입금액이 마이너스인 경우 예외를 던진다
            throw new IllegalArgumentException("마이너스 입금 시도!");
        }
        // 잔액에 입금액을 더한다
        balance += amount;
        // balance = balance + amount와 같다
    }

    // 출금처리 (amount: 출금액)
    public void withdraw(int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("마이너스 출금 시도!");
        } // 출금액이 잔액보다 큰 경우
        if (amount > balance) {
            throw new IllegalArgumentException("잔액이 부족합니다");
        }
        // 잔액에서 출금액을 뺀다
        balance -= amount; // balance = balance - amount와 같다
    }

    // 송금
    // to는 받는 사람, amount는 송금액
    public void trasfer(String to, int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("마이너스 송금시도!");
        }
        if (amount > balance) {
            throw new IllegalArgumentException("잔액이 부족합니다");
        }
        balance -= amount;
    }
}

Transaction Manager

// 거래 내역을 관리하는 객체

import java.util.ArrayList;
import java.util.List;

public class TransactionManager {
    
    // 거래 내역
    private List<String> history = new ArrayList<>();

    public TransactionManager() {}

    // 거래내역을 갱신한다 (content: 새로 추가되는 거래내역)
    public void updateHistory(String content) {
        history.add(content);
    }

    // 거래내역을 반환한다
    public List<String> getHistory() {
        return history;
    }
}

View

import java.util.List;

public class View {
    
    public View() {}

    // 메인 화면
    // balance: 잔액, message: 입/출금, 송금을 처리하고 받은 메시지
    public void showHome(int balance, String message) {

        System.out.println();
        System.out.println("나의 은행 계좌");
        if (message != null) { // 메시지가 있으면 출력
            System.out.println("!" + message);
        }
        System.out.println("------------------------------");
        System.out.println("현재 잔액: " + balance + "원");
        System.out.println("------------------------------");
        System.out.println("예금: save 금액|출금: take 금액|송금: send 아이디 금액");
    }

    // 거래내역 페이지
    public void showHistory(List<String> history) { // history: 출력할 거래내역 데이터

        System.out.println();
        System.out.println("거래내역");
        System.out.println("------------------------------");
        if (history.size() < 1) { // 거래내역이 없을 때
            System.out.println("거래내역이 없습니다");
        } else { // 거래내역 출력
            for (int i = 0; i < history.size(); i++) {
                // i + 1: 인덱스로 넘버 만들기
                System.out.println((i + 1) + ". " + history.get(i));
            }
        }
        System.out.println("-------------------------------");
        System.out.println("돌아가기: home|종료: exit");
    }
}

Controller

import java.util.List;

public class Controller {
    
    private Account account;
    private TransactionManager transactionManager;
    private View view;

    public Controller(Account account, TransactionManager transactionManager, View view) {
        this.account = account;
        this. transactionManager = transactionManager;
        this. view = view;
    }

    // 메인화면 처리
    public void home() {
        // 화면 렌더링은 뷰에게 맡긴다
        view.showHome(account.getBalance(), null);
    }

    public void saveMoney(int amount) { // amount는 입금액
        // 실제 입금처리는 Account 객체를 거쳐야합니다
        account.deposit(amount);

        // 거래 내역 처리는 transactionManager에게 맡긴다
        transactionManager.updateHistory(amount + "원 입금");

        // 성공 메시지
        String message = "성공적으로 " + amount + "원 입금했습니다";
        // 현재 잔액과 성공 메시지를 뷰에게 전달
        view.showHome(account.getBalance(), message);
    }

    // 출금 처리
    public void takeMoney(int amount) {
        // 실제 출금처리는 Account 객체에게 맡긴다
        account.withdraw(amount);

        // 거래내역처리는 transactionManager에게 맡긴다
        transactionManager.updateHistory(amount + "원 출금");

        // 성공 메시지
        String message = "성공적으로 " + amount + "원 출금했습니다";
        
        view.showHome(account.getBalance(), message);
    }

    // 송금 담당
    public void sendMoney(String to, int amount) { // to: 받는 사람

        // 실제 송금처리는 Account에게 맡기고
        account.trasfer(to, amount);

        // 거래 내역 처리는 매니저에게 맡기고
        String format = String.format("%d원 송금 (%s)", amount, to);
        transactionManager.updateHistory(format);

        // 성공 메시지
        String message = String.format("성공적으로 %s에게 %원 송금하였습니다", to, amount);
        view.showHome(account.getBalance(), message);
    }

    // 거래내역 처리
    public void showHistory() {
        // 매니저에게 거래내역 데이터를 요청하고
        List<String> history = transactionManager.getHistory();
        // 화면에 출력
        view.showHistory(history);
    }
}

CommandHandler

import java.util.Scanner;

public class CommandHandler {
    
    private Controller controller;
    private Scanner scanner;

    public CommandHandler(Controller controller, Scanner scanner) {
        this.controller = controller;
        this.scanner = scanner;
    }

    public void run() {
        // 초기 화면을 출력
        controller.home();

        while (true) {
            // 사용자 입력을 받고 처리하는 부분
            System.out.print("bankapp> ");
            String userInput = scanner.nextLine().trim();
            String[] parsed = userInput.split(" ");
            String command = parsed[0]; // 명령어

            try {
                switch (command) {
                    case "home":
                        controller.home();
                        break;
                    case "save":
                        controller.saveMoney(Integer.parseInt(parsed[1]));
                        break;
                    case "take":
                        controller.saveMoney(Integer.parseInt(parsed[1]));
                        break;
                    case "send":
                        controller.sendMoney(parsed[1], Integer.parseInt(parsed[2]));
                        break;
                    case "history":
                        controller.showHistory();
                        break;
                    case "exit":
                        System.out.println("bye");
                        return;
                    default:
                        System.out.println("유효하지 않은 명령어입니다");
                        break;
                }
            } catch (Exception e) { // 앱에서 발생하는 모든 예외를 처리하는 부분
                System.out.println("오류: " + e.getMessage());
            }
        }
    }
}

BankAccountApp

import java.util.Scanner;

public class BankAccountApp {
    public static void main(String[] args) throws Exception {
        
        //Controller
        Account account = new Account();
        TransactionManager transactionManager = new TransactionManager();
        View view = new View();
        Controller controller = new Controller(account, transactionManager, view);

        //CommandHandler
        Scanner scanner = new Scanner(System.in);
        CommandHandler commandHandler = new CommandHandler(controller, scanner);

        commandHandler.run();
        scanner.close();
    }
}

이번 Bank Account 프로젝트를 진행하면서 이전에 직접 만들었던 BankAccount 프로젝트와 비교해볼 수 있었다. 처음에 만들었던 버전은 계좌 정보와 거래 내역을 모두 하나의 BankAccount 클래스에서 관리했다. 기능 구현 자체는 간단하고 이해하기 쉬웠지만, 클래스 하나가 너무 많은 역할을 담당하고 있다는 점이 아쉬웠다.

반면 이번 버전은 Account와 TransactionManager를 분리하여 각각의 역할을 명확하게 나누었다. Account는 잔액과 입금, 출금, 송금만 담당하고, TransactionManager는 거래 내역만 관리한다. 이를 통해 하나의 클래스가 하나의 책임만 가지도록 설계할 수 있다는 점을 배울 수 있었다.

또한 Controller가 Account와 TransactionManager 사이에서 중간 역할을 수행하면서 MVC 패턴의 흐름을 더 명확하게 이해할 수 있었다. 사용자의 요청은 CommandHandler가 받고, 실제 데이터 처리는 Model(Account, TransactionManager)이 담당하며, 화면 출력은 View가 담당하는 구조가 자연스럽게 연결되는 것을 확인할 수 있었다.

이번 프로젝트를 통해 단순히 기능을 구현하는 것보다 역할을 분리하고 객체 간의 책임을 명확하게 나누는 것이 유지보수와 확장성에 얼마나 중요한지 배울 수 있었다. 앞으로 더 큰 프로젝트를 만들 때도 기능 구현뿐만 아니라 클래스 설계와 구조를 먼저 고민하는 습관을 가져야겠다고 느꼈다.

0개의 댓글