[내일배움캠프] 251219 TIL

Bambu·2025년 12월 19일

내배캠 TIL

목록 보기
3/52

1. STEP 5. 옵셔널 이해하기

과제 1: 옵셔널 개념 이해하기

var name = "Alice"
var age = 20
let birthYear = 2003
var car: String? = "BMW"

print("이름: \(name), 나이: \(age), 출생 연도: \(birthYear), 보유 차종: \(car)")

이름: Alice, 나이: 20, 출생 연도: 2003, 보유 차종: Optional("BMW") 출력

car = nil
print("이름: \(name), 나이: \(age), 출생 연도: \(birthYear), 보유 차종: \(car)")

이름: Alice, 나이: 20, 출생 연도: 2003, 보유 차종: nil 출력

print(type(of: car))
Optional<String> 출력

과제 2: 옵셔널 바인딩 하기

변환 결과 확인

  • 변수 score가 옵셔널 타입(Int?)인 이유
    : 주어진 name 값이 딕셔너리에 존재하지 않는 키일 가능성이 있으므로 그 경우에는 딕셔너리가 nil을 반환하기 때문에

  • 방법 2 - 방법 1 순으로 순서 변경 시 출력 메시지 비교
    : "Jane: 점수 없음. early return" 출력 후 return되어 코드 종료
    : 방법 1 먼저 실행 시, "Jane: 점수 없음. else 문 실행" 출력 후 return되어 코드 종료

  • 방법 3(강제 언래핑) 가장 먼저 실행 시
    : "Jane의 점수 조회: \n "정수: nil점" 출력 후 Fatal Error와 함께 코드 강제 종료 - nil값을 강제 언래핑 시도했으므로

2. 프로젝트: "간단한 가위바위보 게임 만들기"

기능 목록

  1. 사용자가 "가위", "바위", "보" 중 하나를 입력하면 컴퓨터가 무작위 선택을 한다.
  2. 조건문을 사용하여 승패를 판별하고 결과를 출력한다.
  3. 사용자가 "그만"을 입력할 때까지 반복적으로 게임을 진행한다.
  4. 사용자의 입력이 올바르지 않으면 다시 입력하도록 처리한다.

1단계: 컴퓨터가 무작위 선택하는 함수 만들기

2단계: 승패를 판별하는 함수 만들기

3단계: 사용자 입력을 받아 게임을 반복하기

enum Choice: String, CaseIterable {
    case 가위 = "가위", 바위 = "바위",= "보"
    
    func result(against: Choice) -> String {
        if self == against {
            return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n무승부입니다!"
        }
        
        switch (self, against) {
        case (.가위, .바위), (.바위, .), (., .가위):
            return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n졌습니다..."
        default:
            return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n이겼습니다!"
        }
    }
}

Choice 열거형에 result 메소드를 생성하여 열거형에서 바로 결과를 알 수 있도록 함

func getComChoice() -> Choice {
    return Choice.allCases.randomElement()!
}

.allCases 메소드 활용을 위해 열거형에 CaseIterable 프로토콜 채택

func rockPaperScissors(_ myChoice: String) {
    if myChoice == "그만" {
        print("가위뱌위보를 종료합니다.")
        isPlaying = false
        return
    }
    
    let com = getComChoice()
    let my = Choice(rawValue: myChoice)
    
    guard let my = my else {
        print("올바른 값을 입력해주세요.(가위, 바위, 보)")
        return
    }
    
    print(my.result(against: com))
}

myChoice"그만"인지 우선 판별 - 맞을 경우 함수 종료
→ 아닐 경우 랜덤값 com 생성, 입력받은 문자열로 Choice 타입의 my 생성
mynil이 아닐 경우 my.result(against:)로 결과 반환
mynil일 경우 재입력 요청

var isPlaying = true

while isPlaying {
    print("가위, 바위, 보 중 하나를 입력해주세요.")
    rockPaperScissors(readLine() ?? "")
}

isPlaying 변수로 while문의 반복 여부 확인
→ 입력값 "그만"일 경우 isPlqying = false, 반복문 종료

➡︎ 처음에는 가위, 바위, 보 문자열이 아니라 각 값을 정수로 치환하여 승패를 판단하려했는데, 튜터님의 힌트로 열거형을 사용하게 되었다.
열거형에서 메소드 선언은 처음해보았는데, 다양한 상황에서 활용할 수 있을 문법같다..

3. STEP 4(마스터). - Struct와 Class / 프로토콜

직접 구현해보기

1) Struct 구현하기

struct Product {
	var id: UUID = UUID()
	var name: String
    var price: Int
    var category: String
}
var a = Product(name: "치약", price: 2000, category: "생필품")
var b = Product(name: "비누", price: 1000, category: "생필품")

a.price = 3000
print("a 가격: \(a.price), b 가격: \(b.price)")
// a 가격: 3000, b 가격: 1000 출력

→ 한 인스턴스의 변경이 다른 인스턴스에 영향 없음

2) Class 사용 예제)

class Product {
	var id: UUID = UUID()
    var name: String
    var price: Int
    var category: String
    
    init(name: String, price: Int, category: String) {
    	self.name = name
        self.price = price
        self.category = category)
    }
}
var a = Product(name: "치약", price: 2000, category: "생필품")
var b = Product(name: "비누", price: 1000, category: "생필품")

a.price = 3000
print("a 가격: \(a.price), b 가격: \(b.price)")
// a 가격: 3000, b 가격: 1000 출력

→ 서로 다른 두 인스턴스를 참조할 경우, 한 참조의 값을 변경하여도 다른 인스턴스에 영향 없음

class ShoppingCart {
    var items: [Product]
    var totalPrice: Double {
        return items.reduce(0) { $0 + $1.price }
    }
    
    init(items: [Product]) {
        self.items = items
    }
    
    func addItem(_ item: Product) {
        items.append(item)
    }
    
    func removeItem(at idx: Int) {
        items.remove(at: idx)
    }
}
var cart = ShoppingCart(items: [a, b])
var anotherCart = cart

print(cart.items)
// "치약", "비누" 출력

anotherCart.addItem(Product(name: "샴푸", price: 7000, category: "생필품"))

print(cart.items)
// "치약", "비누", "샴푸" 출력

→ 동일한 하나의 인스턴스를 서로 다른 2개의 변수에서 참조할 경우, 한 참조(변수)에서 값을 변경한 경우 다른 참조(변수)에도 반영됨

Discountable 프로토콜

protocol Discountable {
    var originalPrice: Double { get }
    
    func discountedPrice(rate: Double)
}
struct PercentageDiscount: Discountable {
    var originalPrice: Double
    
    func discountedPrice(rate: Double) {
        let discounted = originalPrice * (1 - rate)
        print("\(rate * 100)% 할인된 가격은 \(discounted)원입니다.")
    }
}

var c = PercentageDiscount(originalPrice: 10000)
c.discountedPrice(rate: 0.2)
// 20.0% 할인된 가격은 8000.0원입니다. 출력

Discountable 프로토콜을 채택, 필수 프로퍼티와 메소드 구현

class VIPDiscount: Discountable {
    enum Grade: String {
        case gold = "Gold", silver = "Silver", bronze  = "Bronze"
    }
    
    var grade: Grade
    var originalPrice: Double
    var rate: Double
    
    init(grade: Grade, originalPrice: Double) {
        self.grade = grade
        self.originalPrice = originalPrice
        
        switch grade {
        case .gold: self.rate = 0.15
        case .silver: self.rate = 0.1
        case .bronze: self.rate = 0.05
        }
    }
    
    func discountedPrice(rate: Double) {
        let discounted = originalPrice * (1 - rate)
        print("\(self.grade.rawValue) 등급의 할인된 가격은 \(discounted)원입니다.")
    }
}

var d = VIPDiscount(grade: .gold, originalPrice: 10000)
d.discountedPrice(rate: d.discount)
// Gold 등급의 할인된 가격은 8500.0원입니다. 출력

Discountable 프로토콜을 채택, 필수 프로퍼티와 메소드 구현

4. 프로토콜 확장(Extension)

protocol Discountable {
	var originalPrice: Double
    
    func discountedPrice(rate: Double) -> Double
}

extension Discountable {
	func finalPrice() -> Double {
    	return discountedPrice(rate: 0.1) // 기본 10% 할인
    }
}

→ 확장에는 저장 속성 구현 불가, 메소드 리턴값 명시 필요
→ 확장에서 메소드 사용 시 아규먼트 기재 필요

profile
안녕하세요, iOS 개발을 공부하고 있는 Bambu입니다. (프로필: Swifticons)

0개의 댓글