간단하게 말해서 이거다.
Optional은 값이 있을 수도, 없을 수도 있는 타입이다.
Swift는 타입 안정성을 굉장히 중요하게 생각한다.
그래서 nil이라는 개념을 아무 변수에나 막 쓸 수 없다.
var name: String = nil // 에러
var name: String? = nil // 에러 안남
즉, nil을 쓸 수 있으려면 명시적으로 ?를 붙여서 옵셔널 타입이라고 선언해줘야 한다.
여기서 궁금한 게 하나 생긴다.
다른 언어들도 이런 옵셔널 개념이 있을까?
한번 비교해보자.
| 언어 | null 표현 | 옵셔널 처리 방식 |
|---|---|---|
| JavaScript | null, undefined | 아무 변수나 null 가능 |
| Python | None | 역시 아무 변수나 None 가능 |
| Java | null, Optional<T> | 옵셔널은 선택사항 |
| Swift | nil, Optional<T> | 옵셔널 타입만 nil 가능 |
옵셔널이 아니면 nil을 절대 못 넣는다.
var age: Int? = nil
if age == nil {
print("age는 nil입니다")
}
물론 가능하다. == nil을 직접 비교하는 것도 된다.
하지만 Swift에서는 더 깔끔하고 안전한 방식인 **if let이나 guard let**을 권장한다.
if let vs guard let옵셔널을 안전하게 꺼내 쓰는 대표적인 방식이 이 두 가지다.
if letif let value = optional {
// value 사용 가능
print(value)
}
print(value) // 에러발생
조건이 true일 때만 실행된다.
하지만 블록 내부에서만 value를 쓸 수 있다는 단점이 있다.
guard letguard let value = optional
else {
print(value)
return
}
print(value)
// 여기부터는 value가 nil이 아닐경우 구분을 타게 되어이음
이건 다르다.
조건이 false일 때 탈출하기 때문에 이후 코드에서는 언래핑된 value를 계속 쓸 수 있다.
중첩 코드 없이 플랫하게 유지할 수 있어서 훨씬 가독성이 좋다.
| 비교 항목 | if let | guard let |
|---|---|---|
| 조건 실패 시 처리 | 블록 실행 안됨 | else 블록 실행 후 탈출 |
| 바인딩된 값 사용 가능 범위 | if 블록 내부만 가능 | 이후 전체 코드에서 사용 가능 |
| 코드 구조 | 중첩되기 쉬움 | 플랫하게 깔끔하게 유지됨 |
foo.address?.firstLine
옵셔널 체이닝은
중간의 어떤 값이 nil이라도 에러가 나지 않고, 전체가 nil이 되도록 안전하게 처리하는 방식이다.
예를 들어 아래와 같은 중첩된 구조를 생각해보자.
if let address = foo.address {
if let line = address.firstLine {
print(line)
}
}
이걸 한 줄로 줄이면 다음과 같은 코드로 정리 할 수 있다.
if let line = foo.address?.firstLine {
print(line)
}
엄청 깔끔해진다.
옵셔널 바인딩은 하나씩만 하는 게 아니다.
if let address = foo.address, // 중간 값 바인딩
let firstLine = address.firstLine { // 최종 값 바인딩
print(firstLine) // 최종 값 사용가능
print(address) // 중간 값 사용가능
}
이렇게 ,로 이어서 순차적으로 바인딩할 수 있다.
중간 값을 쓰고 싶다면 이 방식이 유용하다.
옵셔널 체이닝은 결과만 얻을 수 있지만, 바인딩은 중간 값까지 활용 가능하다.
사실 옵셔널은 enum이다.
// optional 내부 구조
enum Optional<Wrapped> {
case none // == nil
case some(Wrapped) // 값이 있을 때
}
그래서 이런 코드도 완전히 가능하다.
switch age {
case .none:
print("Age is nil")
case let .some(value):
print("Age is \(value)")
}
즉, 옵셔널도 그냥 열거형일 뿐이다.
다만 Swift가 너무 잘 숨겨줘서 그런 줄 몰랐던 거다.
옵셔널을 선언할 때, 자료형 뒤에 ?를 붙여주는데 이건 optional자료형으로 wrapping하는 거라고 보면 된다.
guard lastName == nil else { ... }는 무슨 의미?여기서 궁금한 게 또 하나 생긴다.
조건에 부정을 넣는 것도 가능할까?
guard lastName == nil else {
// lastName이 nil일 경우 실행
return
}
// nil이 아니면 실행
이건 lastName이 nil이 아니면 return, 즉
nil일 경우에만 계속 진행하겠다는 의미다.
guard는 결국 false일 때 탈출하는 거니까
조건을 부정으로 써도 전혀 문제없다.
옵셔널은 값이 있을 수도 없을 수도 있는 Swift만의 안전한 타입 시스템이며,
옵셔널 바인딩(if let,guard let), 체이닝, nil 병합(??) 등 다양한 방식으로 다룰 수 있다.
옵셔널..... 자주 쓰이는 방식이니 잘 알아둬야겠다.