🔥 모델 클래스 정의

559자
7분

옵셔널 체이닝을 통해 복잡한 모델의 깊숙 곳에 정의된 속성에 접근하는 방법에 대해 알아보겠습니다. 우선 Person, Residence, Room, 그리고 Address라는 4개의 모델 클래스를 정의해 보겠습니다. 이 클래스들은 서로 연관되어 있으며, 다음 예제에서 다단계 옵셔널 체이닝을 시연하는 데 사용될 거예요.

Person 클래스

Person 클래스는 이전과 동일하게 정의됩니다:

class Person {
    var residence: Residence?
}
swift

Residence 클래스

이번에는 Residence 클래스가 좀 더 복잡해집니다. rooms라는 [Room] 타입의 변수 속성을 정의하고, 빈 배열로 초기화해요:

class Residence {
    var rooms: [Room] = []
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}
swift

이 버전의 ResidenceRoom 인스턴스의 배열을 저장하기 때문에, numberOfRooms 속성은 저장 속성이 아닌 계산 속성으로 구현됩니다. 계산된 numberOfRooms 속성은 단순히 rooms 배열의 count 속성 값을 반환하죠.

rooms 배열에 접근하는 지름길로, 이 Residence 버전은 rooms 배열에서 요청된 인덱스에 있는 방에 접근할 수 있는 읽기-쓰기 서브스크립트를 제공합니다.

또한 이 Residence 버전은 printNumberOfRooms라는 메서드를 제공하는데, 이 메서드는 거주지의 방 수를 출력해 줍니다.

마지막으로 ResidenceAddress? 타입의 address라는 옵셔널 속성을 정의합니다. 이 속성에 사용되는 Address 클래스 타입은 아래에 정의되어 있어요.

Room 클래스

rooms 배열에 사용되는 Room 클래스는 name이라는 하나의 속성과, 그 속성을 적절한 방 이름으로 설정하는 이니셜라이저를 가진 간단한 클래스입니다:

class Room {
    let name: String
    init(name: String) { self.name = name }
}
swift

Address 클래스

이 모델의 마지막 클래스는 Address라고 불립니다. 이 클래스에는 String? 타입의 세 개의 옵셔널 속성이 있어요.

첫 번째 두 속성인 buildingNamebuildingNumber는 주소의 일부로 특정 건물을 식별하는 대체 방법이에요. 세 번째 속성인 street은 해당 주소의 거리 이름을 나타내죠:

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\(buildingNumber) \(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}
swift

Address 클래스는 또한 buildingIdentifier()라는 메서드를 제공하는데, 이 메서드의 반환 타입은 String?입니다.

이 메서드는 주소의 속성을 확인하고, buildingName이 값을 가지면 해당 값을 반환하고, buildingNumberstreet 둘 다 값을 가지면 이들을 연결한 문자열을 반환하며, 그 외의 경우에는 nil을 반환합니다.

이렇게 연관된 모델 클래스들을 정의했으니, 이제 옵셔널 체이닝을 사용해서 이들의 깊숙한 속성에 접근해 볼 수 있겠죠?

예를 들어, 다음과 같은 관계를 생각해 볼 수 있습니다:

lecture image

이제 Person 인스턴스에서 시작해서, 옵셔널 체이닝을 통해 Residence, Room, 그리고 Address의 속성과 메서드에 접근할 수 있게 되었어요.

옵셔널 체이닝을 사용하면 중간에 어떤 속성이 nil이더라도 안전하게 접근을 시도할 수 있습니다. nil을 만나면 그냥 nil을 반환하겠죠.

예를 들면 다음과 같은 식으로 사용할 수 있습니다:

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
swift

여기서는 john.residencenil이기 때문에 numberOfRooms에 접근할 수 없어요. 따라서 else 블록이 실행되는 거죠.

반면에 Residence 인스턴스가 존재한다면, 옵셔널 체이닝을 통해 그 속성에 접근할 수 있습니다:

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
swift

이렇게 하면 john.residencenil이 아닌 경우 address 속성에 someAddress가 할당됩니다. nil인 경우에는 할당이 무시되죠.

이처럼 옵셔널 체이닝을 사용하면 복잡하게 연관된 모델 타입 간에 안전하게 조건부로 접근할 수 있답니다. 중간에 nil이 있다면 문제 없이 처리할 수 있으니 정말 유용한 기능이죠? 이어지는 장에서 옵셔널 체이닝을 통한 속성 접근을 좀 더 자세히 알아 보겠습니다.