Swift의 Property Wrapper(프로퍼티 래퍼) 정의와 사용법
Swift의 Property Wrapper(프로퍼티 래퍼)로 프로퍼티를 정의하고 사용하는 방법을 배워보세요.

Swift에서 Property Wrapper 이란?
Property Wrapper는 Class 또는 Struct 인스턴스의 프로퍼티에 값을 할당할 때 유효성 검사를 하기 위해 게터(getter)와 세터(setter), 연산 프로퍼티(computed property)를 추가하는 것을 캡슐화하여 코드를 간결하고 재사용 가능하게 합니다.
Property Wrapper의 기본구조
Property Wrapper는 @propertyWrapper 키워드를 사용해 정의하며, wrappedValue라는 필수 속성을 포함합니다.
Property Wrapper를 정의하는 방법은 다음과 같습니다.
@propertyWrapper
struct Capitalized {
    private var value: String = ""
 
    var wrappedValue: String {
        get { value }
        set { value = newValue.capitalized }
    }
 
    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue
    }
}위에서 사용한 @Capitalized을 사용하면 속성 값을 자동으로 대문자로 변환합니다.
struct Person {
    @Capitalized var name: String
}
 
let person = Person(name: "john")
print(person.name) // JohnProperty Wrapper의 활용
1. 데이터 검증
사용자 입력값 검증에 활용할 수 있습니다.
@propertyWrapper
struct Clamped<Value: Comparable> {
    private var value: Value
    private let range: ClosedRange<Value>
 
    var wrappedValue: Value {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
 
    init(wrappedValue: Value, range: ClosedRange<Value>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}
 
struct Settings {
    @Clamped(range: 0...100) var brightness: Int = 50
}
 
var settings = Settings()
settings.brightness = 120
print(settings.brightness) // 1002. 로깅
값이 변경될 때마다 기록을 남길 수 있다.
@propertyWrapper
struct Logged<Value> {
    private var value: Value
 
    var wrappedValue: Value {
        get { value }
        set {
            print("Value changed from \(value) to \(newValue)")
            value = newValue
        }
    }
 
    init(wrappedValue: Value) {
        self.value = wrappedValue
    }
}
 
struct Logger {
    @Logged var action: String
}
 
var logger = Logger(action: "Start")
logger.action = "Pause"
// Value changed from Start to Pause
logger.action = "Stop"
// Value changed from Pause to Stop3. 캐싱
값을 캐싱하여 계산 비용을 줄일 수 있습니다.
@propertyWrapper
class Cached<Value> {
    private var value: Value?
    private let computation: () -> Value
 
    var wrappedValue: Value {
        if let value = value {
            return value
        } else {
            let computed = computation()
            value = computed
            return computed
        }
    }
 
    init(wrappedValue: @escaping () -> Value) {
        self.computation = wrappedValue
    }
}
 
struct DataManager {
    @Cached { expensiveCalculation() } var cachedValue: Int
}
 
func expensiveCalculation() -> Int {
    print("Expensive calculation performed")
    return 42
}
 
var manager = DataManager()
print(manager.cachedValue) // Expensive calculation performed \n 42
print(manager.cachedValue) // 42