Optional
Optional의 정의 이해하기
Swift
의 Optional
데이터 타입은 다른 프로그래밍 언어에서는 보기 드문 새로운 개념입니다. Swift
는 안정성 높은 언어이기 때문에 오류를 별로 좋아하지 않습니다. 따라서 Optional
타입의 목적은 변수 또는 상수에 값이 할당되지 않은 상황을 처리하기 위해 안전하고 일관된 접근 방식을 제공하는 것입니다.
또한 null safety
를 보장하는 Swift
는 nil
의 가능성을 가지고 있는 변수를 나타내기 위해서 사용하고 readability
와 code dfficiency
에 도움을 줍니다. 이 때문에 complietime
에서 잡을 수 있는 에러의 종류를 늘려주니 개발자에게 도움됩니다.
옵셔널 사용법에 앞서서 오늘 공부할 Optional
의 4가지를 보고 가겠습니다.
//Forced unwrapping == 억지로 내용을 까보기 // 강제 언래핑
//Optional binding (if let) == 부드럽게 내용을 까보기1 // 옵셔널 바인딩
//Optional binding (guard) == 부드럽게 내용을 까보기2 // 옵셔널 바인딩
//Nil coalescing == 내용을 까봤더니, 값이 없으면 디폴트 값을 주자
Optional 사용법
Optional
을 사용하기 위해서는 데이터 타입 어노테이션 뒤에 ?
문자를 두어 Optional
이 되도록 합니다. 아래는 정수형 Optional
을 선언한 것입니다:
var user: Int?
user
는 정숫값이 할당되거나 아무런 값도 할당되지 않을 수 있다는 것을 선언한 것입니다. 이를 내부적으로 컴파일러와 런타임의 관점에서 보면, 어떤 값도 할당되지 않은 Optional
은 실제로 nil
값을 자동으로 참조하게 됩니다.
Optional
값에 데이터가 할당 되어있는 있는지를 확인은 다음과 같이 할 수 있습니다:
var index: Int?
if index != nil {
// index 안에 변수가 있음
} else {
// index 변수는 값이 할당되어 있지 않다
}
만약 Optional
에 값이 할당되었다면, 해당 값이 Optional
내에 래핑되었다고 말할 수 있습니다.
강제 언래핑
Optional
안에 래핑된 값을 사용할 때는 강제 언래핑이라는 개념을 이용하게 됩니다. 간단히 말해, 래핑된 값은 Optional
데이터 타입에서 옵셔널 이름 뒤에 !
를 붙여 강제로 추출할 수 있습니다.
강제 언래핑의 개념을 좀 더 자세히 살펴보기 위해 다음과 같이 할 수 있습니다:
var index: Int?
index = 3
var junhaArray = ["J", "U", "N", "H", "A"]
if index != nil {
print(junhaArray[index!])
} else {
print("index does not contain a value")
}
이 코드는 이름을 나타내는 문자열 배열의 인덱스를 담는 Optional
변수를 사용하고 있습니다. 만약 index
Optional
변수에 값이 할당되면, 배열의 해당 위치에 있는 알파벳이 콘솔에 출력됩니다. index
변수가 Optional
타입이기 때문에, 변수명 뒤에 !
를 붙여 값을 강제로 언래핑하게 됩니다.
print(junhaArray[index!])
반대로, 앞의 코드에서 느낌표를 빼서 index 변수가 언래핑되지 않는다면 컴파일러는 다음과 같은 에러가 납니다.
만약에, 위 코드에서 느낌표를 빼고 index 변수가 언래핑되지 않는다면 아래와 같이 컴파일러가 에러를 보여줍니다:
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
!
는 암시적 추출 옵셔널이라는 것을 이해하고 ?
는 Optional
이라는 것을 이해하셨나요? !
는 Optional
이 아닌 일반 값과 연산이 가능합니다. 즉 !
로 Optional
을 선언한 변수의 경우 개발자가 알아서 nil
이 아닌 경우에만 이를 사용할 것이라고 믿는 것입니다. 때문에 지나치게 !
를 많이 사용하게 된다면 runtime error
의 가능성이 높아집니다. 추가적으로 ?
로 선언된 경우에는 다른 일반값들과 불가능합니다.
Optional 언래핑을 하지 않는다면?
Optional
언래핑을 하지 않고 데이터를 추출하게 된다면 어떻게 될까요? 아래는 그에 대한 예시 코드입니다:
var user: String?
var user = "박준하"
print(user) // Optional("박준하")
위와 같이 저희가 원하는 데이터가 아닌 Optional
이라는 것에 감싸진 데이터를 볼 수 있습니다. 위 코드처럼 데이터가 확정적으로 넣어져있다고 한다면 강제 Optional
을 사용할 수 있지만 안전하게 언래핑을 하는 방법은 없을까요?
Optional 바인딩
지금부터는 강제 언래핑과 같은 무식한 방법이 아닌 옵셔널로 할당된 값을 Optional
바인딩을 이용해서 임시 변수나 상수에 할당하는 방법을 알아봅시다:
var optionalName: String? = "Junha"
if let constantName = optionalName {
print("The name is \(constantName)") // 출력: The name is Junha
}
if var variableName = optionalName {
variableName = "JJunhaa"
print("The name is \(variableName)") // 출력: The name is JJunhaa
}
optionalName = nil
// if-let 구문을 사용한 옵셔널 바인딩 (optionalName이 nil인 경우)
if let constantName = optionalName {
print("The name is \(constantName)") // 이 부분은 실행되지 않음
} else {
print("optionalName is nil") // 출력: optionalName is nil
}
위 코드는 세 가지 작업을 수행합니다. 첫 번째 작업은 주어진 Optional
이 값을 가지고 있는지를 확인하는 것이고, 두 번째 작업은 Optional
변수가 값을 가지고 있을 때, 그 값을 새 변수에 할당하고 이를 수정하는 것입니다. 세 번째 작업은 if-let 구문을 사용해 Optional
이 nil인 경우를 처리하는 것입니다. 이처럼 앞서 언급한 강제 언래핑 예제를 Optional binding
을 사용하는 방식으로 수정할 수 있습니다.
var index: Int? = 3
var junhaArray = ["J", "U", "N", "H", "A"]
if let index = index {
print(junhaArray[index]) // 출력: "H"
} else {
print("index does not contain a value")
}
func printArrayValue(at index: Int?) {
guard let index = index else {
print("index does not contain a value")
return
}
print(junhaArray[index]) // 출력: "H"
}
printArrayValue(at: index)
위 코드는 index
변수에 할당된 값을 Optional binding
을 통해 nameValue
라는 임시 상수에 할당하고, 이를 배열의 인덱스로 사용합니다. nameValue
는 if
구문 내에서만 유효한 상수이기 때문에, if 구문이 종료되면 이 상수는 더 이상 사용할 수 없습니다. 그렇기 때문에 동일한 이름의 변수를 다른 곳에서 다시 사용해도 충돌이 발생하지 않습니다.
Optional 바인딩의 역사
Swift 5.7부터는 if let
뿐만 아니라 guard let
구문도 도입되어, Optional
을 좀 더 간편하게 처리할 수 있게 되었습니다. 굳이 한 번만 사용할 값을 미리 선언하지 않고, if let
을 사용해 바로 언래핑하는 방법이 생긴 것이죠.
본래의 Swift 5.7 버전 전에는 아래와 같이 사용했습니다:
// guard let
guard let self = self else { return }
// if let
if let testData = testData {
print(testData)
}