옵셔널 체이닝은 옵셔널 언래핑하는 방법이라기보다는, 인스턴스에 옵셔널 프로퍼티의 내부 프로퍼티에 접근할 때 옵셔널을 다루는 방법이다.
이해를 돕기 위해 코드와 함께 설명하겠다.
struct Pet {
let name: String
}
struct Person {
let name: String
var pet: Pet?
}
var devapploper = Person(name: "devapploper", pet: nil)
print(devapploper.pet) // nil
Pet 구조체와 Person 구조체가 있다. 각각 name 상수가 있다. Person 구조체의 pet이라는 프로퍼티는 옵셔널 Pet 타입을 가지고 있다.
devapploper의 pet 프로퍼티에 접근하여 값을 출력하면 nil이 출력된다. pet은 옵셔널 Pet 타입이고 Person 인스턴스 생성시에 nil로 표기해주었기 때문이다.
devapploper가 어느날 미미라는 강아지가 생겼다.
struct Pet {
let name: String
}
struct Person {
let name: String
var pet: Pet?
}
var devapploper = Person(name: "devapploper", pet: nil)
devapploper.pet = Pet(name: "미미")
print(devapploper.pet) // Optional(Pet(name: "미미"))
print(devapploper.pet!) // Pet(name: "미미")
devapploper의 pet 프로퍼티에 다시 접근하여 값을 출력하면 이번에는 옵셔널에 감싸진 Pet 인스턴스가 출력된다. 앞서 말했듯 pet은 옵셔널이기 때문에 옵셔널 언래핑을 하지 않으면 옵셔널에 감싸져 있는채로 값이 출력된다. 바로 다음 줄에 강제 언래핑을 이용하여 pet 프로퍼티의 옵셔널을 벗겨내면 내부의 값이 그대로 출력이 되는 것을 확인할 수 있다.
미미에게 이름을 지어주고는 미미가 집으로 온 다음날에 바로 잊어버렸다. Pet 타입의 name 프로퍼티에 접근하여 이름을 알아내보자.
struct Pet { ... }
struct Person { ... }
var devapploper = Person(name: "devapploper", pet: nil)
devapploper.pet = Pet(name: "미미")
// 다음날
print(devapploper.pet.name) // Error! Value of optional type 'Pet?' must be unwrapped to refer to the 'name' of wrapped base type 'Pet'
이렇게 하면 위의 에러가 나면서 컴파일에 실패한다. 위 에러는 "Pet
타입의 name
프로퍼티에 접근하기 전에 Pet?
타입의 옵셔널이 언래핑 되어야합니다." 라는 뜻으로 해석할 수 있다.
즉, devapploper.pet 은 옵셔널 타입이고, pet은 옵셔널에 감싸져있어서 pet의 내부 프로퍼티인 name에 접근하기전에 옵셔널을 벗겨내라는 말이다.
그러면 이전에 알아본 옵셔널 언래핑 방법 중 하나인 옵셔널 바인딩으로 옵셔널을 벗겨내서 pet의 name 타입에 접근해보자.
var devapploper = Person(name: "devapploper", pet: nil)
devapploper.pet = Pet(name: "미미")
// 다음날
if let myPet = devapploper.pet {
let name = myPet.name
print(name) // "미미"
}
이렇게 하면 myPet 상수에 옵셔널이 벗겨진 상태로 pet 인스턴스가 할당되고, if 문 내에서 pet 인스턴스의 name 프로퍼티에 접근하여 잊어버렸던 애완동물의 이름을 알아낼 수 있다.
그런데, 위에서는 옵셔널 언래핑을 한번만 했지만, 옵셔널 언래핑을 3번, 5번 거쳐서 내부의 프로퍼티를 접근해야된다면 매번 옵셔널 바인딩을 사용해서 if 문이 계속해서 중첩되는 상황이 벌어질 수도 있다.
if문이 서너개가 중첩되는 건 읽기에도 좋지 않고, 일일이 작성하는 것도 번거롭다.
이러한 상황에서 옵셔널 체이닝을 이용하면 좀 더 편리하게 인스턴스의 옵셔널 프로퍼티의 내부 프로퍼티에 접근할 수 있다.
옵셔널 체이닝은 옵셔널 프로퍼티에 접근하고, 내부의 프로퍼티를 접근하기 전에 느낌표나 물음표를 붙여서 사용한다.
var devapploper = Person(name: "devapploper", pet: nil)
devapploper.pet = Pet(name: "미미")
// 다음날
print(devapploper.pet?.name) // Optional("미미")
print(devapploper.pet!.name) // "미미"
물음표로 옵셔널 체이닝을 하면, 접근한 프로퍼티의 값은 항상 옵셔널에 감싸져있고, 느낌표로 옵셔널 체이닝을 하면 옵셔널 프로퍼티를 강제 언래핑하여 접근하기 때문에 값이 옵셔널에 감싸져있지 않다.
옵셔널 체이닝을 통해 접근한 옵셔널에 감싸져 있는 값은 옵셔널 바인딩과 함께 사용하여 옵셔널을 간단하게 벗겨낼 수 있다.
var devapploper = Person(name: "devapploper", pet: nil)
devapploper.pet = Pet(name: "미미")
// if - optional binding
if let petName = devapploper.pet?.name {
print(petName) // "미미"
}
// guard - optional binding
guard let petName2 = devapploper.pet?.name else { return }
print(petName2) // "미미"
느낌표를 이용한 옵셔널 체이닝은 강제 언래핑을 사용할때와 마찬가지로 언래핑 시에 nil이 발견되면 프로그램이 비정상적으로 종료된다. 그렇기 때문에 이것 또한 사용시 각별히 주의해야한다.
옵셔널에 대한 7가지 개념