이번 편의 목표: 제목과 연체 일수를 명령으로 받아 CSV에 남기고, 권마다 연체료를 출력합니다.

앞 편의 구조체, 슬라이스, 오류, 파일, 명령행을 한 프로그램으로 모읍니다. 기록 파일 이름은 loans.csv입니다. 한 줄이 책 한 권이고, 제목과 일수를 쉼표로 구분합니다. 제목에는 쉼표를 넣지 않습니다.

main.go를 다음으로 바꿉니다. 11편의 book 패키지를 import하지 않으면 실행에 포함되지 않습니다. 계산은 이 파일의 overdueFee를 씁니다.

Go
package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

type Loan struct {
	Title string
	Days  int
}

func overdueFee(days int) int {
	if days <= 0 {
		return 0
	}
	return days * 100
}

func parseLoan(line string) (Loan, bool) {
	line = strings.TrimSpace(line)
	if line == "" {
		return Loan{}, false
	}
	parts := strings.Split(line, ",")
	if len(parts) != 2 {
		return Loan{}, false
	}
	title := strings.TrimSpace(parts[0])
	days, err := strconv.Atoi(strings.TrimSpace(parts[1]))
	if err != nil || title == "" || days < 0 {
		return Loan{}, false
	}
	return Loan{Title: title, Days: days}, true
}

func load(path string) []Loan {
	text, err := os.ReadFile(path)
	if err != nil {
		return nil
	}
	var loans []Loan
	for _, line := range strings.Split(string(text), "\n") {
		loan, ok := parseLoan(line)
		if ok {
			loans = append(loans, loan)
		}
	}
	return loans
}

func save(path string, loans []Loan) error {
	var body strings.Builder
	for _, loan := range loans {
		fmt.Fprintf(&body, "%s,%d\n", loan.Title, loan.Days)
	}
	return os.WriteFile(path, []byte(body.String()), 0644)
}

func main() {
	path := "loans.csv"
	loans := load(path)
	args := os.Args[1:]
	if len(args) == 0 || args[0] == "list" {
		if len(loans) == 0 {
			fmt.Println("기록이 없습니다.")
		}
		for _, loan := range loans {
			fmt.Printf("%s %d일 %d원\n", loan.Title, loan.Days, overdueFee(loan.Days))
		}
		return
	}
	if args[0] != "add" || len(args) < 3 {
		fmt.Println("사용법: go run . add 제목 연체일수")
		return
	}
	days, err := strconv.Atoi(args[2])
	if err != nil || args[1] == "" || days < 0 {
		fmt.Println("사용법: go run . add 제목 연체일수")
		return
	}
	loans = append(loans, Loan{Title: args[1], Days: days})
	if err := save(path, loans); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("기록했습니다.")
}

파일이 없으면 load는 빈 목록으로 시작합니다. 잘못된 줄은 건너뜁니다. add는 목록 끝에 한 권을 붙인 뒤 파일 전체를 다시 씁니다. list와 인자가 없는 실행은 저장된 권을 출력합니다.

go run .

기록이 없습니다.

go run . add 생각나무 3

기록했습니다.

go run . list

생각나무 3일 300원

일수가 정수가 아니거나 음수이면 사용법만 출력하고 파일은 바꾸지 않습니다.

직접 해 보기

go run . add 도서관 2 를 실행한 뒤 list를 보세요. 둘째 줄은 도서관 2일 200원이어야 합니다.

해설

하루 100원이고 2일이므로 200원입니다. loans.csv에는 생각나무,3과 도서관,2가 각각 한 줄로 남습니다. 목록 순서는 추가한 순서입니다.