스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

14. 스파르타 코딩 클럽 - 옵셔널

seongpil Heo 2025. 3. 17. 16:56

1. 옵셔널 (Optional)

☝️ Swift는 기본적으로 nil(값 없음)을 허용하지 않지만 개발을 하다 보면 값이 없는 경우가 생기게 됩니다!

💡 이럴 때 사용할 수 있는 것이 Optional 입니다.
     Optional을 사용하면 값이 없는 상황을 개발할 수 있습니다.

 

struct Person {
	var name: String
    var age: Int
    var car: String? // ?를 붙여서 옵셔널 타입이라고 명시하여 값이 없을수도 있다고 알려줄 수 있어요
    
    func introduce() {
    	print("안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다.")
    }
}

Swift는 기본적으로 nil을 허용하지 않지만, Optional를 사용하면 값이 없을 수 있는 상태를 안전하게 처리할 수 있습니다.

  • nil 키워드는 값이 없음을 의미합니다.
  • 값이 없을 수 있는 상태를 옵셔널이라고 하며, 옵셔널 타입은 Nil 값을 저장할 수 있습니다.
  • 기존 타입 뒤에 ? 키워드를 사용하여 옵셔널 타입으로 선언할 수 있습니다.
    • 기본 타입 (Int, String, Float ...)
    • Collection Type (Array, Dictionary, Set ...)
    • Custom Type (struct, class, enum)
    • 익명함수인 클로저
  • 값을 할당할 때는 기존의 타입과 동일하게 사용하면 됩니다.
  • 옵셔널 타입의 값에 접근하면 Optional로 감싸진 값이 나옵니다. Optional로 래핑 된 값이라고 부릅니다.

옵셔널 타입 선언 및 사용 방법

옵셔널 타입으로 선언하는 방법

// 기본 타입 옵셔널로 선언하기
var intValue: Int?
var stringValue: String?

// Collection Type 옵셔널로 선언하기
var array: [String]?
var dictionary: [String: String]?

// struct, class, enum을 타입처럼 사용하므로 Optional로 선언할 수 있습니다.
struct Person {
	let name: String
}

var optionalPerson: Person?

// 클로저 옵셔널로 선언하기
var closure: (() -> Void)?

 

옵셔널 타입을 사용하는 방법 (할당과 접근)

// 값을 할당할 때는 기존의 타입과 동일하게 사용하면 됩니다.
var intValue: Int? = 1

var stringValue: String?
stringValue = "안녕하세요"

var nilValue: String? = nil

struct Person {
	let name: String
}

var optionalPerson: Person? = Person(name: "Brody")

var closure: (() -> Void)? = {
	print("Fire")
}

// 값에 접근하면 Optional로 래핑된 값으로 나옵니다.
print(intValue) // Optional(1) : 옵셔널로 래핑된 값 1이 출력됩니다.
print(stringValue) // Optional("안녕하세요") : 옵셔널로 래핑된 값 "안녕하세요"가 출력됩니다.
print(nilValue) // nil : 값이 없음을 의미하는 nil이 출력됩니다.

Optional 값은 기본 타입과 연산이 불가능합니다.

  • 옵셔널 타입과 기본 타입은 다른 타입이기 때문에 연산이 불가능합니다.
  • 비교 연산자 사용 가능합니다.
var intValue: Int? = 5
intValue += 5 // Error 발생
			  // Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
              // Int? 타입은 Int 타입과 다르기 때문에 컴파일 오류 발생!
  • 옵셔널과 기본 타입의 차이로 인해 연산이 불가능합니다.
  • 옵셔널 값에서 값만 추출하는 언래핑 작업을 통해 기본 타입으로 변환하여 사용해야 합니다.

2. 옵셔널 체이닝 (Optional Chaining)

📚 옵셔널 체이닝은 옵셔널 타입을 포함하는 폭잡한 데이터 구조에서 옵셔널 값이 nil 인지를 간결하게 체크하고 접근할 수 있는 방법입니다.
  • 여러 중첩된 프로퍼티나 메소드 호출을 한줄로 처리할 수 있으며, 중간에 nil이 있는 경우 자동으로 nil을 반환합니다.
  • 옵셔널 체이닝은 옵셔널 값에 대해 접근할 때마다 안전하게 처리할 수 있습니다.
  • 옵셔널 값에 접근할 때 프로퍼티나 메소드 이름 뒤에 ?를 붙여서 체이닝할 수 있습니다.
struct Person {
    var name: String = "Default Name"
    var animal: Animal? // 사람 struct은 반려동물이 있을 수도 있고 없을수도 있어요!
}

struct Animal {
    let name: String
    var age: Int? // 언제 태어난지 모른다면 나이를 정할 수 없어서 Optional 타입으로 설정
}

let person: Person? = Person(name: "Brody", animal: nil)
print(person?.animal?.name) // person은 옵셔널 값이여서 ?를 붙였으며, nil이 아니에요. 다음으로 넘어갑니다.
                            // animal?을 확인해보니 nil이여서 nil을 반환합니다.
                            // 출력 값 : nil                      

let person2 = Person(name: "Brody", animal: Animal(name: "Dog", age: 5))
print(person2.animal?.name)  // person2는 옵셔널이 아니기 때문에 ?를 붙이지 않아요
                             // animal?을 확인하니 값이 있군요! 다음으로 넘어갑니다.
                             // name은 값이 있어서 Animal의 "Dog"를 옵셔널 래핑하여 반환합니다
                             // 출력 값 : Optional("Dog")

 

let stringValue: String? = "안녕하세요"

print(stringValue?.count) // Optional(5)
													// 옵셔널 체이닝으로 연결되어 있기 때문에 Optional로 래핑된 값이 출력됩니다. 
													// 옵셔널 값이기 때문에 언래핑하여 사용하면 됩니다!
  • person?.animal?.name  <- 처럼 사용됩니다.
    • 옵셔널 값에 ?를 붙인 후 .을 사용하면 중첩된 프로퍼티나 메소드에 접근할 수 있습니다.
    • 중첩된 값 중 하나라도 nil이면 전체값을 nil을 반환합니다.
    • 중첩된 값이 모두 nil이 아니라면 Optional로 래핑된 값을 반환합니다

3. 실습하기

import UIKit

// 옵셔널 실습하기

// 1. 옵셔널 기본 실습
// 변수나 상수에 타입을 넣으면서 테스트 해보기

var stringValue: String?
print(stringValue)
stringValue = "Hello"
print(stringValue)


var intValut: Int? = 123
var floatValue: Float? = 15.1
var boolValue: Bool? = false

print(intValut)
print(floatValue)
print(boolValue)

// var intValut2 = intValut + 15 // Error 발생 (옵셔널 타입과 기본 타입은 연산 불가능)

print(intValut == 123)


// 2. struct 옵셔널 체이닝 사용하기
struct Dog {
    var toy: String?
}

struct Person {
    var pet: Dog?
}

let person1: Person? = nil
print(person1?.pet?.toy) // person1이 nil이어서 최종 nil 출력

let person2: Person? = Person(pet: nil)
print(person2?.pet?.toy) // pet이 nil이어서 최종 nil 출력

let person3: Person? = Person(pet: Dog(toy: "AAA"))
print(person3?.pet?.toy) // Optional("AAA") 출력


// 3. 클로저 옵셔널 체이닝 사용하기
var optionalClosure: ((Int) -> Void)? = { value in
    print(value)
}

optionalClosure?(3) // 3 출력