Medium - Optional Chaining in Swift

원문 - Rudrani Wankhade

Trend 파악을 Medium 기고문 요약 포스팅 - 스위프트의 옵셔널 체이닝

500x400

스위프트를 사용해서 작업을 하는 사람들이면 모두 옵셔널에 대한 개념을 알 것입니다. 옵셔널은 두 가지 가능성을 내포합니다, 값이 있어서 옵셔널을 언래핑하여 값에 접근할 수 있거나 아니면 값이 없는 것이죠. 느낌표 심벌을 이용하여 옵셔널 값을 강제로 언래핑 할 수도 있으나 옵셔널 값이 nil값인 경우 런타임 에러가 발생하게 됩니다. 그렇다면 이런 런타임 에러 대신에 부드럽게 오류를 처리하는 방법이 있을까요? 당연히 있습니다! 옵셔널 체이닝이라고 알려진 것을 사용하면 됩니다.

이것은 옵셔널 체이닝에 대한 서문이며 저는 여러분이 옵셔널 체이닝의 여러 사용법에 익숙해지도록 할 것입니다. 얼른 시작해 봅시다!

What is optional chaining?

우리가 옵셔널의 사용법에 대해서 알기 전에 옵셔널 체이닝이 뭔지 설명해 드리겠습니다. 옵셔널 체이닝은 프로퍼티나 메소드에 쓰인 옵셔널의 현재 값이 nil인지 아닌지 물어보는 과정입니다. 옵셔널은 값이 있을 수도 있고 없을 수도 있죠. 메소드나 프로퍼티가 옵셔널이 값이 있을 때 그 요청은 성공하지만 값이 없다면 nil을 리턴 받습니다. 체이닝이라고 불리는 이유는 여러개의 질의를 묶어서 사용할 수 있기 때문이고 어떤 체인이라도 nil값이 된다면 전체 체인이 nil값이 됩니다.

How optional chaining is different the forced unwrapping

우리는 느낌표 심벌을 이용하여 옵셔널의 값을 강제로 언래핑할 수 있는 것을 알고 있습니다. 물음표 심벌은 옵셔널이 nil값이 아닐 때만 값을 가져오죠. 만약 프로퍼티나 메소드, 서브스크립트의 쿼리 결과 값이 옵셔널 값이 아니더라도 옵셔널 체이닝의 결과는 항상 옵셔널 값입니다 왜냐하면 옵셔널 체이닝의 결과 값은 nil이 될 수 있기 때문입니다. 예를 들어 String을 리턴하는 평범한 메소드는 우리가 옵셔널 체이닝을 통해 호출할 경우 String?을 리턴할 겁니다. 옵셔널 체이닝과 강제 언래핑의 차이를 이해하기 위해 코드를 좀 보면서 설명해 드리겠습니다.

먼저 Student클래스와 Course 클래스를 정의합시다.

class Student {
  var degree: Course?
}

class Course {
  var courseTitle = "B.Tech"
}

이제 새로운 Student 인스턴스를 생성하면 degree 프로퍼티는 nil로 초기화 될 것입니다 왜냐하면 옵셔널로 선언되었기 때문이죠. 그래서 아래의 코드에서 nidhi의 degree 프로퍼티 값은 nil입니다.

let nidhi = Student
let courseName = nidhi.degree!.courseTitle
                //런타임 에러를 발생시킵니다.

이 경우에 느낌표를 사용하여 degree의 값을 강제로 언래핑 할 경우 값을 꺼낼 degree의 값이 없기 때문에 런타임 에러가 발생합니다. 그러면 옵셔널 체이닝은 어떻게 다르게 동작하는 지 다음의 예를 살펴보겠습니다.

if let courseName = nidhi.degree?.courseTitle {
  print("Nidhi is pursuing \(courseName).")
}else{
  print("Unable to retrieve course name")
}
//Prints "Unable to retrieve course name"

옵셔널 체이닝을 쓰면 옵셔널의 값이 있는 경우 남아있는 코드가 실행되고 nil값일 경우 체인이 깨지게 되고 else 파트의 코드가 실행됩니다. CourseTitle은 옵셔널 string이 아님에도 옵셔널 체이닝의 쿼리를 통해 String 대신에 String?을 리턴한다는 것을 주목하세요. 자 그러면 Course 인스턴스를 nidhi.degree에 값을 할당합시다.

nidhi.degree = courseTitle()

이제 CourseTitle을 옵셔널 체이닝을 통해 접근해보면 “B.Tech”라는 값을 가진 string옵셔널 값이 리턴 될 것입니다.

Swift Model Classes for Optional chaining

옵셔널 체이닝의 중요한 사용법 중에 하나는 복잡한 클래스에서 옵셔널으 선언할 수 있게 해주기 때문입니다. 이러한 유연성은 서브 프로퍼티 내부로 우리가 파고들어갈 수 있게 해줍니다 그리고 우리가 하위 프로퍼티들의 값에 접근할 수 있는지 체크하는 것이 가능합니다. 예제를 통해 더욱 확실히 이해해 봅시다. 두개의 클래스를 정의할건데 하나는 College라는 복잡한 클래스고 다른 Student 클래스는 College를 옵셔널 값으로 가지는 클래스 입니다.

class Student {
  var clg: College?
}

class College {
  var classes =  [ClassRoom]()
  var noOfClasses: Int {
    return classes.count
  }
  subscript (i: Int) -> ClassRoom {
    get  {
      return classes[i]
    }
    set  {
      return classes[i] = newValue
    }
  }
  func printNoOfClasses() {
    print("Number of Classes: \(noOfClasses)")
  }
  var address: Address?
}

class ClassRoom {
  var className: String
  init(className: String) { self.className = className }
}

class Address {
  var city: String?
  var area: String?
  func contactAdd() -> String? {
    if city != nil  {
        return city
    } else if area != nil {
        return area
    } else {
        return nil
    }
  }
}
let john = Student()
if let classCount =  john.clg?.noOfClasses {
  print("College has a \(classCount) Classes")
}
else {
  print("Unable to Retrieve No. of Classes")
}

College 클래스는 ClassRoom 인스턴스의 배열을 저장하고 noOfClasses 프로퍼티는 연산 프로퍼티로 구현되었습니다. 연산 프로퍼티인 noOfClasses는 간단히 classes 배열의 수를 리턴하는 것입니다. classes 배열에 접근하기 위한 숏컷으로 College 클래스는 classes 배열의 인덱스를 이용하여 r/w 할 수 있는 서브스크립트를 제공합니다. College 클래스는 printNoOfClasses라는 함수 또한 제공하는데 단순히 클래스 룸의 숫자를 출력하는 것입니다. 마지막으로 College 클래스는 Address 타입의 클래스를 옵셔널 프로퍼티로 가집니다. ClassRoom 클래스는 classes 배열에 사용되며 간단히 className이라는 프로퍼티를 가지고 적절한 학급 이름으로 초기화 합니다.

우리가 저 코드를 실행시키면 john.clg 가 nil이기 때문에 “Unable to Retrieve No. of Classes” 라는 결과물을 볼 것입니다.

Accessing Properties Through Optional Chaining

옵셔널 체이닝은 옵셔널 값을 가지는 프로퍼티에 접근할 때 사용할 수 있습니다.우리가 위의 예제에서 보았듯이 noOfClasses에 접근을 시도했지만 john.clg가 nil이기 때문에 실패했습니다. 우리가 프로퍼티의 값을 옵셔널 체이닝을 통해 값을 설정하면 어떻게 되는지 다음 예제를 통해 살펴봅시다.

let clgAddress = Address()
clgAddress.city = "Bangalore"
clgAddress.area = "shivajinagar"
john.clg?.address = clgAddress

이 에제에서 address 프로퍼티의 값을 설정하려는 시도는 실패할 것입니다. john.clg의 값이 현재 nil이기 때문이죠.

Calling Methods Through Optional chaining

옵셔널 체이닝은 옵셔널 값에 대해 메소드를 요청하고 그것이 성공적이었는지 확인하는 것에도 쓰일 수 있습니다. 이것은 그 메소드가 리턴값이 없어도 가능합니다. 옵셔널 체이닝의 결과는 항상 옵셔널 이기 때문에 만약 메소드가 void일 경우 void?을 리턴할 것 입니다. 예를 들면 우리는 printNoOfClasses 메소드가 리턴값이 없어도 call이 가능한지 아닌지 체크할 수 있습니다. 이 메소드 call은 성공적인 경우에는 옵셔널 값을 리턴할 것이기 때문에 우리는 메소드 콜의 성공 여부를 확인할 수 있습니다.

if john.clg?.printNoOfClasses() != nil {
  print("It was possible to print the number of classes.")
} else {
  print("It was not possible to print the number of classes")
}
//Prints "It was not possible to print the number of classes"

Accessing Subscripts Through Optional chaining

메소드를 콜하고 프로퍼티에 접근하는 과정에서 옵셔널 체이닝은 subscript한 옵셔널 값을 가져오거나 설정하는 것이 가능합니다 그리고 subscript콜이 성공적인지 체크합니다. 이것은 예제로 보는 것이 더 낫겠네요

if let firstClassName = john.clg?[0].className {
  print("The first class name is \(firstClassName).")
} else {
  print("Unable to retrieve the first class name.")
}
  //Prints "Unable to retrieve the first class name"

john.clg는 현재 nil이기 때문에 subscript콜은 실패 할 것입니다.

우리가 john.clg?[0] = Room(name: “Classroom-1”) 이라는 옵셔널 체이닝 구문을 통해 값을 설정하려고 해도 clg가 nil이기 때문에 실패합니다.

우리가 Collecge 인스턴스를 만들고 john.clg에 할당하고 난 후에야 우리는 옵셔널 체이닝을 이용해서 실제 아이템에 접근하는 것이 가능할 것입니다.

Accessing Subscripts of Optional Type

스위프트의 딕셔너리 타입의 key subscript 처럼 subscript의 리턴값이 옵셔널인 경우 그 값들은 수정되어 선언하는 것이 가능합니다. 다음 예제를 통해 자세히 살펴보겠습니다.

var scores = ["Suresh": [76,87,64], "Ramesh": [45,99,63]]
scores["Suresh"]?[0] -= 10
scores["Ramesh"]?[2] += 21
print("Exam progess report is :",scores)
// prints Exam progess report is :["Suresh": [66, 87,64], "Ramesh": [45, 99, 84]]

Linking Multiple Levels of chaining

여러개의 옵셔널 체이닝을 사용할 때 재미있는 점은 프로퍼티나 메소드, 서브스크립트를 드릴다운하면서 많은 level을 추가할 수 있지만 리턴되는 값은 체이닝이 되어 있지 않다는 것입니다.

Summary

  • 강제로 옵셔널 값을 꺼내면 런타임 에러가 발생하지만 옵셔널 체이닝을 사용하면 else 구문을 통해 옵셔널 값이 nil인 경우를 처리할 수 있다.
  • 옵셔널 체이닝의 결과 값은 항상 옵셔널이다.
  • 메소드나 프로퍼티의 하위 속성까지 여러번 옵셔널 체이닝을 써도 결과 값은 쉽게 얻을 수 있다.

© 2019. All rights reserved.

Powered by Hydejack v8.1.1