본문 바로가기
공부/스위프트

[스위프트] 옵셔널(Optional)

by 초코팅촉 2023. 7. 8.
728x90

옵셔널은 이전에 Objective-c에는 없던 새로운 개념으로 스위프트의 방향성을 보여주는 기능이기도 하다.
옵셔널은 언어 레벨에서 안정성을 높이기 위해 도입한 개념으로 "값이 없을 수도 있는 데이터"를 표현하기 위한 기능이다.
여기서 우리가 중요하게 생각해야 할점은 바로

옵셔널은 에러를 막기 위해 만들어진 것이다.

아마 이렇게 생각할 수도 있다.
'그냥 값이 없을수도 있는거 아닌가?'
물론 맞는 말이긴 한데 옵셔널은 값이 없음으로 인해 에러가 발생할 수 있는걸 막기 위해 존재한다는걸 생각하면
위에서 말한 "옵셔널은 에러를 막기위해 만들어진 것"이라는 말이 이해가 될 것이다.

값이 있다면 Optional(값)으로 반환되며
만약 해당 데이터에 값이 없다면 nil이 출력된다.

옵셔널 타입은 별도로 존재하지않고 단순히 현재 존재하던 타입에 "?"를 붙이면 된다.

선언 및 초기화

var optInt: Int? = 2    // 옵셔널 Int 변수 선언
var optInt: Int? = nil    // 이렇게 선언해도 된다. 이러면 단순히 초기값이 아예 존재하지 않는 변수가 되는것.

var optStr: String?    // 옵셔널 String 변수 선언

var opt Arr: [Int]?
// 등등 위처럼 그냥 선언시에 끝에 ?를 붙이면 된다.


옵셔널 해제 방법

옵셔널 데이터는 앞서 말했듯 Optional이 붙어서 반환되기 때문에 Optional을 제거해야 일반적인 값과 같이 사용할 수 있다. 옵셔널을 해제하는데에는 여러가지 방법이 있는데 다음과 같다.

옵셔널 강제 해제

옵셔널 강제 해제는 간단하다. 단순히 옵셔널 타입의 값 뒤에 "!"를 붙이면 된다.
적용은 다음과 같다.

var optInt: Int? = 2

print("옵셔널 값 : \(optInt)")    // Optional(2)
print("옵셔널 강제 해제된 값 : \(optInt!)")    // 2

하지만 강제 해제는 위험하다.
앞서 얘기했듯 값이 없으면 에러를 발생하는 대신 nil을 반환해 후속처리를 해주기로 한건데
이를 무시해버리는 것이기 때문에 그냥 에러가 발생하고 프로그램이 전체적으로 종료되며 끝나기 때문이다.

 

옵셔널 바인딩

옵셔널 바인딩이라 하니까 거창해 보이지만 조건문을 통해 값이 있다면(nil이 아니라면) 값을 할당하고 진행하며,
값이 없다면(nil이라면) 문제가 발생할 수 있으니 그에 따른 조치를 하는것을 말한다.
조건문은 if와 guard를 사용한다.
기본적인 틀은 다음과 같다.

var optInt: String = "dkdk"

//if를 사용한 옵셔널 바인딩
if let normalInt = Int(optInt) {    // let대신 var를 사용해도 된다.
    print("할당가능.")
} else {
    print("할당불가.")
}

//guard를 사용한 옵셔널 바인딩
//guard를 쓰기위해서 함수를 만들었는데 일단은 그냥 그렇구나 하고 넘어가도록 하자.
func optCharge() {
    guard let normalInt = Int(optInt) else {    //마찬가지로 let대신 var를 써도 된다.
        print("할당 불가")
        return
    }
    print("할당 가능")
}

optCharge()

--추가된 문법이 있어서 아래 링크 추가합니다.--

옵셔널 바인딩 간단한 방법

 

컴파일러에 의한 옵셔널 자동 해제

옵셔널 값은 사용하려면 무조건 !로 강제 해제해야 된다고 했으나 단 한가지 예외가 있다.
바로 값을 비교할 때이다.

//아래 예시 전부 true.
let optInt = Int("123")

optInt == 123
optInt == Optional(123)
optInt! == 123
optInt! == Optional(123)

 

옵셔널의 묵시적 해제

옵셔널의 묵시적 해제는 강제 해제와 비슷하면서 다르기 때문에 헷갈릴 수 있다.
선언시에 "?"대신 "!"를 붙이는 방법이다.
단, 앞에서부터 계속 말하듯이 에러를 방지하려고 옵셔널을 만들었기 때문에 묵시적 해제가 가능한 경우는 한가지이다.

형식적으로는 옵셔널로 정의하나,
실제론 nil이 대입될 가능성이 절대적으로 없는 변수일 때.

 

//원래의 옵셔널 선언
var str: String? = "Swift Optional"
var tempStr: String = str
print(tempStr)    // Optional("Swift Optional")

//묵시적 해제를 위한 선언
var str: String! = "Swift Optional"
var tempStr: String = str
print(tempStr)    // Swift Optional

 

옵셔널 체인(Optional Chain)

옵셔널을 이용해 클래스 내부의 변수들을 만든다면(클래스는 향후 설명할것) 값에 접근하거나 수정 및 사용시에 매우 번거롭게
옵셔널 바인딩을 중첩으로 사용해야 할 것이다. 그러면 소스코드는 매우 길어지고 작성자체도 귀찮아질 것이고.. 아마 여러모로 불편해질 것이다.
이를 편하게 하기 위해 존재하는 것이 옵셔널 체인이다.
우선 바로 예시를 보자.(뭔가 복잡해 보이지만 일단 옵셔널에만 집중해보자.)

struct Company {
var ceo: Human?
var companyName: String?
}

struct Human {
var name: String?
var man: Bool = true
}

var startUp: Company? = Company(ceo: Human(name: "나대표", man: false),
						companyName: "루비페이퍼")

// 옵셔널 해제에 있어 불편해진다.
if let company = startUp {
    if let ceo = company.ceo {
            if let name = ceo.name {
                print("대표 이름 : \(name)")    
            }
        }
}

// 물론 아래처럼 바로 강제로 해제할 수도 있다. 그러나 값이 없다면 에러가 발생한다.
if let name = startup!.ceo!.name {
    print(name)
}

// 옵셔널 체인을 사용해보자.
// 마지막 name은 옵셔널 체인에 해당하지 않는다. 옵셔널인지 직접 검사해야하기 때문이다.
if let name = startUp?.ceo?.name {
    print("대표 이름 : \(name)")
}

맨 아래가 실제로 옵셔널 체인을 쓸때의 방법이다. 주석에 써있듯이 마지막에 name은 옵셔널인지 확인해야하기 때문에
"?"를 붙이지 않고 옵셔널 바인딩을 하면된다.

 

 

 

스위프트 문법 - 옵셔널(Optional), swift