실습: 성적 관리 스크립트
이 PART에서 배운 것들을 하나로 묶어봅니다. 함수로 기능을 분리하고, 연관 배열로 데이터를 관리하고, 01장에서 만든 lib_utils.sh의 로깅과 색상 함수를 활용합니다.
만들 프로그램은 간단한 성적 관리 시스템입니다. 학생 이름과 점수를 입력받아 저장하고, 조회하고, 통계를 계산합니다.
프로그램 구조
grade_manager.sh
├── lib_utils.sh 로드
├── 데이터: 연관 배열 (학생 이름 → 점수)
├── add_score() 성적 입력
├── show_scores() 성적 조회
├── calc_average() 평균 계산
├── show_stats() 최고점/최저점
├── show_menu() 메뉴 출력
└── main() 메인 루프
완성 코드
lib_utils.sh와 grade_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에서는 텍스트 처리의 강력한 도구들을 다룹니다.