@dynamicMemberLookup은 객체의 속성 접근을 실행 시간에 subscript를 통해 재정의할 수 있도록 해주는 스위프트의 주석입니다. 역사적으로 이 메커니즘은 파이썬과 같은 동적 언어와의 더 투명한 통합을 위해 도입되었으며, 프록시 객체, 동적 모델 및 DSL에서 데이터 접근에 더 유연한 라우팅을 목표로 합니다.
문제: 표준 스위프트는 속성을 명시적으로 선언해야 하며, 컴파일 시간에 그 존재 여부를 확인합니다. 그러나 경우에 따라 JSON, API, 프록시 객체, 스크립트 래퍼 작업 시 컴파일 타임에 존재하지 않는 이름으로 속성에 접근해야 할 필요가 있습니다.
해결책: @dynamicMemberLookup을 사용하고 존재하지 않는 속성에 접근하려는 시도를 가로채기 위해 subscript(dynamicMember:)를 구현합니다.
코드 예시:
@dynamicMemberLookup struct JSON { private var data: [String: Any] subscript(dynamicMember member: String) -> Any? { data[member] } } let user = JSON(data: ["name": "Anna", "age": 23]) print(user.name as? String) // Anna
주요 특징:
클래스에 대해 dynamicMemberLookup을 구현할 수 있나요, 아니면 struct만 가능한가요?
네, 클래스, struct 및 enum에 대해 적용할 수 있습니다(스위프트 5+부터). 해당 subscript를 구현하는 것만 중요합니다.
dynamicMemberLookup을 통해 존재하지 않는 속성에 접근하면 어떻게 되나요?
subscript에서 반환된 값이 반환되며, 값이 없을 경우의 처리는 구현에 맡겨집니다.
예를 들어, 위의 예제에서 user.secret에 접근하면 nil이 반환됩니다.
다른 타입의 키(예: Int)를 사용하여 동적 접근을 할 수 있나요?
네! 다른 인자 레이블로 subscript(dynamicMember:)를 선언하고 일반 subscript와 결합할 수 있습니다.
@dynamicMemberLookup struct ArrayProxy { private let array: [Int] subscript(dynamicMember member: String) -> Int? { if member == "first" { return array.first } if member == "last" { return array.last } return nil } subscript(index: Int) -> Int? { array[safe: index] } }
개발자가 일반 사용자 모델(User)에 대해 @dynamicMemberLookup을 사용하여 모든 필드를 문자열로 접근합니다. 코드가 불투명해지고 IDE는 힌트를 잃게 됩니다.
장점:
단점:
@dynamicMemberLookup은 사전 정의되지 않은 임의의 JSON 객체와 작업하는 데 사용됩니다. 이 메커니즘은 구조화되지 않은 데이터와 우아하고 안전하게(Any?/optional binding을 통해) 작업할 수 있도록 합니다.
장점:
단점: