* 해당 글은 개인 공부자료이므로 오류가 있을 수 있습니다.
옵셔널이란 ?
- 값이 있을 수도 있고 없을 수도 있는 변수나 상수를 나타내는 데이터
- 타입 뒤에 "?" 또는 "!"를 사용하여 선언
옵셔널(Optional)은 값이 있을 수도 없을 수도 있는 경우에 사용되는 데이터 타입이며 ?
,!
로 표현합니다.
Swift로 작성된 코드를 보다 보면 타입뒤에 Int?
, String?
, Int!
, String!
와 같이 사용되는 것을 보신적 있을겁니다. 이 기호가 바로 옵셔널 기호입니다.
반대로, ?
가 없는 일반적인 데이터 타입일 때는 nil
을 할당할 수 없습니다. ( *nil은 값이 없음을 의미합니다. = null )
이렇게 옵셔널로 값의 존재 여부를 명시적으로 표현하는 것은 코드 안정성과 신뢰성을 높이는데 도움이 됩니다.
예제를 통해 옵셔널을 알아보고, ?
, !
의 차이점도 같이 확인해보겠습니다.
옵셔널의 변수 선언은 데이터 타입 뒤에 ?
가 붙은 형식으로 되어있습니다.
먼저 ?
가 붙은 옵셔널변수와 그렇지 않은 일반 변수의 코드를 보겠습니다.
// "myOptionalString"이라는 String타입을 "옵셔널변수"로 선언
var myOptionalString: String?
// "myString"이라는 String타입을 선언
var myString: String
// "myOptionalString"의 옵셔널 변수에 nil 할당
myOptionalString = nil
//"myString"의 일반 변수에 nil 할당, 그러나 불가능
myString = nil // Error
이렇게 nil값을 할당받기 위해서는 ?
기호를 사용하여 옵셔널로 선언해 주어야만 정상적으로 할당이 가능합니다.
정상적으로 선언이 완료되었으면 사용할 준비가 끝났습니다.
이제 옵셔널을 사용하기 위해서는 언래핑(Unwrapping)을 해주어야 하는데요.
다음은 이 언래핑을 하기위해서 알아보겠습니다.
먼저 왜? 추출해서 사용해야되는지 부터 알아보겠습니다.
앞에서 옵셔널은 값이 있을 수도 없을 수도 있는 데이터 타입이라고 말씀드렸습니다.
옵셔널 타입 변수를 일반 데이터 타입변수에 할당이 가능할까요?
var myOptionalString: String? = "Hello, World!"
var mySecondOptionalString: String?
var myString: String
mySecondOptionalString = myOptionalString // Success
print(myOptionalString) // result : Optional("Hello,World!")
print(mySecondOptionalString) // result : Optional("Hello,World!")
myString = myOptionalString // Error
myOptinalString
을 mySecondOptionalString
에 할당이 가능합니다. 같은 옵셔널 타입이기 때문입니다.
하지만 myOptionalString
을 myString
에 대입한다면 컴파일 에러가 발생합니다.
있을 수도 없을 수도 없는 이 myOptionalString
옵셔널을 항상 값이 있어야하는 일반 데이터 타입myString
에 할당한다면 실제 코드 실행될 때 까지 이 값이 있는지 없는지 확인할 방법이 없기 때문입니다.
이 문제를 해결하기 위해서 Optional("Hellow, World!")
에서 옵셔널을 해제해줘야 합니다.
이 때 사용하는 것이 옵셔널 언래핑(Optional Unwrapping)입니다.
옵셔널 언래핑은 몇가지 방법이 있습니다. 그 중 강제 언래핑에 대해 알아보겠습니다.
!
를 붙혀 강제로 값을 추출nil
이면 런타임 에러 발생var myOptionalString: String? = "Hello, World!"
print(myOptionalString) // result : Optional("Hello,World!")
print(myOptionalString!) // result : "Hello,World!"
myOptionalString = nil
print(myOptionalString!) // Error
뒤에 !
만 붙혀주면 옵셔널을 풀 수가 있습니다. 간단하지만 nil
일 경우 런타임 에러가 발생합니다.
지금은 코드가 짧기 때문에 에러가 생겨도 금방 찾을 수 있지만 코드양이 많아지고 복잡해지면 힘들어집니다.
값이 "100% 무조건 있다!"가 아니라면 강제 언래핑은 지양하는것이 좋겠습니다.
if let
, guard let
을 사용var myOptionalString: String? = "Hello, World!"
// if let 을 사용한 옵셔널 바인딩
if let myString = myOptionalString {
// myOptional is not nil
print(myString) // result : "Hello, World!"
} else {
// myOptional is nil
print("myOptional is nil!")
}
// guard let을 사용한 옵셔널 바인딩
guard let myString = myOptionalString else {
// myOptional is nil
return
}
// myOptional is not nil
print(myString) // result : "Hello, World!"
if let
, guard let
두 방식의 사용방법은 비슷합니다. 옵셔널 변수를 myString
상수에 할당해서 사용하는 방식입니다. 만약 옵셔널이 nil
이면 해당 에러에 대한 처리만 잘 해준다면 런타임 에러는 발생 하지 않기 때문에 안전한 방법이라고 볼 수 있습니다.
if let
은 옵셔널 변수에서 할당 받은 상수는 if문 내부에서만 사용가능하고 nil경우 에러처라가 필요 없습니다.
guard let
은 할당 받은 상수는 계속 사용이 가능하고 nil 값에 대한 에러 처리가 필요합니다.
nil일 때 해당 메서드나 루프에서 탈출을 해야되는 코드를 작성할 때는 guard let
을
nil일 때도 코드를 계속 진행하고자 할 때는 if let
을 사용하면 되겠습니다.
옵셔널체이닝은 단어 뜻 그대로 점(.)을 사용하여 henry?.car.model
또는 herny?.car?.model
과 같이 이어진 여러 개의 속성을 접근하여 사용합니다.
?
는 생략struct Car {
var model: String?
var color: String
}
struct Person {
var name: String
var age: Int
var car: Car?
init(name: String, age: Int, carModel: String, color: String) {
self.name = name
self.age = age
car = Car(model: carModel, Color: color)
}
}
// "henry"변수에 Person 옵셔널 타입 할당
var henry: Person? = Person(name: "henry", age: 10, carModel: "K5", color: "white")
print(henry.car.model) // Error
print(henry?.car?.model) // result : Optional("K5")
henry
이름의 Person 옵셔널 변수에 Person값을 할당하였습니다.
henry
와henry
의car
는 옵셔널 이기 때문에 ?
를 붙혀주지 않으면 에러가 발생힙니다.
여기서 henry
,car
,model
중 하나라도 nil 값이면 옵셔널 타입이 아니여도 결과는 에러가 아닌 nil값이 할당되어 런타임에러를 방지하여 코드의 안정성을 높일 수 있습니다.
또 henry
는 옵셔널 변수 이기때문에 옵셔널바인딩을 이용하여 언래핑이 가능합니다.
// 옵셔널 체이닝 사용 x
f let henry = henry {
if let car = henry.car {
print(car.model)
if let model = car.model {
print(model)
}
}
}
// 옵셔널 체이닝 사용 o
if let model = henry?.car?.model {
print(model) // result : "K5"
}
옵셔널 체이닝을 이용하면 위와 같이 코드를 간단하게 작성할 수 있습니다.
!
기호를 사용nil
값 할당 가능nil
값일 때 접근하면 런타임 에러 발생var myOptionalString: String!
var myString: String
myOptionalString = nil
print(myOptionalString) // result : nil
// 'myOptionalString'에 "Hello"문자열 할당
myOptionalString = "Hello"
// 'myOptionalString'은 옵셔널이기 때문에 일반타입의 변수에 할당하지 못함
myString = myOptionalString // Error
// 단독 사용은 옵셔널로 출력
print(myOptionalString) // result : Optional("Hello")
// 접근 시 자동으로 언래핑
print(myOptionalString + "World!") // result : "HelloWorld"
암시적 언랩 옵셔널은 선언할 때 !
기호로 표현합니다. 옵셔널이기 때문에 nil
값을 할당이 가능합니다.
잠재적으로 언랩을 하겠다라고 사용하는 것인데, 옵셔널이지만 이 옵셔널타입 변수에 접근을 하면 자동으로 강제 언랩핑을 하는것과 동일합니다.
print(myOptionalString + "World!")
에서 접근을 하게되면 강제로 언래핑이 되어 결과값이 일반 String타입으로 나오게됩니다.
선언할 때 강제 언래핑하는것 말고는 일반 옵셔널과 다를게 없는데 왜 사용할까?
옵셔널에 값이 무조건 있다면 강제 언래핑으로 이 옵셔널에는 값이 무조건 들어있다 라는 것을 명확하게 알 수 있어 코드의 가독성과 편리성을 높일 수 있다고합니다.
하지만 혹시나 nil
이 들어가게 된다면 앱이 죽어버리니 사용은 강제 언래핑과 같이 지양하는것이 좋을것같습니다.
이상으로 옵셔널(Optional)과 그 옵셔널을 사용하기 위해 옵셔널을 해제하는 옵셔널 언래핑(Optional Unwrapping)을 알아보았습니다.
잘못된 정보가 있다면 댓글로 알려주시면 감사하겠습니다.