🔥 모델 클래스 정의
옵셔널 체이닝을 통해 복잡한 모델의 깊숙 곳에 정의된 속성에 접근하는 방법에 대해 알아보겠습니다. 우선 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
이 버전의 Residence
는 Room
인스턴스의 배열을 저장하기 때문에, numberOfRooms
속성은 저장 속성이 아닌 계산 속성으로 구현됩니다. 계산된 numberOfRooms
속성은 단순히 rooms
배열의 count
속성 값을 반환하죠.
rooms
배열에 접근하는 지름길로, 이 Residence
버전은 rooms
배열에서 요청된 인덱스에 있는 방에 접근할 수 있는 읽기-쓰기 서브스크립트를 제공합니다.
또한 이 Residence
버전은 printNumberOfRooms
라는 메서드를 제공하는데, 이 메서드는 거주지의 방 수를 출력해 줍니다.
마지막으로 Residence
는 Address?
타입의 address
라는 옵셔널 속성을 정의합니다. 이 속성에 사용되는 Address
클래스 타입은 아래에 정의되어 있어요.
Room 클래스
rooms
배열에 사용되는 Room
클래스는 name
이라는 하나의 속성과, 그 속성을 적절한 방 이름으로 설정하는 이니셜라이저를 가진 간단한 클래스입니다:
class Room { let name: String init(name: String) { self.name = name } }
swift
Address 클래스
이 모델의 마지막 클래스는 Address
라고 불립니다. 이 클래스에는 String?
타입의 세 개의 옵셔널 속성이 있어요.
첫 번째 두 속성인 buildingName
과 buildingNumber
는 주소의 일부로 특정 건물을 식별하는 대체 방법이에요. 세 번째 속성인 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
이 값을 가지면 해당 값을 반환하고, buildingNumber
와 street
둘 다 값을 가지면 이들을 연결한 문자열을 반환하며, 그 외의 경우에는 nil
을 반환합니다.
이렇게 연관된 모델 클래스들을 정의했으니, 이제 옵셔널 체이닝을 사용해서 이들의 깊숙한 속성에 접근해 볼 수 있겠죠?
예를 들어, 다음과 같은 관계를 생각해 볼 수 있습니다:
이제 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.residence
가 nil
이기 때문에 numberOfRooms
에 접근할 수 없어요. 따라서 else
블록이 실행되는 거죠.
반면에 Residence
인스턴스가 존재한다면, 옵셔널 체이닝을 통해 그 속성에 접근할 수 있습니다:
let someAddress = Address() someAddress.buildingNumber = "29" someAddress.street = "Acacia Road" john.residence?.address = someAddress
swift
이렇게 하면 john.residence
가 nil
이 아닌 경우 address
속성에 someAddress
가 할당됩니다. nil
인 경우에는 할당이 무시되죠.
이처럼 옵셔널 체이닝을 사용하면 복잡하게 연관된 모델 타입 간에 안전하게 조건부로 접근할 수 있답니다. 중간에 nil
이 있다면 문제 없이 처리할 수 있으니 정말 유용한 기능이죠? 이어지는 장에서 옵셔널 체이닝을 통한 속성 접근을 좀 더 자세히 알아 보겠습니다.