iBetter Books
수정

실습: 성적 관리 스크립트

이 PART에서 배운 것들을 하나로 묶어봅니다. 함수로 기능을 분리하고, 연관 배열로 데이터를 관리하고, 01장에서 만든 lib_utils.sh의 로깅과 색상 함수를 활용합니다.

만들 프로그램은 간단한 성적 관리 시스템입니다. 학생 이름과 점수를 입력받아 저장하고, 조회하고, 통계를 계산합니다.

프로그램 구조

grade_manager.sh
├── lib_utils.sh 로드
├── 데이터: 연관 배열 (학생 이름 → 점수)
├── add_score()      성적 입력
├── show_scores()    성적 조회
├── calc_average()   평균 계산
├── show_stats()     최고점/최저점
├── show_menu()      메뉴 출력
└── main()           메인 루프

완성 코드

lib_utils.shgrade_manager.sh를 같은 디렉토리에 저장합니다. lib_utils.sh는 04절(함수 라이브러리와 source)에서 만든 파일을 그대로 사용합니다.

#!/bin/bash# 새 파일: grade_manager.sh# grade_manager.sh — 학생 성적 관리 프로그램# 라이브러리 로드SCRIPT_DIR="$(dirname "$(realpath "$0")")"source "${SCRIPT_DIR}/lib_utils.sh"# === 데이터 ===# 연관 배열: 학생 이름 → 점수declare -A grades# 입력 순서를 보존하기 위한 이름 목록student_order=()# === 함수 정의 ===# 1) 성적 입력add_score() {    local name score    echo ""    read -rp "학생 이름을 입력하세요: " name    if [[ -z "$name" ]]; then        log_warn "이름을 입력하지 않았습니다."        return 1    fi    read -rp "${name}의 점수를 입력하세요 (0~100): " score    # 숫자 유효성 검사    if ! [[ "$score" =~ ^[0-9]+$ ]] || (( score < 0 || score > 100 )); then        log_error "유효하지 않은 점수입니다: $score"        return 1    fi    # 이미 있으면 덮어씀, 없으면 순서 목록에 추가    if [[ ! -v grades["$name"] ]]; then        student_order+=("$name")    fi    grades["$name"]=$score    log_info "${name}: ${score}점 저장 완료."}# 2) 성적 조회show_scores() {    if (( ${#grades[@]} == 0 )); then        log_warn "저장된 성적이 없습니다."        return    fi    echo ""    print_header "성적 목록"    printf "  %-15s %5s  %s\n" "이름" "점수" "등급"    print_separator    for name in "${student_order[@]}"; do        local score="${grades[$name]}"        local grade        # 등급 계산        if (( score >= 90 )); then            grade="A"        elif (( score >= 80 )); then            grade="B"        elif (( score >= 70 )); then            grade="C"        elif (( score >= 60 )); then            grade="D"        else            grade="F"        fi        # 점수에 따라 색상 표시        if (( score >= 90 )); then            printf "  %-15s " "$name"            print_green "$(printf '%3d점  %s' "$score" "$grade")"        elif (( score < 60 )); then            printf "  %-15s " "$name"            print_red "$(printf '%3d점  %s' "$score" "$grade")"        else            printf "  %-15s %3d점  %s\n" "$name" "$score" "$grade"        fi    done    print_separator    echo "  총 ${#grades[@]}명"}# 3) 평균 계산calc_average() {    if (( ${#grades[@]} == 0 )); then        log_warn "저장된 성적이 없습니다."        return    fi    local total=0    local count=${#grades[@]}    for name in "${!grades[@]}"; do        (( total += grades["$name"] ))    done    # Bash는 정수 나눗셈만 지원 → awk로 소수점 계산    local average    average=$(awk "BEGIN { printf \"%.2f\", $total / $count }")    echo ""    log_info "총점: ${total}점 / 학생 수: ${count}명"    log_info "평균: ${average}점"}# 4) 최고점/최저점show_stats() {    if (( ${#grades[@]} == 0 )); then        log_warn "저장된 성적이 없습니다."        return    fi    local max_score=-1    local min_score=101    local max_name=""    local min_name=""    for name in "${!grades[@]}"; do        local score="${grades[$name]}"        if (( score > max_score )); then            max_score=$score            max_name=$name        fi        if (( score < min_score )); then            min_score=$score            min_name=$name        fi    done    echo ""    print_header "최고점 / 최저점"    printf "  최고점: %-15s " "$max_name"    print_green "${max_score}점"    printf "  최저점: %-15s " "$min_name"    print_red "${min_score}점"    print_separator}# 5) 메뉴 출력show_menu() {    echo ""    print_separator    echo "  학생 성적 관리 프로그램"    print_separator    echo "  1) 성적 입력"    echo "  2) 성적 조회"    echo "  3) 평균 계산"    echo "  4) 최고점 / 최저점"    echo "  5) 종료"    print_separator    echo -n "  선택: "}# === 메인 루프 ===main() {    log_info "성적 관리 프로그램을 시작합니다."    while true; do        show_menu        read -r choice        case "$choice" in            1) add_score ;;            2) show_scores ;;            3) calc_average ;;            4) show_stats ;;            5)                echo ""                log_info "프로그램을 종료합니다."                exit 0                ;;            *)                log_warn "잘못된 선택입니다: $choice"                ;;        esac    done}main

실행 방법

chmod +x grade_manager.sh./grade_manager.sh

실행 결과 예시

프로그램을 실행하고 학생 3명의 성적을 입력한 다음 조회한 결과입니다.

[INFO]  20:00:00 성적 관리 프로그램을 시작합니다.

==================================================
  학생 성적 관리 프로그램
==================================================
  1) 성적 입력
  2) 성적 조회
  3) 평균 계산
  4) 최고점 / 최저점
  5) 종료
==================================================
  선택: 1

학생 이름을 입력하세요: 김철수
김철수의 점수를 입력하세요 (0~100): 85
[INFO]  20:00:05 김철수: 85점 저장 완료.

  선택: 1

학생 이름을 입력하세요: 이영희
이영희의 점수를 입력하세요 (0~100): 93
[INFO]  20:00:10 이영희: 93점 저장 완료.

  선택: 1

학생 이름을 입력하세요: 박민수
박민수의 점수를 입력하세요 (0~100): 52
[INFO]  20:00:15 박민수: 52점 저장 완료.

  선택: 2

==================================================
  성적 목록
==================================================
  이름             점수  등급
==================================================
  김철수            85점  B
  이영희            93점  A   ← 초록색
  박민수            52점  F   ← 빨간색
==================================================
  총 3명

  선택: 3

[INFO]  20:00:20 총점: 230점 / 학생 수: 3명
[INFO]  20:00:20 평균: 76.67점

  선택: 4

==================================================
  최고점 / 최저점
==================================================
  최고점: 이영희          93점   ← 초록색
  최저점: 박민수          52점   ← 빨간색
==================================================

  선택: 5

[INFO]  20:00:25 프로그램을 종료합니다.

코드 핵심 포인트 정리

연관 배열로 데이터 관리. grades["이름"]=점수 형태로 학생 데이터를 저장합니다. 연관 배열은 순서를 보장하지 않으므로 student_order 인덱스 배열을 별도로 관리해 입력 순서대로 출력합니다.

함수로 기능 분리. 입력, 조회, 평균, 통계, 메뉴, 메인 루프를 각각 함수로 분리했습니다. 각 함수는 한 가지 역할만 합니다. 나중에 기능을 추가하거나 수정할 때 해당 함수만 건드리면 됩니다.

lib_utils.sh 활용. log_info, log_warn, log_error, print_green, print_red, print_separator, print_header를 직접 구현 없이 가져다 씁니다. 라이브러리를 한 번 잘 만들어두면 이렇게 재사용할 수 있습니다.

입력 유효성 검사. [[ "$score" =~ ^[0-9]+$ ]]로 숫자인지 확인하고, 범위도 체크합니다. 실전 스크립트에서 사용자 입력은 항상 검증해야 합니다.

Bash 정수 나눗셈 보완. Bash의 산술 연산은 정수만 지원합니다. 소수점이 필요한 평균 계산은 awk로 처리했습니다. Bash의 한계를 인식하고 적절한 도구를 조합하는 방법입니다.

PART 05를 마쳤습니다. 함수로 코드를 정리하고, 배열로 데이터를 관리하는 방법을 익혔습니다. 다음 PART에서는 텍스트 처리의 강력한 도구들을 다룹니다.