λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ₯– Bread Basics/Swift

Swift 곡식 λ¬Έμ„œ 정리 - κΈ°λ³Έ(The Basics)(2/2)

by BreadDev 2025. 4. 10.
728x90

μ•ˆλ…•ν•˜μ„Έμš” μ΄λ²ˆμ—λŠ” Swift κΈ°λ³Έ κ°œλ… μ •λ¦¬μ˜ 두 번째 파트둜, λΆ€μšΈ, νŠœν”Œ, μ˜΅μ…”λ„, μ—λŸ¬ 처리 λ“± μ€‘μš”ν•œ κ°œλ…λ“€μ„ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ“Œ λΆ€μšΈ (Booleans)

SwiftλŠ” μ°Έκ³Ό 거짓을 ν‘œν˜„ν•˜κΈ° μœ„ν•œ Bool νƒ€μž…μ„ μ œκ³΅ν•©λ‹ˆλ‹€:

let orangesAreOrange = true
let turnipsAreDelicious = false

// μ‘°κ±΄λ¬Έμ—μ„œ μ‚¬μš©
if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// "Eww, turnips are horrible." 좜λ ₯

Bool: μ°Έ(true) λ˜λŠ” κ±°μ§“(false) κ°’λ§Œ κ°€μ§ˆ 수 μžˆλŠ” 논리 νƒ€μž…μž…λ‹ˆλ‹€.

SwiftλŠ” νƒ€μž… μ•ˆμ „μ„±μ„ μœ„ν•΄ λΆ€μšΈμ΄ μ•„λ‹Œ 값이 Bool둜 λŒ€μ²΄λ˜λŠ” 것을 λ°©μ§€ν•©λ‹ˆλ‹€:

let i = 1
if i == 1 {
    // μ˜¬λ°”λ₯Έ μ‚¬μš©: 비ꡐ κ²°κ³ΌλŠ” Bool νƒ€μž…
}

// λ‹€μŒ μ½”λ“œλŠ” 컴파일 μ—λŸ¬ λ°œμƒ
// if i { ... }  // 잘λͺ»λœ μ‚¬μš©: iλŠ” Bool이 μ•„λ‹˜

πŸ“Œ νŠœν”Œ (Tuples)

νŠœν”Œμ€ μ—¬λŸ¬ 값을 ν•˜λ‚˜μ˜ 볡합 κ°’μœΌλ‘œ κ·Έλ£Ήν™”ν•©λ‹ˆλ‹€:

// HTTP μƒνƒœ μ½”λ“œλ₯Ό λ‚˜νƒ€λ‚΄λŠ” νŠœν”Œ
let http404Error = (404, "Not Found")
// νƒ€μž…μ€ (Int, String)

// νŠœν”Œ λΆ„ν•΄(decompose)ν•˜κΈ°
let (statusCode, statusMessage) = http404Error
print("μƒνƒœ μ½”λ“œ: \(statusCode)")  // "μƒνƒœ μ½”λ“œ: 404" 좜λ ₯
print("μƒνƒœ λ©”μ‹œμ§€: \(statusMessage)")  // "μƒνƒœ λ©”μ‹œμ§€: Not Found" 좜λ ₯

// 일뢀 κ°’λ§Œ ν•„μš”ν•  λ•Œ
let (justTheStatusCode, _) = http404Error

// 인덱슀둜 μ ‘κ·Ό
print("μƒνƒœ μ½”λ“œ: \(http404Error.0)")  // "μƒνƒœ μ½”λ“œ: 404" 좜λ ₯

// 이름이 μžˆλŠ” νŠœν”Œ μš”μ†Œ
let http200Status = (statusCode: 200, description: "OK")
print("μƒνƒœ μ½”λ“œ: \(http200Status.statusCode)")  // "μƒνƒœ μ½”λ“œ: 200" 좜λ ₯

νŠœν”Œ(Tuple): μ—¬λŸ¬ νƒ€μž…μ˜ 값을 ν•˜λ‚˜λ‘œ 묢은 볡합 νƒ€μž…μœΌλ‘œ, ν•¨μˆ˜μ—μ„œ μ—¬λŸ¬ 값을 λ°˜ν™˜ν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€.

πŸ“Œ μ˜΅μ…”λ„ (Optionals)

μ˜΅μ…”λ„μ€ 값이 없을 수 μžˆλŠ” 상황을 μ•ˆμ „ν•˜κ²Œ μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ Swift의 핡심 κΈ°λŠ₯μž…λ‹ˆλ‹€:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumberλŠ” Int? νƒ€μž… (μ˜΅μ…”λ„ Int)

μ˜΅μ…”λ„(Optional): 값이 μžˆκ±°λ‚˜ 없을 수 μžˆλŠ” 상황을 ν‘œν˜„ν•˜λŠ” νƒ€μž…μœΌλ‘œ, νƒ€μž… 뒀에 λ¬ΌμŒν‘œ(?)λ₯Ό λΆ™μ—¬ ν‘œν˜„ν•©λ‹ˆλ‹€.

nil

nil은 값이 μ—†μŒμ„ λ‚˜νƒ€λ‚΄λŠ” νŠΉμˆ˜ν•œ κ°’μž…λ‹ˆλ‹€:

var serverResponseCode: Int? = 404
serverResponseCode = nil  // κ°’ μ—†μŒμœΌλ‘œ μ„€μ •

// μ΄ˆκΈ°κ°’ 없이 μ„ μ–Έλœ μ˜΅μ…”λ„μ€ μžλ™μœΌλ‘œ nil
var surveyAnswer: String?  // nil둜 μ΄ˆκΈ°ν™”λ¨

nil: 값이 μ—†μŒμ„ λ‚˜νƒ€λ‚΄λŠ” Swift의 특수 κ°’μž…λ‹ˆλ‹€. μ˜΅μ…”λ„ νƒ€μž…λ§Œ nil을 κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜΅μ…”λ„ 바인딩 (Optional Binding)

μ˜΅μ…”λ„μ— 값이 μžˆλŠ”μ§€ ν™•μΈν•˜κ³  μžˆλ‹€λ©΄ ν•΄λ‹Ή 값을 μž„μ‹œ λ³€μˆ˜λ‚˜ μƒμˆ˜λ‘œ μ‚¬μš©ν•  수 있게 ν•©λ‹ˆλ‹€:

if let actualNumber = Int(possibleNumber) {
    print("λ¬Έμžμ—΄ \"\(possibleNumber)\"은 μ •μˆ˜κ°’ \(actualNumber)μž…λ‹ˆλ‹€.")
} else {
    print("λ¬Έμžμ—΄ \"\(possibleNumber)\"을 μ •μˆ˜λ‘œ λ³€ν™˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€.")
}
// "λ¬Έμžμ—΄ "123"은 μ •μˆ˜κ°’ 123μž…λ‹ˆλ‹€." 좜λ ₯

// 더 짧은 ν˜•νƒœμ˜ μ˜΅μ…”λ„ 바인딩 (Swift 5.7+)
let myNumber: Int? = Int(possibleNumber)
if let myNumber {
    print("μˆ«μžλŠ” \(myNumber)μž…λ‹ˆλ‹€.")
}
// "μˆ«μžλŠ” 123μž…λ‹ˆλ‹€." 좜λ ₯

// μ—¬λŸ¬ μ˜΅μ…”λ„ 바인딩 ν•¨κ»˜ μ‚¬μš©
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber {
    print("\(firstNumber) < \(secondNumber)")
}
// "4 < 42" 좜λ ₯

μ˜΅μ…”λ„ 바인딩: μ˜΅μ…”λ„μ— 값이 μžˆλŠ”μ§€ ν™•μΈν•˜κ³ , μžˆλ‹€λ©΄ κ·Έ 값을 μž„μ‹œ μƒμˆ˜λ‚˜ λ³€μˆ˜λ‘œ μΆ”μΆœν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

λŒ€μ²΄κ°’ 제곡 (Nil-Coalescing Operator)

nil-κ²°ν•© μ—°μ‚°μž(??)λ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜΅μ…”λ„μ΄ nil일 경우 기본값을 μ œκ³΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

let name: String? = nil
let greeting = "μ•ˆλ…•ν•˜μ„Έμš”, " + (name ?? "친ꡬ") + "!"
print(greeting)
// "μ•ˆλ…•ν•˜μ„Έμš”, 친ꡬ!" 좜λ ₯

nil-κ²°ν•© μ—°μ‚°μž(??): μ˜΅μ…”λ„μ΄ nil이면 기본값을 μ œκ³΅ν•˜λŠ” μ—°μ‚°μžμž…λ‹ˆλ‹€.

κ°•μ œ μ–Έλž˜ν•‘ (Force Unwrapping)

μ˜΅μ…”λ„μ— 값이 μžˆλ‹€κ³  ν™•μ‹ ν•  λ•Œ λŠλ‚Œν‘œ(!)λ₯Ό μ‚¬μš©ν•˜μ—¬ κ°•μ œλ‘œ 값을 μΆ”μΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

// κ°•μ œ μ–Έλž˜ν•‘ - μ˜΅μ…”λ„μ— 값이 μ—†μœΌλ©΄ λŸ°νƒ€μž„ μ—λŸ¬ λ°œμƒ
let number = convertedNumber!

κ°•μ œ μ–Έλž˜ν•‘: μ˜΅μ…”λ„μ—μ„œ 값을 κ°•μ œλ‘œ μΆ”μΆœν•˜λŠ” λ°©λ²•μœΌλ‘œ, μ˜΅μ…”λ„μ΄ nil이면 λŸ°νƒ€μž„ μ—λŸ¬κ°€ λ°œμƒν•©λ‹ˆλ‹€.

μ•”μ‹œμ μœΌλ‘œ μ–Έλž˜ν•‘λœ μ˜΅μ…”λ„ (Implicitly Unwrapped Optionals)

μ•”μ‹œμ μœΌλ‘œ μ–Έλž˜ν•‘λœ μ˜΅μ…”λ„μ€ 처음 μ •μ˜λœ ν›„ 항상 값이 μžˆλ‹€κ³  κ°€μ •ν•  수 μžˆμ„ λ•Œ μœ μš©ν•©λ‹ˆλ‹€:

let possibleString: String? = "μ˜΅μ…”λ„ λ¬Έμžμ—΄μž…λ‹ˆλ‹€."
let forcedString: String = possibleString!  // λͺ…μ‹œμ  μ–Έλž˜ν•‘ ν•„μš”

let assumedString: String! = "μ•”μ‹œμ μœΌλ‘œ μ–Έλž˜ν•‘λœ μ˜΅μ…”λ„ λ¬Έμžμ—΄μž…λ‹ˆλ‹€."
let implicitString: String = assumedString  // μžλ™μœΌλ‘œ μ–Έλž˜ν•‘λ¨

μ•”μ‹œμ μœΌλ‘œ μ–Έλž˜ν•‘λœ μ˜΅μ…”λ„: νƒ€μž… 뒀에 λŠλ‚Œν‘œ(!)λ₯Ό λΆ™μ—¬ μ„ μ–Έν•˜λ©°, μ ‘κ·Όν•  λ•Œλ§ˆλ‹€ μžλ™μœΌλ‘œ μ–Έλž˜ν•‘λ©λ‹ˆλ‹€.

πŸ“Œ μ—λŸ¬ 처리 (Error Handling)

SwiftλŠ” ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 쀑 λ°œμƒν•  수 μžˆλŠ” μ—λŸ¬λ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ λ©”μ»€λ‹ˆμ¦˜μ„ μ œκ³΅ν•©λ‹ˆλ‹€:

// μ—λŸ¬ μ •μ˜
enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

// μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆλŠ” ν•¨μˆ˜
func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "μž‘μ—…μ΄ μ „μ†‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€."
}

// μ—λŸ¬ 처리
do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
// "μž‘μ—…μ΄ μ „μ†‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€." 좜λ ₯

// μ—¬λŸ¬ catch λΈ”λ‘μœΌλ‘œ λ‹€μ–‘ν•œ μ—λŸ¬ 처리
do {
    let printerResponse = try send(job: 1440, toPrinter: "Never Has Toner")
    print(printerResponse)
} catch PrinterError.onFire {
    print("ν”„λ¦°ν„°κ°€ λΆˆνƒ€κ³  μžˆμŠ΅λ‹ˆλ‹€!")
} catch let printerError as PrinterError {
    print("ν”„λ¦°ν„° μ—λŸ¬: \(printerError).")
} catch {
    print("μ—λŸ¬: \(error)")
}
// "ν”„λ¦°ν„° μ—λŸ¬: noToner." 좜λ ₯

// try?λ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜΅μ…”λ„ κ²°κ³Ό λ°˜ν™˜
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
// 성곡 μ‹œ String?, μ‹€νŒ¨ μ‹œ nil

Error ν”„λ‘œν† μ½œ: Swiftμ—μ„œ μ—λŸ¬ νƒ€μž…μ€ Error ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

throws: ν•¨μˆ˜κ°€ μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” ν‚€μ›Œλ“œμž…λ‹ˆλ‹€.

throw: μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚€λŠ” ν‚€μ›Œλ“œμž…λ‹ˆλ‹€.

try: μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆλŠ” ν•¨μˆ˜ 호좜 μ‹œ μ‚¬μš©ν•˜λŠ” ν‚€μ›Œλ“œμž…λ‹ˆλ‹€.

do-catch: μ—λŸ¬λ₯Ό ν¬μ°©ν•˜κ³  μ²˜λ¦¬ν•˜λŠ” κ΅¬λ¬Έμž…λ‹ˆλ‹€.

πŸ“Œ μ—­μ„€κ³Ό μ „μ œμ‘°κ±΄ (Assertions and Preconditions)

λŸ°νƒ€μž„μ— 쑰건을 ν™•μΈν•˜μ—¬ μ½”λ“œκ°€ μ˜¬λ°”λ₯΄κ²Œ μ‹€ν–‰λ˜λ„λ‘ 보μž₯ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€:

μ—­μ„€ (Assertions)

역섀은 디버그 λΉŒλ“œμ—μ„œλ§Œ ν™•μΈλ˜λ©°, 쑰건이 falseλ©΄ 앱이 μ’…λ£Œλ©λ‹ˆλ‹€:

let age = -3
assert(age >= 0, "λ‚˜μ΄λŠ” μŒμˆ˜κ°€ 될 수 μ—†μŠ΅λ‹ˆλ‹€.")
// 이 역섀은 μ‹€νŒ¨ν•˜λ©°, 앱이 μ’…λ£Œλ©λ‹ˆλ‹€.

// λ©”μ‹œμ§€ 없이 μ‚¬μš©
assert(age >= 0)

// 이미 쑰건이 체크된 경우
if age > 10 {
    print("λ‘€λŸ¬μ½”μŠ€ν„°λ‚˜ κ΄€λžŒμ°¨λ₯Ό νƒˆ 수 μžˆμŠ΅λ‹ˆλ‹€.")
} else if age >= 0 {
    print("κ΄€λžŒμ°¨λ₯Ό νƒˆ 수 μžˆμŠ΅λ‹ˆλ‹€.")
} else {
    assertionFailure("λ‚˜μ΄λŠ” μŒμˆ˜κ°€ 될 수 μ—†μŠ΅λ‹ˆλ‹€.")
}

assert: 디버그 λΉŒλ“œμ—μ„œ 쑰건을 ν™•μΈν•˜κ³ , 쑰건이 falseλ©΄ 앱을 μ’…λ£Œν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

assertionFailure: 쑰건 확인 없이 λ°”λ‘œ 앱을 μ’…λ£Œν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

μ „μ œμ‘°κ±΄ (Preconditions)

μ „μ œμ‘°κ±΄μ€ 디버그와 ν”„λ‘œλ•μ…˜ λΉŒλ“œ λͺ¨λ‘μ—μ„œ ν™•μΈλ©λ‹ˆλ‹€:

// μΈλ±μŠ€κ°€ λ°°μ—΄ λ²”μœ„ 내에 μžˆλŠ”μ§€ 확인
precondition(index > 0, "μΈλ±μŠ€λŠ” 0보닀 컀야 ν•©λ‹ˆλ‹€.")

// κ΅¬ν˜„λ˜μ§€ μ•Šμ€ κΈ°λŠ₯에 μ‚¬μš©
func fetchData() -> [String] {
    if useMockData {
        return ["ν…ŒμŠ€νŠΈ 데이터"]
    } else {
        preconditionFailure("아직 κ΅¬ν˜„λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
    }
}

precondition: 디버그와 ν”„λ‘œλ•μ…˜ λΉŒλ“œ λͺ¨λ‘μ—μ„œ 쑰건을 ν™•μΈν•˜κ³ , 쑰건이 falseλ©΄ 앱을 μ’…λ£Œν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

preconditionFailure: 쑰건 확인 없이 λ°”λ‘œ 앱을 μ’…λ£Œν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

fatalError: μ΅œμ ν™” μ„€μ •κ³Ό 관계없이 항상 앱을 μ’…λ£Œν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

πŸ“Œ μš”μ•½

Swift의 κΈ°λ³Έ κ°œλ… 쀑 μ£Όμš” λ‚΄μš©μ„ μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€:

  • λΆ€μšΈ(Bool): μ°Έ/κ±°μ§“ 값을 ν‘œν˜„ν•˜λŠ” 논리 νƒ€μž…
  • νŠœν”Œ(Tuple): μ—¬λŸ¬ 값을 ν•˜λ‚˜μ˜ 볡합 κ°’μœΌλ‘œ κ·Έλ£Ήν™”
  • μ˜΅μ…”λ„(Optional): 값이 μžˆκ±°λ‚˜ 없을 수 μžˆλŠ” 상황을 μ•ˆμ „ν•˜κ²Œ 처리
    • nil: 값이 μ—†μŒμ„ λ‚˜νƒ€λ‚΄λŠ” 특수 κ°’
    • μ˜΅μ…”λ„ 바인딩: if let을 μ‚¬μš©ν•΄ μ˜΅μ…”λ„ 값을 μ•ˆμ „ν•˜κ²Œ μΆ”μΆœ
    • nil-κ²°ν•© μ—°μ‚°μž(??): κΈ°λ³Έκ°’ 제곡
    • κ°•μ œ μ–Έλž˜ν•‘(!): μ˜΅μ…”λ„μ—μ„œ 값을 κ°•μ œλ‘œ μΆ”μΆœ
  • μ—λŸ¬ 처리: do-try-catch ꡬ문을 μ‚¬μš©ν•œ μ—λŸ¬ 처리 λ©”μ»€λ‹ˆμ¦˜
  • μ—­μ„€κ³Ό μ „μ œμ‘°κ±΄: λŸ°νƒ€μž„μ— μ½”λ“œμ˜ 쑰건을 ν™•μΈν•˜λŠ” 방법