Swift Academy · 강의

@dynamicMemberLookup

동적이면서도 형식이 안전한 멤버 접근을 구현합니다.

레슨 4/413개 단계

@dynamicMemberLookup은(는) CoddyKit의 무료 Swift Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Swift Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

동적 멤버 조회란 무엇인가요

@dynamicMemberLookup 특성은 실제 속성으로 선언되지 않은 멤버에도 점 표기법으로 접근할 수 있도록 형식이 응답하게 합니다. value.something을 작성하면 Swift가 이를 사용자가 정의한 특수 서브스크립트로 전달합니다.

이를 통해 래퍼 형식이 자연스럽게 동작하면서도 접근 로직을 한곳에서 관리할 수 있습니다.

@dynamicMemberLookup
struct Settings {
    private var storage: [String: String] = ["theme": "dark"]
    subscript(dynamicMember key: String) -> String? {
        storage[key]
    }
}

let s = Settings()
print(s.theme ?? "none")

필수 서브스크립트

@dynamicMemberLookup이 표시된 형식은 subscript(dynamicMember:)를 구현해야 합니다. 매개변수가 String이면 입력한 멤버 이름이 실행 시간에 해당 문자열이 됩니다.

따라서 config.timeout은 키 "timeout"과 함께 서브스크립트를 호출합니다.

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? 0
    }
}

let config = Config(values: ["timeout": 30, "retries": 3])
print(config.timeout)
print(config.retries)

문자열 조회는 형식 안전하지 않습니다

문자열 기반 동적 멤버 조회는 유연하지만 컴파일 시점의 검사를 잃게 됩니다. 멤버 이름을 잘못 입력해도 코드는 계속 컴파일되며, 누락된 키에 대해 서브스크립트가 반환하는 값이 그대로 전달됩니다.

아래에서는 알 수 없는 멤버가 빌드에 실패하는 대신 기본값을 조용히 반환합니다.

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? -1
    }
}

let config = Config(values: ["timeout": 30])
print(config.tmout)

키 경로를 사용한 형식 안전 조회

안전성을 높이려면 서브스크립트가 문자열 대신 KeyPath를 받도록 만들 수 있습니다. 그러면 컴파일러가 래핑된 형식에 해당 멤버가 존재하는지 확인하므로, 접근을 계속 전달하면서도 완전한 검사를 복원할 수 있습니다.

이 패턴은 형식이 명확하게 지정된 값을 보유하는 래퍼 형식에 적합합니다.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct Boxed {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

let boxed = Boxed(user: User(name: "Ada", age: 36))
print(boxed.name)
print(boxed.age)

키 경로 버전이 더 안전한 이유

서브스크립트가 KeyPath<User, T>를 받으므로 실제로 User에 존재하는 멤버만 허용됩니다. boxed.naem과 같은 오타는 문자열 버전과 달리 이제 컴파일에 실패합니다.

위험 요소 없이 점 표기법의 편리함을 누릴 수 있습니다.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct ReadOnly {
    let value: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        value[keyPath: path]
    }
}

let r = ReadOnly(value: User(name: "Lin", age: 22))
print(r.name, r.age)

쓰기 가능한 동적 멤버

서브스크립트가 설정자를 제공하고 WritableKeyPath를 받으면 전달된 멤버에 값을 할당할 수도 있습니다. 래퍼는 쓰기 작업을 래핑된 값으로 곧바로 전달합니다.

이를 통해 투명한 변경 가능 래퍼를 만들 수 있습니다.

struct Profile { var name: String; var score: Int }

@dynamicMemberLookup
struct Wrapper {
    var value: Profile
    subscript<T>(dynamicMember path: WritableKeyPath<Profile, T>) -> T {
        get { value[keyPath: path] }
        set { value[keyPath: path] = newValue }
    }
}

var w = Wrapper(value: Profile(name: "Mo", score: 0))
w.score = 99
print(w.value.score)

래핑된 객체로 전달하기

동적 멤버 조회는 내부 값으로 대부분의 작업을 전달하면서 약간의 동작을 추가하는 가벼운 프록시에 적합합니다. 서브스크립트가 전달 로직을 한곳에 모아 줍니다.

여기서는 로그를 기록하는 래퍼가 읽기 작업을 전달하고 어떤 멤버에 접근했는지 출력합니다.

struct Account { let id: Int; let balance: Double }

@dynamicMemberLookup
struct Logged {
    let account: Account
    subscript<T>(dynamicMember path: KeyPath<Account, T>) -> T {
        let result = account[keyPath: path]
        print("accessed a member")
        return result
    }
}

let logged = Logged(account: Account(id: 7, balance: 100))
print(logged.balance)

동적 호출 개념과 결합하기

동적 멤버 조회는 제네릭과 잘 결합됩니다. 하나의 제네릭 서브스크립트가 어떤 속성 형식이든 전달할 수 있으므로, 하나의 래퍼를 다양한 형태의 값에 사용할 수 있습니다.

여기서는 멤버가 문자열이든 정수든 동일한 Proxy가 작동합니다.

struct Item { let title: String; let count: Int }

@dynamicMemberLookup
struct Proxy<Wrapped> {
    let wrapped: Wrapped
    subscript<T>(dynamicMember path: KeyPath<Wrapped, T>) -> T {
        wrapped[keyPath: path]
    }
}

let proxy = Proxy(wrapped: Item(title: "Book", count: 4))
print(proxy.title, proxy.count)

JSON과 유사한 데이터를 위한 문자열 조회

문자열 방식에도 여전히 적합한 용도가 있습니다. 예를 들어 컴파일 시점에 멤버를 알 수 없는, 구문 분석된 JSON과 같은 구조가 느슨한 데이터를 표현할 때입니다. 각 접근은 사전 조회가 됩니다.

여기서는 작은 JSON 유사 값이 점 표기법을 통해 fields를 제공합니다.

@dynamicMemberLookup
struct JSON {
    let fields: [String: String]
    subscript(dynamicMember key: String) -> String {
        fields[key] ?? ""
    }
}

let json = JSON(fields: ["city": "Oslo", "country": "Norway"])
print(json.city)
print(json.country)

기억해 둘 제한 사항

동적 멤버 조회는 멤버 접근 문법에만 영향을 주며 실제 저장 속성을 추가하지는 않습니다. 자동 완성 같은 개발 도구는 키 경로 형식에서는 작동하지만 임의의 문자열 키에서는 작동하지 않습니다.

기반 데이터의 형태를 알고 있다면 언제나 키 경로 버전을 우선하고, 문자열 버전은 실제로 동적인 데이터에만 사용하십시오.

struct Point { let x: Int; let y: Int }

@dynamicMemberLookup
struct Safe {
    let point: Point
    subscript<T>(dynamicMember path: KeyPath<Point, T>) -> T {
        point[keyPath: path]
    }
}

let safe = Safe(point: Point(x: 3, y: 4))
print(safe.x + safe.y)

실용적인 데코레이터

지금까지의 내용을 합치면, 아래와 같이 내부 값에 대한 모든 속성 읽기를 전달하면서 접근 횟수를 세는 측정 기능이 있는 래퍼를 만들 수 있습니다. 실제 프레임워크에서는 관찰과 추적에 이 아이디어를 사용합니다.

래퍼에서 사용하는 점 표기법은 값을 직접 사용할 때와 똑같이 느껴집니다.

struct Sensor { let temperature: Double; let humidity: Double }

@dynamicMemberLookup
class Tracked {
    let sensor: Sensor
    var reads = 0
    init(_ s: Sensor) { sensor = s }
    subscript<T>(dynamicMember path: KeyPath<Sensor, T>) -> T {
        reads += 1
        return sensor[keyPath: path]
    }
}

let t = Tracked(Sensor(temperature: 21.5, humidity: 0.4))
print(t.temperature)
print(t.humidity)
print(t.reads)

빠른 확인: 형식 안전 동적 조회

동적 멤버 조회를 형식 안전하게 만드는 서브스크립트 매개변수가 무엇인지 떠올려 보십시오.

복습: 동적 멤버 조회

@dynamicMemberLookup의 작동 방식을 배웠습니다.

  • 이 특성은 선언되지 않은 점 구문 접근을 subscript(dynamicMember:)로 전달합니다.
  • String 매개변수를 사용하면 유연하지만 검사되지 않는 접근이 가능하므로 JSON과 유사한 데이터에 적합합니다.
  • KeyPath 매개변수를 사용하면 완전한 형식 안전성과 자동 완성을 되찾을 수 있습니다.
  • 설정자가 있는 WritableKeyPath를 사용하면 래퍼를 통해 쓰기 작업을 전달할 수 있습니다.
  • 이는 프록시 및 데코레이터 패턴과 자연스럽게 어울리는 문법 편의 기능입니다.

이제 키 경로의 전체 도구 모음인 읽기, 쓰기, 컬렉션 사용, 동적 전달을 익혔습니다.

struct User { let name: String }

@dynamicMemberLookup
struct Wrap {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

print(Wrap(user: User(name: "Final")).name)
무료로 시작

AI 튜터와 함께 Swift을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
122
레슨
409

자주 묻는 질문

“@dynamicMemberLookup” 강의는 무료인가요?

네 — “@dynamicMemberLookup” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Swift Academy 강의 전체를 잠금 해제할 수 있습니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“@dynamicMemberLookup”에서 뭘 배우나요?

동적이면서도 형식이 안전한 멤버 접근을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 Swift Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Swift Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Swift Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“@dynamicMemberLookup” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Swift Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Swift Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. KeyPath 기초
  2. 컬렉션에서 KeyPaths 사용하기
  3. 쓰기 가능 및 참조 KeyPaths
  4. @dynamicMemberLookup
← Swift Academy(으)로 돌아가기