[ Swift ] #3. 옵셔널(Optional)

ma.caron_g·2022년 6월 29일
0

Swift

목록 보기
3/4
post-thumbnail

[ 옵셔널 (Optional) ]

Swift가 가지고 있는 가장 큰 특징 중 하나가 바로 옵셔널(Optional)입니다.
직역하면 "선택적인" 이라는 뜻입니다.

값이 있을 수도 있고 없을 수도 있는 것을 나타냅니다.

예를 들어,
문자열의 값이 있으면 "가나다"가 될 것입니다.
보통 값이 없다면 ""으로 표현합니다. 하지만 ""도 엄연히 값이 있는 문자열입니다.
정확히는 "값이 없다"가 아니고 "빈 값"입니다.

값이 없는 문자열은 nil로 표현합니다.

정수를 예를 들면,
값이 있다면 123 입니다.
하지만 값이 없다면 0이라고 표현하지 않습니다. 0이라는 값도 숫자 '값' 입니다.
이 경우에도 값이 없는 정수는 nil 입니다.

배열과 딕셔너리도 값이 없는 것이이 아니고 비어 있을 뿐입니다.
배열과 딕셔너리도 마찬가지로 값이 없는 경우 nil입니다.

이렇게 값이 없는 경우를 나타날 때 nil을 사용합니다.
그렇다고 모든 변수에 nil을 넣을 수 있는 것은 아닙니다.

var name: String = "마승현"
name = nul // 컴파일 에러!

Nil cannot be assigned to type 'String'

값이 있을 수도 있고 없을 수도 있는 변수를 정의할 때에는 타입 ?(어노테이션)를 붙여야 합니다.

이렇게 정의한 변수를 바로 옵셔널(Optional)이라 하고, 옵셔널 초기값을 지정하지 않으면 기본값은 nil 입니다.

var email: String?
print(email) // nil

email = "tpdlqj0514@gmail.com"
print(email) // Optional("tpdlqj0514@gmail.com")

옵셔널로 정의한 변수는 옵셔널이 아닌 변수와 다릅니다.
예를 들어, 아래와 같은 코드는 사용할 수 없습니다.

let optionalEamil: String? = "tpdlqj0514@gmail.com"
let requiredEmail: String = optionalEmail // 컴파일 에러

Value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?

requeiredEmail 변수는 옵셔널이 아닌 String이기 때문에 항상 값을 가지고 있어야 합니다.
반면에, optionalEmail은 옵셔널로 선언된 변수이기 때문에 실제 코드가 실행되기 전까지는 값이 있는지 없는지 알 수 없습니다.
따라서 Swift 컴파일러는 안전은 위해 requiredEmail에는 옵셔널로 선언된 변수를 대입할 수 없게 만들었습니다.

옵셔널은 개념적으로 이렇게 표현할 수 있습니다.
어떤 값 또는 nil을 가지고 있는 변수입니다.

옵셔널 바인딩 (Optional Binding)

옵셔널의 값을 가져오고 싶은 경우 옵셔널 바인딩(Optional Binding) 사용합니다.

옵셔널 바인딩은 옵셔널의 값이 존재하는지 검사한 뒤, 존재한다면 그 값을 다른 변수에 대입시켜줍니다.
if let 또는 if var를 사용합니다.
옵셔널의 값을 벗겨서 값이 있다면 if문 안으로 들어가고, 값이 nil이라면 그냥 통과합니다.

예를 들어,
아래의 코드에서 optionalEmail에 값이 존재한다면 email 이라는 변수안에 실제 값이 저장되고, if문 내에서 그 값을 사용할 수 있습니다.
만약 optionalEmailnil이라면 if문이 실행되지 않고 넘어갑니다.

if let email = optionalEmail { // optionalEmail의 값이 존재한다면 해당 값을 출력.
  print(email)
}
// optionalEmail의 값이 존재하지 않는다면 if문을 그냥 지나칩니다.

하나의 if문에서 콤마(,)로 구분하여 여러 옵셔널을 바인딩할 수 있습니다. 이곳에 사용된 모든 옵셔널의 값이 존재해야 if문 안으로 진입합니다.

var optionalName: String? = "마승현"
var optionalEmail: String? = "tpdlqj0514@gmail.com"

if let name = optionalName, email = optionalEmail {
  // name과 email 값이 존재
}

🍯 Tip >> 코드가 너무 길 경우, 아래와 같이 여러 줄에 걸쳐 사용하는 것이 바람직합니다.

if let name = optionalName,
  let email = optionalEmail {
    //name과 email 값이 존재  
}

두 번째 let 부터는 생략이 가능합니다.

위 코드는 아래와 동일합니다.

if let name = optionalName {
  if let email = optionalEmail {
    // name과 email 값이 존재
  }
}

🍯 Tip >> 한 번의 if문에서 여러 옵셔널을 바인딩 할 수 있게 된 것은 Swift 1.2 버전부터입니다. 이전 버전까지는 바로 위와 가이 여러 번으로 감싸진 옵셔널 바인딩을 사용했습니다.

옵셔널 바인딩 할 때, ,를 사용ㅎ서 조건도 함께 지정할 수 있습니다.
, 이후의 조건절은 옵셔널 바인딩이 일어난 후에 실행됩니다.
즉, 옵셔널이 벗겨진 값을 가지고 조건을 검사하게 됩니다.

var optionalAge: Int? = 26

if let age = optionalAge, age >= 20 {
  // age의 값이 존재하고, 20 이상입니다.
}

옵셔널 체이닝 (Optional Chaining)

Swift 코드를 간결하게 만들어주는 많은 요소들이 있는데, 옵셔널 체이닝(Optional Chaining)을 알게 되면 다른 프로그래밍 언어가 조금 불편하게 느껴지는 경우가 생깁니다.

옵셔널 체이닝을 이해하는 데에는 설명보다 코드를 보는 편이 훨씬 좋습니다.
배열에서 '빈 배열'인지를 검사하려면 nil이 아니면서 빈 배열인지 확인합니다.

let array: [String]? = []
var isEmptyArray = false

// 배열이 빈 값을 가지고, array가 비어있다면
if let array = array, array.isEmpty {
  isEmptyArray = true
}
else {
  isEmptyArray = false
}

옵셔널 체이닝을 사용하면 간결하게 작성 가능합니다.

let isEmptyArray = array?.isEmpty == true

이 처럼, 옵셔널 체이닝은 옵셔널의 속성에 접근할 때, 옵셔널 바인딩 과정을 ? 키워드로 줄여주는 역할입니다.

예시로

  • array가 nil인 경우
    array?.isEmpty
    ...

    nil을 반환합니다.

  • array가 빈 배열인 경우
    array?.isEmpty
    ...

    true를 반환합니다.

  • array에 요소가 있는 경우
    array?.isEmpty
    ...

    false를 반환합니다.

array?.isEmpty의 결과로 나올 수 있는 값은 nil, true, false가 됩니다.
isEmpty의 반환값은 Bool인데, 옵셔널 체이닝으로 인해 Bool?을 반환하도록 바뀐 것입니다.
따라서 값이 실제로 true인지를 확인하려면, == true를 해주어야 합니다.

옵셔널 벗기기

옵셔널을 사용할 때마다 옵셔널 바인딩을 하는 것이 가장 바람직합니다.
하지만, 개발을 하다보면 분명히 값이 존재함에도 불구하고 옵셔널로 사용해야 하는 경우가 종종 있습니다.
이럴 때에는 옵셔널에 값이 있다고 가정하고 값에 바로 접근할 수 있도록 도와주는 키워드인 !를 붙여서 사용하면 됩니다.

print(optionalEmail) // Optional("tpdlqj0514@gmail.com")
print(optionalEmail!) // tpdlqj0514@gmail.com

! 사용 시 주의할 점은, nil인 경우에는 런타임 에러가 발생합니다.
Java의 NullPointerException과 비슷하다고 생각하면 될 거 같습니다.

var optionalEmail: String?
print(optionalEmail!)  // 런타임 에러

fatal error: unexpectedly found nul while unwrapping an Optional value

암묵적으로 벗겨진 옵셔널 (Implicitly Unwrapped Optional)

만약, 옵셔널을 정의할 때 ? 대신 !를 붙이면
ImplicitlyUnwrappedOptional 이라는 옵셔널로 정의됩니다.
직역하면 "암묵적으로 벗겨진 옵셔널"입니다.

var email: String! = "tpdlqj0514@gmail.com"
print(email) // tpdlqj0514@gmail.com

이렇게 정의된 옵셔널은 nil을 포함할 수 있는 옵셔널이긴 한데,
접근할 때 옵셔널 바인딩이나 옵셔널을 벗기는 과정을 거치지 않고도 바로 값에 접근할 수 있다는 점에서 일반적인 옵셔널과 조금 다릅니다.

옵셔널 벗기기와 마찬가지로, 값이 없는데 접근을 시도하면 런타임 에러가 발생합니다.

var email: String!
print(email) // 런타임 에러!

fatal error: unexpectedly found nil while unwrapping an Optional value

가급적이면 일반적인 옵셔널을 사용하고,
옵셔널 바인딩 또는 옵셔널 체이닝을 통해 값에 접근하는 것이 바람직합니다.

profile
다른 사람이 만든 것을 소비하는 활동보다, 내가 생산적인 활동을 하는 시간이 더 많도록 생활화 하자.

0개의 댓글