KeyPath
는 Swift의 기능 중 하나로, 타입 안전한 속성 또는 서브스크립트에 대한 참조를 나타냅니다. 이를 사용하면 속성이나 서브스크립트의 값을 타입 안전하게 저장하고 조작할 수 있습니다.
예를 들어, Person
이라는 구조체를 정의하고 name
이라는 속성을 가지고 있다고 가정해봅시다.
이때 Person
구조체의 인스턴스에서 name
속성에 접근하고자 한다면,
KeyPath
를 사용하여 타입 안전하게 이러한 속성에 접근할 수 있습니다.
struct Person {
let name: String
}
let person = Person(name: "jimmy")
let nameKeyPath = \Person.name
person[keyPath: nameKeyPath] // "jimmy"
person[keyPath: \.name] // "jimmy"
이 때 subscript 메서드를 활용하여 코드를 더 간결하게 줄일 수 있습니다.
구조체의 깊이를 한 층 더 늘려보겠습니다.
struct Name {
let value: String
let lastName: String
}
struct Person {
var name: Name
subscript(keyPath: KeyPath<Person, String>) -> String {
self[keyPath: keyPath]
}
}
let person = Person(name: Name(value: "jimmy", lastName: "jung"))
person[\.name.value] // "jimmy"
person[\.name.lastName] // "jung"
하지만 이렇게 KeyPath로 프로퍼티에 접근하는 방식은 직관적이지 않고 복잡해 보입니다.
이때 등장하는게 바로 @dynamicMemberLookup
인데요
@dynamicMemberLookup
이 속성을 사용하면 대괄호 대신 점(.)으로 접근할 수 있습니다.
또한, 원하는 Key와 Value의 타입을 지정하여 경로를 축약하는 것도 가능합니다.
KeyPath<Name, String>
을 사용하면 Name 타입 중 String 타입에 직접 접근할 수 있습니다.
@dynamicMemberLookup
struct Person {
var name: Name
subscript(keyPath: KeyPath<Person, String>) -> String {
self[keyPath: keyPath]
}
subscript(dynamicMember keyPath: KeyPath<Name, String>) -> String {
name[keyPath: keyPath]
}
}
let person = Person(name: Name(value: "jimmy", lastName: "jung"))
person[keyPath: \.name.value] // KeyPath
person.value // @dynamicMemberLookup
person.lastName // @dynamicMemberLookup

@dynamicMemberLookup
은 Swift 프로그래밍 언어의 기능 중 하나로,
컴파일 타임에 명시적으로 정의되지 않은 타입의 멤버에 접근할 수 있게 해줍니다.
이를 통해 타입에 대한 사용자 정의 서브스크립트 동작을 정의할 수 있습니다.
@dynamicMemberLookup
struct Person {
let info: [String: String]
subscript(dynamicMember name: String) -> String? {
return info[name]
}
}
let person = Person(info: ["name": "jimmy"])
let name = person.name
Person
구조체의 속성에 이름으로 접근하기 위해 서브스크립트 메서드를 사용할 수 있습니다.
컴파일 타임에는 person.name
의 타입이 명시되지 않기 때문에
컴파일 타임에서는 해당 타입을 추론할 수 없습니다.
하지만 @dynamicMemberLookup
를 사용하면 subscript(dynamicMember:)
를 구현해
런타임에서 dynamicMember 파라미터로 받은 값에 대한 타입을 추론할 수 있게됩니다.
KeyPath에는 기본적으로 읽기 전용입니다.
하지만 Swift에서는 쓰기도 가능한 KeyPath도 지원합니다.
WritableKeyPath<Root, Value>
와 ReferenceWritableKeyPath<Root, Value>
은
KeyPath의 일종으로, 속성에 대한 쓰기 작업을 지원하는 특징을 가지고 있습니다.
WritableKeyPath<Root, Value>
은 일반적인 속성에 대한 쓰기 작업을 지원합니다.
즉, 해당 KeyPath를 통해 속성 값을 읽고 쓸 수 있습니다.
ReferenceWritableKeyPath<Root, Value>
는 참조 타입에 대한 쓰기 작업을 지원합니다.
이는 클래스나 참조 타입에만 적용됩니다.
ReferenceWritableKeyPath
를 사용하면 속성 값을 변경할 수 있습니다.
WritableKeyPath<Root, Value>
와 @dynamicMemberLookup
을 활용하면
다음과 같은 방식으로 사용이 가능합니다.
struct Name {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
@dynamicMemberLookup
class Reference<Value> {
private(set) var value: Value
init(value: Value) {
self.value = value
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
get { value[keyPath: keyPath] }
set { value[keyPath: keyPath] = newValue }
}
}
let reference = Reference(value: Name(firstName: "jimmy", lastName: "jung"))
reference.firstName = "sam"
reference.lastName = "sung"
WritableKeyPath<Root, Value>
을 잘 활용하면 객체의 원하는 경로에 값을 가져오거나 값을 할당할 수 있게 됩니다.
Uploaded by N2T