[Swift 기초] - 옵셔널(Optional)을 파헤쳐보자 3편​

justdotheg·2023년 12월 31일
1
post-thumbnail

[목차]
1. 옵셔널 체이닝(Optional Chaining)
2. Optional Chaining을 사용한 경우 vs 사용하지 않은 경우 비교
3. struct 객체에 옵셔널 체이닝을 사용해보자

1. 옵셔널 체이닝(Optional Chaining)

'옵셔널 체이닝(Optional Chaining)'이란 nil일수도 있는 옵셔널 타입의 메서드(함수)나 프로퍼티(변수)의 값에 연속으로 접근할 때 ? 키워드를 사용해 코드의 복잡성을 줄여주는 역할을 한다. ? 키워드를 반복적이고 연속적으로 사용하여 값에 접근하는 모습이 마치 Chain(체인) 같다고 하여 '옵셔널 체이닝'이라고 부른다.

옵셔널 체이닝은 Swift의 코드를 매우 간결하게 해주는 장치 중 하나이다.

Swift 공식 문서(https://bbiguduk.gitbook.io/swift/language-guide-1/optional-chaining)에는 옵셔널 체이닝은 "강제 언래핑의 대안으로 옵셔널 체이닝 (Optional Chaining as an Alternative to Forced Unwrapping)"으로도 설명하고 있다.

2. Optional Chaining을 사용한 경우 vs 사용하지 않은 경우 비교

2.1. (예제1) 옵셔널 체이닝을 사용하지 않은 경우

예제1을 살펴보기 전에 알고가야 할 사전지식을 짚고 넘어가자.

  • Swift에서 제공하는 배열에는 isEmpty라는 펑션를 제공한다.
  • 또한, if let(명시적인 Optional 비강제 해제 방법)은 상수 뿐만 아니라 변수인 if var도 가능하다. 이건 guard let도 마찬가지다. 👇

3가지 경우에 대해서 예제를 살펴보겠다.

1️⃣ 첫번째는 optionalArray라는 String? 배열(스트링 옵셔널 배열)가 빈 배열 []로 초기화된 경우이다. 빈 배열이긴 하지만 요소가 없는 배열로 초기화되었을 뿐, nil은 아니다. 따라서 if var 구문에서 true값이 되고, if array.isEmpty 구문에서 true값이 되어, 최종적으로 isEmpty는 true가 된다.

2️⃣ 두번째는 optionalArray가 초기화되지 않아 nil 값인 경우이다. 따라서 if var 구문에서 array에 대입하지 못하고 false를 반환하여, else 구문에서 isEmpty를 nil로 초기화한다.

3️⃣ 세번째는 optionalArray에 "just do the g" 문자열 요소가 한개 있는 배열로 초기화된 경우이다. 따라서 if var 구문에서 true값이 되고, if array.isEmpty 구문에서 false값이 되어, 최종적으로 isEmpty는 false가 된다.

2.2. (예제2) 옵셔널 체이닝을 사용한 경우

옵셔널 체이닝을 사용하면 예제1을 더 간결하게 코드 간소화시킬 수 있다!!

var isEmpty: Bool? = optionalArray?.isEmpty == true

바로 이 한줄로 예제1의 10줄 정도의 코드를 나타낼 수 있다.
정말 대단한 기능이다..

옵셔널 체이닝은 옵셔널의 속성에 접근할 때, '옵셔널 바인딩 과정'을 ? 키워드로 줄여주는 역할을 한다. 위의 1️⃣, 2️⃣, 3️⃣ 각각의 경우에 대해서 옵셔널 체이닝을 사용했을 때의 결과를 확인해보자.

1️⃣ 첫번째는 optionalArray라는 String? 배열(스트링 옵셔널 배열)가 빈 배열 []로 초기화된 경우이다

배열이 빈값으로 초기화되어있으므로 컴파일러는 optionalArray?.isEmpty 까지 실행하고 true 값을 던진다.

optionalArray?.isEmpty //true

따라서 (true == true) ➡️ true이므로 isEmpty는 true를 반환한다.

2️⃣ 두번째는 optionalArray가 초기화되지 않아 nil 값인 경우이다

배열이 초기화되지 않아 nil값이므로 컴파일러는 1️⃣과 달리 optionalArray?.isEmpty 까지 실행하지 않고 아래와 같이 optionalArray? 까지 실행하고 nil 값을 던진다.

optionalArray? //nil 

🙋 엇! 그런데.. nil값이면 nil == true ➡️ false니까 isEmpty 옵셔널 bool 변수는 nil값을 가져가야 되는거 아닌가??!!

아니다.

아래 코드를 보면 nil == true를 비교했을 때 "Comparing non-optional value of type 'Bool' to 'nil' always returns false"라고 옵셔널 타입이 아닌 Bool 값(여기서는 true)와 nil을 비교하면 항상 false를 반환한다고 경고 메시지가 발생한다.

따라서 (nil == true) ➡️ false이므로 isEmpty는 nil이 아닌 false를 반환한다.

3️⃣ 세번째는 optionalArray에 "just do the g"라는 문자열 요소가 한 개 존재하는 배열로 초기화한 경우이다.

배열이 비지 않은 문자열 배열로 초기화되어있으므로 컴파일러는 optionalArray?.isEmpty 까지 실행하고 false 값을 던진다.

optionalArray?.isEmpty //false

따라서 (false == true) ➡️ false이므로 isEmpty는 false를 반환한다.

3. struct 객체에 옵셔널 체이닝을 사용해보자

위의 2.1~2.2 목차에서는 옵셔널타입의 변수에 Optional Chaining 사용 여부에 따른 코드 간소화를 보았다.
이번 목차에서는 struct나 class로 생성하는 객체의 프로퍼티에 접근할 때 옵셔널 체이닝을 적용해보겠다.

3.1. ?을 한번 붙인 경우

코드를 부연설명하자면 Company라는 struct 객체를 선언 후 그 내부 프로퍼티(속성)으로 Employee 스트럭트를 갖게 선언하였다.
이후 james라는 객체를 앞에서 선언한 Compnay 스트럭트를 이용해 생성하였는데, james 객체의 타입은 정확히 말하면 Company?이다.
즉, struct optional type이다.

이제 struct optional type인 james의 내부 프로퍼티에 접근해보자! 👇


에러가 발생한다.
"Company?(Comapany Optional Type)은 email이라는 멤버가 없습니다."라는 뜻이다.
당연하다. email이라는 멤버는 Compnay?의 프로퍼티 중 하나인 Employee 스트럭트 안에 있기 때문에, 스트럭트에 한번 더 접근해야 한다. 👇


이렇게 선언해도 에러가 발생한다.
"Company? 타입은 employee의 멤버를 참조하기 위해 언래핑되어야 한다."라는 에러가 발생한다.

그리고 두가지를 Error Fix 방법으로 제안하고 있다.

첫번째는
"non-nil(nil이 아닌 값)인 employee 멤버에 접근하기 위해 옵셔널을 ? 키워드를 사용해 체이닝을해라." 이다.

두번째는
"! 키워드를 사용해 Force-unwrap(강제 언래핑)을 하여 optional 변수가 nil값을 포함하고 있다면 실행을 중지해라." 이다.

정리하면 옵셔널 체이닝(?)을 사용하거나 강제 언래핑(!)을 해서 Company의 프로퍼티인 employee의 멤버에 접근하라는 뜻이다.


Compnay가 옵셔널 타입의 struct이므로 Compnay의 객체인 james 뒤에 체이닝 키워드 ?를 붙여서 출력해보자! 👇

Optional이 래핑된 상태에서 출력이 되었음을 볼 수 있다.

⭐️ 이 코드에서 볼 수 있는 중요한 점은 Optional Chaining의 역할은 옵셔널 언래핑이 아니다. 단순히 옵셔널 타입의 값에 접근하는 연속된 작업의 코드를 간략화시켜주는 장치이다!

Optional에 래핑되어있는게 불편하지 않은가?


그럼 이번엔 강제 언래핑을 해서 출력해보자! 👇

옵셔널 체이닝때 Optional Type인 Company의 객체 바로 뒤에 ? 키워드를 붙여줬던 것처럼, 이번에 강제 언래핑(Forced Unwrapping)할때도 동일한 위치에 ! 키워드를 붙이면 된다.

옵셔널이 해제되었음을 볼 수 있다. 이메일 주소만 출력된다.

3.2. ?를 연속해서 체이닝해보자.

옵셔널 체이닝이 nil값을 가질수도 있는 변수, 프로퍼티, 메서드에 반복적으로/연속적으로 접근해야 해서 ?를 반복해서 사용하는 모습이 체인 형태와 같다고 했다. 그래서 ? 키워드를 사용해 코드를 단순화 하는 이 프로세스를 Optional Chaining(옵셔널 체이닝)이라고 부른다고 했다.

james?.employee.email 

🙋 그런데 지금까지 본 예제들은 "?를 한번밖에 사용 안 하는데... 체인모양 같다고..?" 라는 생각이 들 수 있다.

아래 코드를 봐보자! 👇

struct Company {
    var employee : Employee?  
 	...
}

만약 2.3의 예제에서 Company의 멤버로 employee 프로퍼티를 선언할 때, Company와 동일하게 Employee? (Optional Struct Type)으로 선언해보면 어떻게 될까?

james?.employee.email 

🚨 기존대로 james 뒤에만 ?를 붙여 접근하면 옵셔널 관련 에러가 발생한다.


따라서 아래와 james에도 employee에도 연속해서 ?를 객체 뒤에 붙임으로써 체이닝의 형태를 띄게 코드를 작성해줘야 한다. (이게 옵셔널 체이닝이다!)

0개의 댓글