이번 편의 목표: 기초 문법을 결합해 메모리에서 동작하는 프로그램을 완성합니다.

이제 문법별 예제를 하나의 프로그램으로 합치겠습니다. 먼저 파일 저장은 빼고, 입력·검증·목록 조회·합계를 연결합니다. 작은 버전을 완성한 다음 저장 기능을 붙이면 오류가 생긴 위치를 구분하기 쉽습니다.

기능보다 규칙을 먼저 적는다

이번 버전은 항목과 원 단위 금액을 입력받습니다. 항목은 비어 있으면 안 되고 금액은 양의 정수여야 합니다. 메뉴는 `1` 입력, `2` 조회, `0` 종료입니다. 날짜와 파일 저장은 다음 편에서 추가합니다. 종료하면 이번 자료는 사라집니다.

Python
def read_expense():
    item = input("항목: ").strip()
    if not item:
        raise ValueError("항목을 입력해 주세요.")
    try:
        amount = int(input("금액(원): "))
    except ValueError:
        raise ValueError("금액에는 정수를 입력해 주세요.") from None
    if amount <= 0:
        raise ValueError("금액은 0보다 커야 합니다.")
    return {"item": item, "amount": amount}

def show_expenses(expenses):
    if not expenses:
        print("아직 기록이 없습니다.")
        return
    for number, row in enumerate(expenses, start=1):
        print(f"{number}. {row['item']} / {row['amount']:,}원")
    print(f"합계: {sum(row['amount'] for row in expenses):,}원")

def main():
    expenses = []
    while True:
        choice = input("1: 입력 / 2: 조회 / 0: 종료 > ").strip()
        if choice == "0":
            print("종료합니다. 이번 기록은 파일에 저장되지 않습니다.")
            break
        elif choice == "1":
            try:
                row = read_expense()
            except ValueError as error:
                print(error)
            else:
                expenses.append(row)
                print("기록했습니다.")
        elif choice == "2":
            show_expenses(expenses)
        else:
            print("메뉴의 번호를 입력해 주세요.")

if __name__ == "__main__":
    main()

이 코드 전체가 한 파일입니다. ZIP의 `examples/ch23.py`를 실행해도 됩니다. 먼저 `2`를 입력해 빈 목록 메시지를 확인하세요. 다음에 `1 → 커피 → 4500`, 다시 `1 → 점심 → 8000`으로 입력한 뒤 `2`로 조회하면 합계가 `12,500원`입니다.

함수 세 개의 책임

`read_expense()`는 한 건의 입력과 검증을 담당합니다. 실패하면 예외를 발생시키고, 성공하면 딕셔너리를 반환합니다. `show_expenses()`는 받은 목록을 표시하고 수정하지 않습니다. `main()`은 메뉴와 전체 기록 목록의 수명을 관리합니다.

입력에 성공하기 전에 목록에 빈 딕셔너리를 먼저 추가하지 않습니다. 검증이 끝난 한 건만 `append()`하므로 실패한 입력이 기록에 남지 않습니다. `except` 뒤의 `else`는 입력 과정에 예외가 없을 때만 실행됩니다.

`raise ... from None`은 사용자에게 보여 줄 변환 오류를 새 설명으로 바꾸면서 원래 예외 연결 표시를 생략합니다. 개발 과정에서 원인을 추적해야 할 때는 원래 예외 정보를 유지하는 편이 도움이 될 수 있습니다. 여기서는 예상한 입력 오류에만 적용했습니다.

직접 해 보기

메뉴에 `3: 고액 조회`를 추가하고 만 원 이상인 기록만 출력하세요. 기존 조회 함수를 재사용하면 됩니다.

해설

`main()`의 조회 분기 다음에 아래 가지를 추가합니다. 이 조각은 기존 `if` 묶음 안에 넣습니다.

elif choice == "3": selected = [row for row in expenses if row["amount"] >= 10000] show_expenses(selected)

메뉴 안내문도 함께 수정하세요. `커피 4500`, `책 18000`을 입력한 뒤 고액 조회하면 책 한 건과 합계 `18,000원`이 나와야 합니다. 프로그램의 구조가 분리되어 있으면 기능 추가도 작은 변경으로 끝납니다.