wanted_preonboarding

Woozoo·2023년 4월 17일
0

[Whatis]

목록 보기
5/8

성적 관리 프로그램

프로젝트 이름

  • MyCreditManager

사용 언어 / 환경

  • Swift
  • Xcode 기본 템플릿 중 [macOS - Command Line Tool]

프로그램의 메뉴

  • 학생추가
  • 학생삭제
  • 성적추가(변경)
  • 성적삭제
  • 평점보기
  • 종료

프로그램 동작 조건

  • 사용자가 종료 메뉴를 선택하기 전까지는 계속해서 사용자의 입력을 받는다
  • 메뉴 선택을 포함한 모든 입력은 숫자 또는 영문으로 받는다

성적별 점수

  • A+ (4.5점) / A (4점)
  • B+ (3.5점) / B (3점)
  • C+ (2.5점) / C (2점)
  • D+ (1.5점) / D (1점)
  • F (0점)

평점

  • 각 과목의 점수 총 합을 / 과목 수로 나눈다
  • 최대 소수점 2번째 자리까지 출력
    • 예)
      • 3.75
      • 4.1
      • 2

프로그램 동작 모습

메뉴의 잘못된 입력 처리

굵은 글씨는 콘솔출력 내용, 얇은 글씨는 콘솔을 통한 입력 내용

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[스페이스바 입력인듯]
뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[6 입력]
뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[aa 입력]
뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


학생추가

  • 메뉴를 선택한 후에도 잘못 입력한 것이 있으면 처리하기
  • 이미 존재하는 학생은 다시 추가하지 않는다

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[1 입력]
추가할 학생의 이름을 입력해주세요
[스페이스바 입력]
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[1 입력]
추가할 학생의 이름을 입력해주세요
[Mickey 입력]
Mickey학생을 추가했습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[1 입력]
추가할 학생의 이름을 입력해주세요
[Mickey 입력]
Mickey은 이미 존재하는 학생입니다. 추가하지 않습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


학생 삭제

  • 메뉴를 선택한 후에도 잘못 입력한 것이 있으면 처리해 주어야합니다.
  • 없는 학생은 삭제하지 않습니다

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[2 입력]
삭제할 학생의 이름을 입력해주세요
[Mickey 입력]
Mickey 학생을 삭제하였습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[2 입력]
삭제할 학생의 이름을 입력해주세요
[Mickey 입력]
Mickey 학생을 찾지 못했습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


성적 추가

  • 메뉴를 선택한 후에도 잘못 입력한 것이 있으면 처리해 주어야합니다.
  • 없는 학생의 성적은 추가하지 않습니다.

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[3 입력]
성적을 추가할 학생의 이름, 과목 이름, 성적(A+, A, F 등)을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift A+
만약에 학생의 성적 중 해당 과목이 존재하면 기존 점수가 갱신됩니다.
[스페이스바 입력]
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[3 입력]
성적을 추가할 학생의 이름, 과목 이름, 성적(A+, A, F 등)을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[3 입력]
성적을 추가할 학생의 이름, 과목 이름, 성적(A+, A, F 등)을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift A+
만약에 학생의 성적 중 해당 과목이 존재하면 기존 점수가 갱신됩니다.
[Mickey Swift A+ 입력]
Mickey 학생의 Swift 과목이 A+로 추가(변경)되었습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


성적삭제

  • 메뉴를 선택한 후에도 잘못 입력한 것이 있으면 처리해 주어야 합니다
  • 없는 학생의 성적은 삭제하지 않습니다.

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[4 입력]
성적을 삭제할 학생의 이름, 과목 이름을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift
[스페이스바 입력]
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[4 입력]
성적을 삭제할 학생의 이름, 과목 이름을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift
[Jack 입력]
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[4 입력]
성적을 삭제할 학생의 이름, 과목 이름을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift
[Jack Swift 입력]
Jack학생을 찾지 못했습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[4 입력]
성적을 삭제할 학생의 이름, 과목 이름을 띄어쓰기로 구분하여 차례로 작성해주세요.
입력예) Mickey Swift
[Mickey Swift 입력]
Mickey 학생의 Swift 과목의 성적이 삭제되었습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


평점보기

  • 메뉴를 선택한 후에도 잘못 입력한 것이 있으면 처리해 주어야합니다.
  • 해당 학생의 과목과 성적을 모두 출력한 후 마지막 줄에 평점을 출력합니다.
  • 없는 학생은 평점을 보여주지 않습니다.

원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[5 입력]
평점을 알고 싶은 학생의 이름을 입력해주세요
[스페이스바 입력]
입력이 잘못되었습니다. 다시 확인해주세요.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[5 입력]
평점을 알고 싶은 학생의 이름을 입력해주세요
[Jack 입력]
Jack 학생을 찾지 못했습니다.


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
[5 입력]
평점을 알고 싶은 학생의 이름을 입력해주세요
[Mickey 입력]
Swift: A+
Python: B
평점: 3.75


원하는 기능을 입력해주세요
1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료


종료

  • 프로그램을 종료합니다.

    원하는 기능을 입력해주세요
    1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료
    [X 입력]
    프로그램을 종료합니다...
    Program ended with exit code: 0


프로젝트 구성

Command Line Tool은 처음 사용해보는 것 같다.

readLine()이라는 메소드를 사용해서 유저의 인풋을 콘솔로 받을 수 있다는 걸 알게 됨

콘솔에 타이핑을 입력하고 엔터를 치면 된다!

우선은 프로그램이 계속 실행되어야 하니까 while문이 항상 true이게 해주자

while true {
    let something = readLine()
    print(something)
}

하고 입력을 해보면


옵셔널 해제가 필요하겠군요

그리고 x를 입력하면 while문을 벗어나게 해줍시다
x의 대소문자 구분없이!

while true {
    if let input = readLine() {
        if input.lowercased() == "x" {
            print("프로그램을 종료합니다...")
            break
        }
    }
}

이제 class를 구성해서 메뉴가 뜨는 거랑, 유저 인풋을 가지고 처리들을 해줍시다
따로 인스턴스 안만들어도 되게 싱글톤으로 만들까?!

class MyCreditManager {
    static let shared = MyCreditManager()
    private init() { }
    
    func showMenu() {
        print("원하는 기능을 입력해주세요")
        print("1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료")
    }    
}

오케이! 그리고 showMenu를 while문 제일 상단에 올려줌

학생추가

이제 UserInput의 진행을 관리하는 메소드를 구성해야겠다

흠,,,
공백 처리도 해줄까? 아니면 그냥 숫자만 입력하게할까

//In MyCreditManager...
func processUserInput(_ input: String) {
    let menu = input.trimmingCharacters(in: .whitespacesAndNewlines)
    
    switch menu {
    case "1":
        print("추가할 학생의 이름을 입력해주세요")
    default:
        print("뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.")
    }
}

앞뒤로 스페이스바 공백 제거하게 해줌!

case들 12345 만들어주고,
각각의 case들에 해당하는 메소드를 구성, 실행해주면 되겠다!
(이 메소드들에서 readLine()을 한번더 작성하면 거기서 걸쳐진다는 걸 알게됨!)

func addStudent() {
    print("추가할 학생의 이름을 입력해주세요")
    if let name = readLine()?.trimmingCharacters(in: .whitespacesAndNewlines) {        
    
    }    
}

addStudent를 할라고 보니 student 모델이 있어야겠음

struct Student {
    let name: String
    let subject: String?
    let grade: String?
}

간단하게 구성함!

🤔 subject랑 grade는 우선은 옵셔널하게 만들었는데 나중에 수정해야될 수도

아하잇
var가 되야겠구나

struct Student {
    let name: String
    var subject: String?
    var grade: String?
    
    init(name: String, subject: String? = nil, grade: String? = nil) {
        self.name = name
        self.subject = subject
        self.grade = grade
    }
}

init구문안에서 nil넣어서 새로 만들때 다른 거 입력 안해도 되게해줌

func addStudent() {
        print("추가할 학생의 이름을 입력해주세요")
        if let name = readLine()?.trimmingCharacters(in: .whitespacesAndNewlines) {
            if allStudents.contains(where: { $0.name == name }) {
                print("\(name)은(는) 이미 존재하는 학생입니다. 추가하지 않습니다.")
                return
            }
            
            let newStudent = Student(name: name)
            allStudents.append(newStudent)
            print("\(name) 학생이 추가되었습니다.")
        }
    }

Manager안에 allStudents array 만들어두고 addStudent로직 추가해줬다

근데 이거 여기서도 잘못된 입력에 대한 처리가 필요함

아하핫 영어 입력만 받는 거 찾다가 입력이 영어 혹은 숫자인지 알아내는 방법을 찾았다!
스택갓

extension String {
    // 영어 혹은 숫자인지
    var isAlphanumeric: Bool {
        return !isEmpty && range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil
    }
    
    // 영어 인지
    var isEnglish: Bool {
        return self.range(of: "[^a-zA-Z]", options: .regularExpression) == nil
    }
}

String extension으로 영어 입력체크하는 computed property 만듬

옴하하

대소문자 구별해줄까?!
흠 .lowercased()쓸까말까 고민하다 그냥 대소문자 다르면 다른 학생인걸로 인식하게 해줌


학생 삭제

func removeStudent() {
    print("삭제할 학생의 이름을 입력해주세요")
    if let name = readLine()?.trimmingCharacters(in: .whitespacesAndNewlines) {
        if !name.isEnglish {
            print("뭔가 잘못 입력하셨습니다. 다시 시작하세요")
            return
        }
        
        if let index = allStudents.firstIndex(where: { $0.name == name }) {
            allStudents.remove(at: index)
            print("\(name) 학생을 삭제하였습니다.")
            return
        } else {
            print("\(name) 학생을 찾지 못했습니다.")
            return
        }
    }
}

이전이랑 비슷한 뉘앙스로 구성


성적 추가

앗하이
성적 추가 구현하다보니까
이거 과목이랑 성적이 같이 매핑되야함
SubjectAndGrade라는 또 새로운 struct 모델을 만들어줌!

func addGrade() {
    print("성적을 추가할 학생의 이름, 과목 이름, 성적(A+, A, F 등)을 띄어쓰기로 구분하여 차례로 작성해주세요.")
    print("입력예) Mickey Swift A+")
    print("만약에 학생의 성적 중 해당 과목이 존재하면 기존 점수가 갱신됩니다.")
    if let input = readLine()?.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ") {
        if input.count != 3 {
            print("입력이 잘못되었습니다. 다시 확인해주세요.")
            return
        }
        
        let name = input[0]
        let subject = input[1]
        let grade = input[2]
        
        if !allStudents.contains(where: { $0.name == name }) {
            print("\(name) 학생을 찾지 못했습니다.")
            return
        }
        
        if let index = allStudents.firstIndex(where: { $0.name == name }) {
            let newSubGrade = SubjectAndGrade(subject: subject, grade: grade)
            allStudents[index].subGradeList?.append(newSubGrade)
            print("\(name) 학생의 \(subject) 과목이 \(grade)로 추가(변경)되었습니다.")
            return
        }
    }
}

성적 삭제

func removeGrade() {
    print("성적을 삭제할 학생의 이름, 과목이름을 띄어쓰기로 구분하여 차례로 작성해주세요.")
    print("입력예) Mickey Swift")
    if let input = readLine()?.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ") {
        if input.count != 2 {
            print("입력이 잘못되었습니다. 다시 확인해주세요.")
            return
        }
        
        let name = input[0]
        let subject = input[1]
        
        if !allStudents.contains(where: { $0.name == name }) {
            print("\(name) 학생을 찾지 못했습니다.")
            return
        }
        
        if let index = allStudents.firstIndex(where: { $0.name == name }) {
            if let subIndex = allStudents[index].subGradeList?.firstIndex(where: { $0.subject == subject}) {
                allStudents[index].subGradeList?[subIndex].grade = nil
            }
            print("\(name) 학생의 \(subject) 과목의 성적이 삭제되었습니다.")
            return
        }
    }
}

🤔firstIndex찾는걸로 두번 반복했는데 더 효율적으로 할 방법있을 것 같음!
딕셔너리로 바꿔줘야하려나?


평점보기

grade를 enum타입으로 바꾸는 게 나을 거 같다..!

enum Grade: String {
    case APlus = "A+"
    case A = "A"
    case BPlus = "B+"
    case B = "B"
    case CPlus = "C+"
    case C = "C"
    case DPlus = "D+"
    case D = "D"
    case F = "F"
    
    var point: Double {
        switch self {
        case .APlus: return 4.5
        case .A: return 4
        case .BPlus: return 3.5
        case .B: return 3
        case .CPlus: return 2.5
        case .C: return 2
        case .DPlus: return 1.5
        case .D: return 1
        case .F: return 0
        }
    }
}

struct SubjectAndGrade {
    var subject: String
    var grade: Grade?
}

struct Student {
    let name: String
    var subGradeList: [SubjectAndGrade]?
    
    init(name: String, subGradeList: [SubjectAndGrade]? = nil) {
        self.name = name
        self.subGradeList = subGradeList
    }
}

와아아
진짜 뭐가 잘못됐나 한참을 찾았음...
var subGradeList: [SubjectAndGrade]?
여부분이 잘못 됐었음
옵셔널하지 않게 바꿔야함
grade는 옵셔널해도 Subgrade리스트는 없으면 그냥 빈배열로 고고!

func showAverageGrade() {
        print("평점을 알고싶은 학생의 이름을 입력해주세요.")
        if let name = readLine() {
            if !name.isEnglish {
                print("뭔가 잘못 입력하셨습니다. 다시 시작하세요")
                return
            }
            
            if !allStudents.contains(where: { $0.name == name }) {
                print("\(name) 학생을 찾지 못했습니다.")
                return
            }
            
            if let index = allStudents.firstIndex(where: { $0.name == name }) {
                let subAndGradeList = allStudents[index].subGradeList
                var totalScore = 0.0
                
                for item in subAndGradeList {
                    print("\(item.subject): \(item.grade?.rawValue ?? "성적 없음")")
                    guard let grade = item.grade else { continue }
                    let score = grade.point
                    totalScore += score
                }
                
                let average = totalScore / Double(subAndGradeList.count)
                
                print("\(name) 학생의 평균 평점은 \(average.formattedString())")
            } else {
                print("뭔가 잘못 입력하셨습니다. 다시 시작하세요")
            }
        }
    }


끝! 프로그램을 종료합니다..

profile
우주형

0개의 댓글

관련 채용 정보