From 2ddcbb9b11416e5e7f6fbfd00ff9b5ffa8823cf8 Mon Sep 17 00:00:00 2001 From: ueunli Date: Tue, 20 Dec 2022 10:32:45 +0900 Subject: [PATCH] =?UTF-8?q?feat.=20=ED=95=99=EC=83=9D=20=EB=AA=85=EB=8B=A8?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B0=9C=EB=B3=84=20=EC=84=B1=EC=A0=81=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=94=84=EB=A1=9C=EA=B7=B8=EB=9E=A8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 조건반복문으로 메뉴 선택 또는 프로그램을 종료합니다. 2. Student클래스의 생성자 및 소멸자를 통해 학생추가, 삭제를 실행, 안내합니다. 3. Student클래스 객체의 저장속성을 통해 성적추가(변경), 삭제를 실행, 안내합니다. 4. 올바른 메뉴선택 및 입력문자 감별은 에러처리 구문으로 작성하였습니다. 5. Codable프로토콜 및 프로퍼티리스트 파일로 데이터를 백업, 불러올 수 있습니다. --- .gitignore | 95 ++++++ .../project.pbxproj | 311 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../MyCreditManager_cdenen20/Info.plist | Bin 0 -> 114 bytes .../MyCreditManager_cdenen20/input data.swift | 80 +++++ .../MyCreditManager_cdenen20/main.swift | 80 +++++ .../MyCreditManager_cdenen20/store data.swift | 28 ++ .../student class.swift | 42 +++ README.md | 84 ++++- 9 files changed, 726 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.pbxproj create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20/input data.swift create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20/main.swift create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20/store data.swift create mode 100644 MyCreditManager_cdenen20/MyCreditManager_cdenen20/student class.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ff29cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,95 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## DS_Store +.DS_Store + +## User settings +xcuserdata/ +xcuserdata/* +*.xcuserstate + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.pbxproj b/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e925a51 --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.pbxproj @@ -0,0 +1,311 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 3D3B817B293DAA480066040E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3B817A293DAA480066040E /* main.swift */; }; + 3D3B8198293E4CE90066040E /* student class.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3B8197293E4CE90066040E /* student class.swift */; }; + 3D3B819A294111910066040E /* input data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3B8199294111910066040E /* input data.swift */; }; + 3D3B819E2941959B0066040E /* store data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3B819D2941959B0066040E /* store data.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3D3B8175293DAA480066040E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3D3B8177293DAA480066040E /* MyCreditManager_cdenen20 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MyCreditManager_cdenen20; sourceTree = BUILT_PRODUCTS_DIR; }; + 3D3B817A293DAA480066040E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 3D3B8197293E4CE90066040E /* student class.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "student class.swift"; sourceTree = ""; }; + 3D3B8199294111910066040E /* input data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "input data.swift"; sourceTree = ""; }; + 3D3B819D2941959B0066040E /* store data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "store data.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3D3B8174293DAA480066040E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3D3B816E293DAA470066040E = { + isa = PBXGroup; + children = ( + 3D3B8179293DAA480066040E /* MyCreditManager_cdenen20 */, + 3D3B8178293DAA480066040E /* Products */, + ); + sourceTree = ""; + }; + 3D3B8178293DAA480066040E /* Products */ = { + isa = PBXGroup; + children = ( + 3D3B8177293DAA480066040E /* MyCreditManager_cdenen20 */, + ); + name = Products; + sourceTree = ""; + }; + 3D3B8179293DAA480066040E /* MyCreditManager_cdenen20 */ = { + isa = PBXGroup; + children = ( + 3D3B817A293DAA480066040E /* main.swift */, + 3D3B8197293E4CE90066040E /* student class.swift */, + 3D3B8199294111910066040E /* input data.swift */, + 3D3B819D2941959B0066040E /* store data.swift */, + ); + path = MyCreditManager_cdenen20; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3D3B8176293DAA480066040E /* MyCreditManager_cdenen20 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3D3B817E293DAA480066040E /* Build configuration list for PBXNativeTarget "MyCreditManager_cdenen20" */; + buildPhases = ( + 3D3B8173293DAA480066040E /* Sources */, + 3D3B8174293DAA480066040E /* Frameworks */, + 3D3B8175293DAA480066040E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MyCreditManager_cdenen20; + productName = MyCreditManager_cdenen20; + productReference = 3D3B8177293DAA480066040E /* MyCreditManager_cdenen20 */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3D3B816F293DAA470066040E /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1330; + LastUpgradeCheck = 1330; + TargetAttributes = { + 3D3B8176293DAA480066040E = { + CreatedOnToolsVersion = 13.3.1; + }; + }; + }; + buildConfigurationList = 3D3B8172293DAA470066040E /* Build configuration list for PBXProject "MyCreditManager_cdenen20" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3D3B816E293DAA470066040E; + productRefGroup = 3D3B8178293DAA480066040E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3D3B8176293DAA480066040E /* MyCreditManager_cdenen20 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 3D3B8173293DAA480066040E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3D3B817B293DAA480066040E /* main.swift in Sources */, + 3D3B819A294111910066040E /* input data.swift in Sources */, + 3D3B819E2941959B0066040E /* store data.swift in Sources */, + 3D3B8198293E4CE90066040E /* student class.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 3D3B817C293DAA480066040E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OUTPUT_FORMAT = "same-as-input"; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + NEW_SETTING = ""; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 3D3B817D293DAA480066040E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OUTPUT_FORMAT = "same-as-input"; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + NEW_SETTING = ""; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 3D3B817F293DAA480066040E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "/Users/Junely/Desktop/새싹/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/backupData.plist"; + INFOPLIST_OUTPUT_FORMAT = "same-as-input"; + INFOPLIST_PREPROCESS = YES; + PLIST_FILE_OUTPUT_FORMAT = "same-as-input"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 3D3B8180293DAA480066040E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "/Users/Junely/Desktop/새싹/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/backupData.plist"; + INFOPLIST_OUTPUT_FORMAT = "same-as-input"; + INFOPLIST_PREPROCESS = YES; + PLIST_FILE_OUTPUT_FORMAT = "same-as-input"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3D3B8172293DAA470066040E /* Build configuration list for PBXProject "MyCreditManager_cdenen20" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3D3B817C293DAA480066040E /* Debug */, + 3D3B817D293DAA480066040E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3D3B817E293DAA480066040E /* Build configuration list for PBXNativeTarget "MyCreditManager_cdenen20" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3D3B817F293DAA480066040E /* Debug */, + 3D3B8180293DAA480066040E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3D3B816F293DAA470066040E /* Project object */; +} diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..ce34a5eca9869b407175e9c6aa53dc1cc60a422a GIT binary patch literal 114 zcmYc)$jK}&F)+Bq$i&PYmXw$WgrSKA1v#mgSXdd^Lh=%GQ^V4W5>rx(FR^oQas?Nc uWP|{*QIL~%kh1|$5fje^4qg!{c@=E~6H6O=7X~n3WQ5QRyigiOxdH%HNf?{} literal 0 HcmV?d00001 diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20/input data.swift b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/input data.swift new file mode 100644 index 0000000..36863aa --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/input data.swift @@ -0,0 +1,80 @@ +// +// input data.swift +// MyCreditManager_cdenen20 +// +// Created by Junely on 2022/12/07. +// + +import Foundation + +//MARK: - readLine()값 저장 + +///**'메뉴선택'용 입력값**을 저장하는 변수 +///- main의 repeat-while문의 반복조건 +///- `X`를 입력하면 반복문 종료 +var inputMenu = String() + +///**'기능실행'용 입력값(들)**을 저장하는 변수 +///- main의 switch-case문에서 호출 +///- 중복·가장자리 공백은 자동으로 처리, 유효한 단어 수에 따라 변수에 저장 +var inputData = String() +var (name, subject, grade) = (String(), String(), String()) +func getInputs(_ s: String = (readLine() ?? "")) { + let input = s.components(separatedBy: .whitespaces).compactMap { try? checkInput(str: $0).get() } + switch input.count { + case 1: (name, subject, grade) = (input[0], "", "") + case 2: (name, subject, grade) = (input[0], input[1], "") + case 3: (name, subject, grade) = (input[0], input[1], input[2]) + default: print("입력이 잘못되었습니다. 다시 확인해주세요.") + } +} + + +//MARK: - 허용된 입력값인지 검사 + +///**입력값 에러 종류** 목록 +///- __notMenu__: `inputMenu`에 선택지 6종 외의 값이 들어올 경우 +///- __notAlphanumeric__: `inputMenu` 또는 `inputData`에 영문·숫자가 아닌 자모가 포함된 경우 +enum InputError: Error { + case notMenu(text: String = "뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.") + case notAlphanumeric(text: String = "입력이 잘못되었습니다. 다시 확인해주세요.") +} + +///**입력값 에러 판별** 함수 +///- `checkInput(str:)`: **기능실행** 단계에서 활용 +///- `checkInput(chr:)`: **기능선택** 단계에서 활용 +func checkInput(str: String) -> Result { + var isValid = Bool() + for chr in str { + isValid = chr.isCased || chr.isNumber || chr.isWhitespace || chr == "+" + } + guard isValid else { return .failure(InputError.notAlphanumeric()) } + return .success(isValid ? str : nil) +} +///**입력값 에러 판별** 함수 +///- `checkInput(str:)`: **기능실행** 단계에서 활용 +///- `checkInput(chr:)`: **기능선택** 단계에서 활용 +func checkInput(chr: String) -> Result { + guard chr.count == 1 && ["1", "2", "3", "4", "5", "X"].contains(chr) else { return .failure(InputError.notMenu()) } + return .success(chr) +} + + +///**입력값 에러 처리** 함수 - 기능 선택 시 +///+ 에러가 아니면 true, 에러에 해당하면 false를 반환 +///+ 용도에 따라(guard문 조건 or 문구출력) 다용도로 사용 +func getInput(_ checkedResult: Result) -> Bool { + switch checkedResult { + case .success: return true //return try! checkedResult.get() + case .failure(let error): print("\(error)".components(separatedBy: "\"")[1]); return false + } +} +///**입력값 에러 처리** 함수 - 기능 실행 시 +///+ 에러가 아니면 true, 에러에 해당하면 false를 반환 +///+ 용도에 따라(guard문 조건 or 문구출력) 다용도로 사용 +func getInput(_ checkedResult: Result) -> Bool { + switch checkedResult { + case .success: return true + case .failure(let error): print("\(error)".components(separatedBy: "\"")[1]); return false + } +} diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20/main.swift b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/main.swift new file mode 100644 index 0000000..4a7f576 --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/main.swift @@ -0,0 +1,80 @@ +// +// main.swift +// MyCreditManager_cdenen20 +// +// Created by Junely on 2022/12/05. +// + +import Foundation + +//MARK: - 지난 실행기록 불러오기 +readWholeData() + +repeat { + + //MARK: - 원하는 기능 선택하기 + print(""" + 원하는 기능을 입력해주세요 + 1: 학생추가, 2: 학생삭제, 3: 성적추가(변경), 4: 성적삭제, 5: 평점보기, X: 종료 + """) + inputMenu = readLine() ?? "" + let _ = checkInput(chr: inputMenu) + + //MARK: - 선택한 기능 실행하기 + switch inputMenu { + case "1": + print("추가할 학생의 이름을 입력해주세요") + inputData = readLine() ?? "" + guard getInput(checkInput(str: inputData)) else { print(); continue } + getInputs(inputData) + let _ = Student(name: name) + case "2": + print("삭제할 학생의 이름을 입력해주세요") + inputData = readLine() ?? "" + guard getInput(checkInput(str: inputData)) else { print(); continue } + getInputs(inputData) + print(Student.students.removeValue(forKey: name) == nil ? "\(name) 학생을 찾지 못했습니다." : "") + case "3": + print(""" + 성적을 추가할 학생의 이름, 과목 이름, 성적(A+, A0, F 등)을 띄어쓰기로 구분하여 차례로 작성해주세요. + 입력예) Mickey Swift A+ + 만약에 학생의 성적 중 해당 과목이 존재하면 기존 점수가 갱신됩니다. + """) + inputData = readLine() ?? "" + guard getInput(checkInput(str: inputData)) else { print(); continue } + getInputs(inputData) + guard let student = Student.students[name] else { print("\(name) 학생을 찾지 못했습니다."); break } + student.grades.updateValue(grade, forKey: subject) + print("\(name) 학생의 \(subject) 과목이 \(grade)로 추가(변경)되었습니다.") + case "4": + print(""" + 성적을 삭제할 학생의 이름, 과목 이름을 띄어쓰기로 구분하여 차례로 작성해주세요. + 입력예) Mickey Swift + """) + inputData = readLine() ?? "" + guard getInput(checkInput(str: inputData)) else { print(); continue } + getInputs(inputData) + guard let student = Student.students[name] else { print("\(name) 학생을 찾지 못했습니다."); break } + student.grades.removeValue(forKey: subject) + print("\(name) 학생의 \(subject) 과목의 성적이 삭제되었습니다.") + case "5": + print("평점을 알고싶은 학생의 이름을 입력해주세요") + inputData = readLine() ?? "" + guard getInput(checkInput(str: inputData)) else { print(); continue } + getInputs(inputData) + guard let student = Student.students[name] else { print("\(name) 학생을 찾지 못했습니다."); break } + student.grades.sorted(by: { $0.key.count < $1.key.count }).forEach { print("\($0.key): \($0.value)") } + print("평점 :", "\(student.overallScore)".replacingOccurrences(of: ".0", with: "").prefix(4)) + case "X": + //MARK: - 지난 실행기록 저장하기 + saveWholeData() + print("프로그램을 종료합니다...") + default: + print("뭔가 입력이 잘못되었습니다. 1~5 사이의 숫자 혹은 X를 입력해주세요.") + } + print() + +} while inputMenu != "X" + + + diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20/store data.swift b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/store data.swift new file mode 100644 index 0000000..880f464 --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/store data.swift @@ -0,0 +1,28 @@ +// +// store data.swift +// MyCreditManager_cdenen20 +// +// Created by Junely on 2022/12/08. +// + +import Foundation + +//MARK: - 학생객체를 백그라운드에 저장하는 방법 + +///이번 실행데이터 저장하기 +func saveWholeData() { + let path = "/Users/Junely/Desktop/새싹/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist" + let backupDictionary = NSMutableDictionary(contentsOfFile: path) ?? [:] + let encoder = PropertyListEncoder() + let encodedData = try! encoder.encode(Student.students) + try! encodedData.write(to: .init(fileURLWithPath: path)) +} + +///지난 실행데이터 불러오기 +func readWholeData() { + let path = "/Users/Junely/Desktop/새싹/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist" + let backupDictionary = try! Data(contentsOf: URL(fileURLWithPath: path)) + let decoder = PropertyListDecoder() + let data = try! decoder.decode([String: Student].self, from: backupDictionary) + Student.students = data +} diff --git a/MyCreditManager_cdenen20/MyCreditManager_cdenen20/student class.swift b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/student class.swift new file mode 100644 index 0000000..f1405fe --- /dev/null +++ b/MyCreditManager_cdenen20/MyCreditManager_cdenen20/student class.swift @@ -0,0 +1,42 @@ +// +// student class.swift +// MyCreditManager_cdenen20 +// +// Created by Junely on 2022/12/05. +// + +import Foundation + +///학생객체를 생성하고 관리하는 클래스 +///- `students`: 이름(key)에 대응하는 학생객체(value)를 저장 +///- `grades`: 과목(key)에 대응하는 성적(value)을 저장 +class Student: Codable { + + static var students = [String: Student]() + let name: String + var grades = [String: String]() + + ///평점 계산 + var overallScore: Double { + let gradeList = ["F", "D0", "D+", "C0", "C+", "B0", "B+", "A0", "A+"] + let scoreList = [0, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5] + return grades.values.map { scoreList[gradeList.firstIndex(of: $0) ?? 0] }.reduce(0, +) / Double(grades.count) + } + + ///학생 추가 + init?(name: String) { + if Student.students.keys.contains(name) { + print("\(name)은(는) 이미 존재하는 학생입니다. 추가하지 않습니다.") + return nil + } + self.name = name + Self.students.updateValue(self, forKey: name) + print("\(name) 학생을 추가했습니다.") + } + + ///학생 삭제 + deinit { + print("\(name) 학생을 삭제하였습니다.") + } + +} diff --git a/README.md b/README.md index 5305121..5ecf7e2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ -# pull-request-practice -Pull Request 연습용 레포지토리입니다. +> # tastycode_SeSAC_1st +> [2022 맛있는코드 x 새싹] iOS 앱 개발자 부트캠프 사전과제 + +  +*** + +# 구현 진행상황 및 고민 +## 🧐 구현 현황 +> 필수 구현요소) **볼드체** +- [x] ┏ **[공통] 입력양식 판별(기능메뉴)** +- [x] ┗ **[개별] 입력양식 판별(작업내용)** +- [x] ┏ **학생 추가** +- [x] ┗ **학생 삭제** +- [x] ┏ **성적 추가(변경)** +- [x] ┗ **성적 삭제** +- [x] ┏ **총점 계산** +- [x] ┗ **총점 조회** +- [x] 저장된 데이터 백업 + (앱 종료와 무관하게 or 앱 종료 직전에 입력·처리된 데이터를 저장) + +### 구현하려 했지만 못 한 부분 +> - 출력문구 변수화 - 여러줄 문자열 할당 가능 여부에 따라 + - 가능했지만 case별 구분이 힘들어질 듯하여 보류 + - 열거형 내에 의미가 통하게(ex. `Text.printMessage(noName)`) print()해주는 함수를 만들면 되겠지만 코드가 너무 길어짐 + - 이미 몇몇 문구는 에러처리 과정에서 출력되어, 따로 만들어도 흩어지는 결과를 낳을 것 같았음 +> - 묶지 못한 반복코드들 + - case별 3줄씩 반복되는 코드를 함수로 묶지 못한 것, + - 원래 Student 내 메서드로 구현하려 했으나 제어전송문 때문에 옮기지 못한 것 +> - 뒤늦게 발견한 에러 + - 과목 0개인 학생은 총점 ‘nan’으로 뜸 + - 관련 희망구현 요소도 따로 적었는데, 오히려 놓쳤다. +   +   +  +   +   +  +## 🧐 구현 여부·방법 고민 +> 배제한 발상) ~~취소선~~ +1. '입력양식 판별'은 반복이 많은 코드(모든 요구기능의 조건) +
✎ (학생과 성적의 자료형을 정한 뒤 타입 일관성 만들어 봐야 결정할 수 있을 것 같다. 리팩토링 할 것.) + - [ ] 리턴타입이 클로저인 전역변수 혹은 메서드로 만들까? + 입력 양식에 어긋나지 않으면 선택한 기능(함수)을 실행하도록 (또는 인풋타입도 함수) +1. 학생과 성적의 자료형 + - [ ] ~~학생과 성적을 별도의 배열로 저장, zip으로 매치(index가 같을 테니)~~ + - [ ] 학생과 성적을 딕셔너리로 저장 - 성적도 딕셔너리로 구현해야 하니 이차원 콜렉션 + - [ ] ~~학생과 성적을 연관값을 가진 열거형으로 묶기~~ + - [x] 클래스) 학생은 객체, 성적은 속성 + (구조체가 아닌 이유: 소멸자가 필요할 것 같고, 잦은 데이터 수정이 있으므로 구조체(저장위치)에 참조타입을 저장해야 함.) + - 생성된 객체(학생)를 검색할 수 있어야 한다. 어디에 저장할까? + - [ ] ~~부모클래스 Students를 만들어 상속한 뒤, ...~~ + - [x] 타입속성 배열에 추가(생성자에서) +1. '이미 존재 / 없는 학생·성적은 ~하지 않습니다'에 관해 +
✎ 추가했다가 삭제하지 말고, 추가하는 작업 실행 자체를 하지 말라는 뜻인 듯하다. +
✎ Set으로 중복처리하지 말고, 사전에 유무를 판별하여 추가·삭제 등의 동작을 수행하지 않도록 하자. + - [x] 공통적으로 쓸 수 있는 함수를 만들까, 아니면 각 동작(함수)에 판별용 코드(guard문 등)를 작성할까? + - [ ] (클래스 활용 확정 시) 타입메서드로 만들까, 하나의 인스턴스메서드로 분리하고 각 메서드에서 호출할까, 각 인스턴스메서드 내에 만들까? +1. 오버로딩 +
✎ 학생의 추가·삭제는 생성·소멸자로, 성적의 추가·변경·삭제는 인스턴스메서드로 구현하게 될 듯하다. +
✎ 후자의 경우, (성적을 딕셔너리로 저장하는 게 확정될 시) 요구되는 매개변수의 수만 다르지 작성되는 코드는 유사하다. + - [ ] '성적'에 관한 네이밍 하나로 통일하여 오버로딩 함수로 구현할까? +1. 종료코드 위치 +
✎ `X` 입력 시 나타날 종료코드를 어디에 위치시킬지 — ※당연하지만 요구사항 지문에 드러났듯, `현재 학생과 성적의 모든 상태를 저장` --> `프로그램을 종료` 작업은 *순서대로* 나타나야 한다. 두 작업의 처리 위치를 분리하게 되면 이 부분 신경쓸 것. + - [x] switch-case문 내부 (현재 구상) + - [ ] 반복문 바깥에서 처리 ('종료 시 데이터 백업'기능을 구현하게 된다면) + - [ ] while문 → repeat-while문으로 변경할 수 있음 + - [ ] 또는 (클래스 활용 확정 시) 소멸자 내부에서 처리 +1. 데이터 '백업' 방법 (종료코드) +
✎ 가능하면 구현 - 아래 2가지 선택지 모두 공부 필요한 내용 ··· [문서](https://github.com/ueunli/tastycode_SeSAC_1st/issues/2#issue-1478072276) + - [x] 프로젝트 내 .plist 파일에 저장 + - [ ] CoreData 프레임워크 사용 + +## 🧐 기타 사항 +> 요구조건 외 사항이거나 테스트 단계에서는 큰 문제가 없을 오류에 관한 고민 +- 입력란에 `한글 자모` → `Backspace키` → `알파벳` 순서대로 입력 시,
+입력한 한글 자모의 숫자만큼 or 그 2배만큼 Backspace를 누른 후 알파벳을 입력해도
+`인코딩되지 않은 특수문자 + 알파벳 문자열`조합이 입력되는 현상 + - [x] ⮑ [희망 구현] 입력양식 판별에서 처리할 것(유니코드 범위 등을 통해) +- 긴 출력문구가 많아 코드의 가독성이 떨어짐 + - [ ] ⮑ [희망 구현] ~~print용 문자열의 변수화~~ ··· (정리할것 -*'여러 줄 문자열'을 일반 변수 할당 시에도 쓸 수 있는가?*) ··· [문서](https://github.com/ueunli/tastycode_SeSAC_1st/issues/1#issue-1478006243) + - [x] ⮑ [희망 구현] 다음번 기능 선택문구 앞에 개행 추가(`print()`) +