본문 바로가기
Swift

[Swift] 옵셔널(Optional)[2] 강제 추출(Forced Unwrapping), 옵셔널 바인딩(Optional Binding)

by ykr0919 2024. 3. 26.

 

 

 

저번 글에서 옵셔널(Optional)에 대해서 공부해 봄 

 

저번글에 보면 

 

var bandName: String? = "The 1975"
print(bandName)

// Optional("The 1975")

 

옵셔널 변수가 값을 가질 때 값만 출력되는 것임 아니라 Optional( ) 이라는 껍데기가 붙어서 출력이 되었음 

 

근데 우리가 그 껍데기 안에 값만 원하지 껍데기까지 원하지 않음!!!

 

print("좋아하는 밴드는 \(bandName)")

// 좋아하는 밴드는 Optional("The 1975")

 

좋아하는 밴드가 Optional("The 1975")라고 말할 수는 없겠지?? ㅋㅋㅋ

 

껍데기를 없애서 값에 접근할 수 있는 방법은 ~~ 

 

강제 추출 (Force Unwrapping)

 

옵셔널(Optional)로 선언된 값을 사용할 때 그 뒤에 ! 를 붙여주면 됨.

 

var bandName: String? = "The 1975"
print("좋아하는 밴드는 \(bandName!)")

// 좋아하는 밴드는 The 1975

 

그럼 값이 없어서 nil 인 상황에서도 강제로 옵셔널을 해제할 수 있나?? 

 

var bandName: String? = nil
print("좋아하는 밴드는 \(bandName!)")

 

 

그럼 컴파일 에러가 뜸!! 

 

강제 추출이 위험한 이유임.

 

강제 추출은 값이 nil 인지 아닌지 체크하지 않고, 바로 안에 있는 값을 꺼냄.
만약 nil 을 강제 언래핑하면, 프로그램 실행 중에 멈춰버림!!

따라서, 강제 추출은 쓰지 않는 것이 일반적임!! 

이 옵셔널(Optional)에 들어있는 값이 nil 이 아니다! 라고 100% 확신할 수 있을 때만 써야함!!! 

 

 실무에서는 안 쓰는 것이 원칙이라고 많은 글에서 말한다고 함!!!!! 

 

 

 

그러면 옵셔널(Optional)에 값이 있는지 판단을 하고 추출하면 좋겠지 ~~    

 

옵셔널 바인딩(Optional Binding)

옵셔널 바인딩(Optional Binding)을 사용하면 안전하게 옵셔널 값을 추출할 수 있음!! 

 

if문, while문, guard문에서 사용할 수 있음! 근데 while문은 거의 사용하지 않는다고 함 ~~~ 

 

if let 

if let을 사용해서 옵셔널 바인딩(Optional Binding)을 하면 

 

var bandName: String? = "The 1975"

if let printBandName = bandName {
    print(printBandName)
} else {
    print("모름")
}

// The 1975

 

결과값으로는 bandName에 할당된 값이 출력이 됨! 

 

만약 bandName에 nil 값이 들어가면 

 

var bandName: String? = nil

// 모름

 

bandName이 값이 존재하면 할당된 값이 출력이 되고, 값이 존재하지 않으면 "모름"이 출력됨 

 

여기서 주의할 점으로는 🚨

 

🙋‍♂️ 첫째!! 

printBandName이라고 if let을 통해 선언된 상수의 scopeif 구문이라고 함 

 

Scope가 뭘까? Scope는 코드의 범위

Scope는 크게 두 가지로 분류
Global Scope, Local Scope

그럼 이 두 가지를 나누는 기준은 무엇일까요?? 바로 { } (brace) 중괄호임.

 

{ } 밖이면 Global Scope.

{ } 안이면 Local Scope.

 

무슨 말이냐?? 

 

 

이런 식으로 if문 밖에서는 printBandName이라는 상수에 접근할 수 없음!!! 

 

🙋‍♂️ 둘째!! 

 

printBandName은 bandName을 Unwrapping(껍데기를 깐) 값을 대입한 것뿐임!!! 

여전히 bandName의 값은 여전히 Optional Type임 

 

var bandName: String? = "The 1975"

if let printBandName = bandName {
    print(bandName)
    print(printBandName)
}

// Optional("The 1975")
// The 1975

 

bandName을 Unwrapping 한 값이 printBandName일 뿐, bandName은 여전히 Optional Type의 값임 

 

💸 Tip!! 

 

옵셔널 바인딩(Optional Binding)을 사용하다 보면 바인딩될 Non Optional Type의 이름을 짓는 것이 굉장히 힘들고 난해함.. 

그럴 땐 그냥 Optional Type의 이름을 그대로 똑같이 써도 된다고 함!!! 

 

if let bandName = bandName {
    print(bandName)
}

// The 1975

 

만약 두 개의 변수가 서로 다른 Scope에서 같은 이름으로 선언된 경우, 실행 시점에서 가까운 곳에 선언된 변수로 선택된다고 함. 

 

var bandName: String? = "The 1975"
var country: String? = nil

if let bandName = bandName, let country = country {
}

 

이런 식으로 한 번에 여러 개의 옵셔널 타입을 바인딩할 수도 있음!!

 

이때는 bandName, country 모두 nil 이 아니여야 if 구문이 true가 됨!! 

 

var bandName: String? = "The 1975"
var age: Int? = nil

if let bandName = bandName, let age = age, age > 20 {
}

 

이런 식으로 condition을 넣어 줄 수 있음. 마찬가지로 bandName, age가 nil 이 이니여야 하고, age의 값이 20보다 커야 true가 됨. 

 

guard let 

 

guard let도 if let과 비슷하지만 다른 점이 있음. 

 

guard 문은 특성상 함수(메서드)에서만 쓰이며 guard 문의 조건을 만족하지 못하면 else 문으로 빠져서 함수의 실행을 종료시킬 때 사용함 (return을 때림)

 

 guard문은 else와 한 쌍임. 둘은 항상 같이 사용해야 함!! 

조건을 지키지 못하면 else에서 return 해야 하기 때문에 guard 뒤에 else가 붙지 않으면 오류가 남!! 

 

func printBandName(band: String?) {
    guard let BandName = band else {
        print("모름")
        return
    }
    print(BandName)
}
printBandName(band: band)

// The 1975

 

guard 문에서 표현식이 nil 이 아닐 경우 else 문으로 빠지지 않고 원래 guard 문을 실행시키던 scope으로 돌아옴. 

BandName이라는 옵셔널(Optional)이 해제된 값을 guard 문 밖에서 사용할 수 있음 

 

if문과 다른 게 옵셔널 바인딩(Optional Binding) 된 상수 BandName는 guard 문 밖에서 사용해야 함!! 

 

💸 Tip!! 

 

if 문 같은 경우는 언제든 옵셔널 타입(Optional Type)논 옵셔널 타입(Non Optional Type)의 이름을 같게 선언해 줘도 됐음!! 

 

guard 문도 가능!!! 

var band: String? = "The 1975"

func printBandName(band:String?) {
    guard let band = band else {
        print("모름")
        return
    }
    print(band)
}

printBandName(band: band)

//The 1975

 

함수 내에서 선언한 상수 or 변수를 바인딩하는 경우도 가능! 

 

func printBandName() {
    let band: String? = "The 1975"
    guard let band = band else {
        print("모름")
        return
    }
    print(band)
}

printBandName()

//The 1975

 

또한 if let과 마찬가지로 

 

func printBandName(band:String?, age:Int?) {
    guard let band = band, let age = age, age > 20 else {
        return
    }
}

 

옵셔널 바인딩(Optional Binding)을 하나의 gurad문에서 연속으로 할 수도 있고, condition을 넣을 수 있음

 

 

 

if let은 단순히 옵셔널 처리 값에 대한 피드백만 주고 싶을 때!!

예외사항만을 처리하고 싶다면 guard let을 사용하는 것이 훨씬 간편함!! 

 

 

 

강제 추출(Forced Unwrapping)과  옵셔널 바인딩(Optional Binding)의 차이!! 

 

강제 추출(Forced Unwrapping)은 값이 nil 이라도 강제로 추출하기 때문에 위험할 수 있음!! 옵셔널 바인딩(Optional Binding)을 쓰면 값이 있는지 없는지를 확인하기 때문에 안전하게 값을 추출할 수 있음!! 

 

 

 

 

 

 

참고 및 인용 

 

https://beepeach.tistory.com/8

https://babbab2.tistory.com/17

https://velog.io/@eddy_song/optional-unwrapping