| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- swiftdata
- webp
- github 시작하기
- 코드스쿼드
- 캐러셀
- Cocoa Pod
- TestFlight
- .pbxproj
- png
- 팀 개발을 위한 git
- 타뷸레이션
- JPEG
- xcode 엔터 표시
- spm 에러
- Firestore
- 무한스크롤
- NSTextStorageDelegate
- JPG
- SwiftUI
- 클린 아키텍처
- swift 모듈화
- 테스트 타겟
- NSTextStorage
- 함께자라기
- Tuist
- contentalignmentpoint
- heic
- fetchdescriptor
- xcode 공백 표시
- nidthirdpartylogin
- Today
- Total
Sure, Why not?
@propertyWrapper 프로퍼티 래퍼란? - 1 본문

프로퍼티 래퍼를 직접 만들기 전에,
SwiftUI나 이미 Apple의 여러 프레임워크에서 제공되는 프로퍼티 래퍼가 제공되고 있다면
이미 검증된 시스템 기능을 다시 구현해서 비효율적인 작업이 될 수 있다.
그래서 먼저 SwiftUI 범위 내에서 Apple이 의도적으로 제공하는 검증된 프로퍼티 래퍼들을 정리한다면
이후에 내가 필요한 경우의 커스텀 프로퍼티 래퍼를 설계하는 것이 생산적인 접근이라고 생각해서
정리하고자 한다.
Property Wrappers
Documentation
docs.swift.org
프로퍼티의 값이 어떻게 저장되거나 관리되는지에 대한 로직과
프로퍼티의 선언부와 분리해주는 Swift의 언어 기능이다.
즉, 보일러플레이트 코드 하나의 타입으로 캡슐화하여서 선언부를 간결하게 만들고 의도를 명확히 표현하는 기능!
원래라면 각 상황에 맞게 모든 프로퍼티마다 반복해야 하지만,
- 값 범위 제한
- 스레드 안전성 검사
- 캐싱 / DB 저장
- 유효성 검사
- 상태 추적
프로퍼티 래퍼를 사용하면
- 관리 로직은 한 번만 작성하고
- 여러 프로퍼티에 재사용하여서
- 선언부는 깔끔하게 유지할 수 있다.
구성요소
문서의 예시코드를 바탕으로 구성요소를 파악하고자 한다.
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0".
rectangle.height = 10
print(rectangle.height)
// Prints "10".
rectangle.height = 24
print(rectangle.height)
// Prints "12".
1. @propertyWrapper 으로 프로퍼티 래퍼를 선언한다.
2. wrappedValue 실제로 프로퍼티처럼 접근되는 값으로
height, width 같은 프로퍼티에 접근하면 사실상 wrappedValue 통해 접근된다.
프로퍼티 래퍼를 사용하는 것은
내부적으로 getter와 setter를 직접 구현한 코드를 컴파일러가 자동으로 생성해주는 syntactic sugar일 뿐이다.
syntactic sugar : 기능은 그대로인데, 코드를 더 읽기 쉽게 만든 문법으로 표현됨
(물론 내부 동작을 이해하지 못한다면 디버깅 단계에서 당뇨걸릴 수 있다고 한다.)
초기화
기본적으로 property Wrapper는 내부에서 정의한 초기값을 그대로 사용한다.
위의 코드의 래퍼 내부에서 number = 0으로 초기화하면,
이를 사용하는 쪽에서는 다른 초기값을 지정할 수 없다.
이를 해결할려면 프로퍼티 래퍼에 initializer를 직접 정의해줘야 한다.
SmallNumber 예제를 기준으로 보면,
프로퍼티 래퍼 초기화 방식은 다음 3가지로 동작된다.
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
1. 초기값을 지정하지 않은 경우 -> init()
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
- 래퍼 내부의 기본값(number = 0, maximum = 12)이 적용됨
2. 초기값을 지정한 경우 -> init(wrappedValue:)
struct UnitRectangle {
@SmallNumber var height: Int = 1
}
단순한 대입이 아니라,
- 내부적으로 init(wrappedValue: 1) 호출
3. 래퍼에 파라미터를 전달한 경우 -> 커스텀 initializer
struct NarrowRectangle {
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
}
래퍼에 전달한 인자를 바탕으로 초기화됨
bonus. 파라미터 + 초기값을 함께 사용하는 경우
struct MixedRectangle {
@SmallNumber(maximum: 9) var width: Int = 2
}
프로퍼티 래퍼의 Projected Value
프로퍼티 래퍼는 wrappedValue를 통해,
프로퍼티의 실제 값 접근을 제어하는 역할을 한다.
하지만 경우에 따라서는 값을 넘어서,
래퍼의 내부적인 상태나 부가적인 정보가 필요한 상황도 필요하다.
이를 위해 Projected Value라는 개념을 제공한다.
Projected Value란?
프로퍼티 래퍼는 wrappedValue외에
추가적인 정보를 $ 접두어 를 통해 노출할 수 있다.
- property → 실제 값 (wrappedValue)
- $property → 래퍼가 제공하는 부가 정보 (projectedValue)
이때 $ 로 시작하는 이름은
Swift 문법상 사용자가 직접 정의할 수 없기 때문에
기존 프로퍼티와 충돌할 걱정이 없다.
앞서 사용한 SmallNumber 예제를 확장하면,
@propertyWrapper
struct SmallNumber {
private var number: Int
private(set) var projectedValue: Bool
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
init() {
self.number = 0
self.projectedValue = false
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false".
someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true".
값은 그대로 쓰되, 래퍼의 판단 결과를 함께 보여주는 구조가 된다.
SwiftUI의 $를 사용한 바인딩 문법 또한 이를 바탕으로 동작된다.
이렇게 보니깐 신기하다.
마지막으로 property Wrapper는 local stored variable 에 적용할 수 있다.
전역 변수와 계산 프로퍼티에 적용할 수 없음
전역변수는 lazy 특성을 가져서 생명주기가 달라 허용되지 않는 것 같고,
프로퍼티 래퍼의 인스턴스를 저장해야하는데, 계산 프로퍼티는 저장공간을 가지지 않으니 허용되지 않으니깐?
이제 프로퍼티 래퍼 자체를 파악했으니,
다음은 SwiftUI에서 제공되는 프로퍼티 래퍼들을 다 알아보자.
'💻' 카테고리의 다른 글
| @State, @Binding (프로퍼티 래퍼 - 2 ) (0) | 2025.12.24 |
|---|---|
| SwiftUI ) @Published는 어떻게 동작하는가 - objectWillChange (1) | 2025.11.27 |
| UITextView에서 한글 스타일 적용 (1) | 2025.10.13 |
| async let vs TaskGroup vs 연속 await (2) | 2025.08.28 |
| Core Bluetooth (3) | 2025.07.21 |