
👋 들어가기 전
정말 오랜만에 컨커린시 관련 포스팅이다.
최근에 현생이 정말 정신이 없다.

주말은 쉬는 날인데 현재는 평일에 없는 학습시간을 위한 날인 것 같다.
정말 침대에 눕고 싶다. ㅋㅋㅋ
잡소리는 그만하고 오늘의 주제로 들어가기 전 간단한 사전 지식을 알아보자.
✊@property wrapper
프로퍼티 래퍼 정말 무의식적으로 많이 썼고 특히, SwiftUI를 사용할 때
많이 쓴 것 같다.
한번 구조를 간단하게 정리해보자.
전체적인 내용인 출처 첫번 째 링크를 참고했다.
목적
프로퍼티 래퍼의 목적은 반복적인 로직을 담당하는 하나의 타입처럼 선언하여
프로퍼티의 타입처럼 쉽게 사용할 수 있게 만들어주는 것이다.
구조
property wrapper는 크게 2가지 프로퍼티를 제공한다.
wrappedValue
wrappedValue는 가장 핵심적인 프로퍼티이며 필수 구현 프로퍼티다.
위에서 말한 반복적인 로직을 이곳에서 처리한다.
projectedValue
추가적인 로직이 필요할 때 사용하므로 구현 여부는 옵셔널이다.
사용할 때는 변수에 $를 붙혀 사용한다.
예
예는 String 값의 시작 값을 대문자로 변환해주고
upperCase를 projectedValue로 제공해주는 형태의 프로퍼티 래퍼를 만들어보자.
import Foundation
@propertyWrapper
struct Capitalized {
private var _value: String = ""
var wrappedValue: String {
get {
return _value.capitalized
}
set {
_value = newValue
}
}
var projectedValue: String {
return _value.uppercased()
}
init(_ value: String = "") {
self._value = value
}
}
struct Person {
@Capitalized var engName: String
func sayHello() {
print("Hello, my name is \(engName)")
print("Hello, my name is \(_engName)")
}
}
var p = Person()
p.engName = "abcde"
print(p.engName) //Abcde
print(p.$engName) //ABCDE
p.sayHello()
// Hello, my name is Abcde
// Hello, my name is Capitalized(_value: "abcde")
여기서 보면 sayHello 함수에서 만들지 않은 _engName 변수가 있다.
이건 propertywrapper로 만든 Capitalized로 접근할 수 있도록 변수 앞에 _를 붙혀 제공한다.
☝️구조 및 정의
구조
오늘의 주제인 @TaskLocal을 들어가기전 프로퍼티 래퍼에 대해 알아봤다.
왜냐하면 형태를 보면 알겠지만 @TaskLocal 역시 프로퍼티 래퍼 형태로 사용된다.
구현 구조를 살펴보면 다음과 같이 프로퍼티 래퍼와 매우 비슷한 구조이다.
wrappedValued와 projectValue가 보인다.

정의
공식문서를 보면 특정 스코프에 바인딩되어 사용할 수 있는 value 키다.
✌️특징
Task 메타데이터에 포함된다.
우리는 자식 Task에게 Task 메타데이터가 상속되는 것을 알고 있다.
우선순위, 실행중인 엑터 등 메타 데이터에 @TaskLocal 값이 포함된다.
static 형태로 선언되야 한다.
문법적인 약속, 별도의 작업간 데이터 공유 없이 사용하기 위해 그런 듯
옵셔널 또는 기본 값을 선언해야한다.
옵셔널로 선언해주거나 옵셔널이 아니라면 기본 값을 부여해야한다.
값의 변경은 wrappedValue로 하지 않는다.
위에 구조를 보면 당연한거지만 get-only 이므로 wrappedValue 자체는 변경이 불가능하다.

값의 변경은 projectedValue를 이용한다.
값의 직접적인 변경은 할 수 없고 projectedValue의 withValue를 이용해서 현재 Task 바운더리에서
@TaskLocal 값을 일시적으로 변경한다.

👍 예제 코드
코드를 보고 출력 값을 예상해보자.
import Foundation
enum Level {
case low
case medium
case high
}
class Game {
@TaskLocal
static var level: Level = .low
func printLevel() async {
print("현재 난이도는 \(Game.level) 입니다.")
}
}
let game = Game()
Task {
print("#1 location [첫번 째 Task] \(Game.$level.get())")
print("------------------------------------")
await Game.$level.withValue(.medium) {
print("값 변경: medium")
print("#2 location [첫번 째 Task] \(Game.$level.get())")
print("------------------------------------")
Task {
print("#3 location [두번 째 Task] \(Game.$level.get())")
print("------------------------------------")
}
}
print("첫번 째 값 변경 종료")
print("#4 location [첫번 째 Task] \(Game.$level.get())")
await Game.$level.withValue(.high) {
print("값 변경: high")
print("#5 location [첫번 째 Task] \(Game.$level.get())")
print("------------------------------------")
Task {
print("#6 location [세번 째 Task] \(Game.$level.get())")
}
}
}
#1 location [첫번 째 Task] low
------------------------------------
값 변경: medium
#2 location [첫번 째 Task] medium
------------------------------------
첫번 째 값 변경 종료
#4 location [첫번 째 Task] low
값 변경: high
#5 location [첫번 째 Task] high
------------------------------------
#3 location [두번 째 Task] medium
------------------------------------
#6 location [세번 째 Task] high
😀 목적
공통된 로직을 상태(예제로는 level)에 따라 전혀 다른 동작하게 컨트롤 하기위해 사용된다.
이 때 범위 제약적으로 컨트롤 한다는 중요한 특징을 기억하자.
출처
Property wrappers in Swift | Swift by Sundell
This week, let’s take a look at how Swift’s property wrappers work, and explore a few examples of situations in which they could be really useful.
www.swiftbysundell.com
TaskLocal | Apple Developer Documentation
Wrapper type that defines a task-local value key.
developer.apple.com
'iOS > Swift Concurrency' 카테고리의 다른 글
컨커런시 문법 정리 (0) | 2025.03.22 |
---|---|
Task Cancellation (0) | 2024.10.27 |
구조적 동시성 (1) (0) | 2024.10.26 |
Continuation (0) | 2024.10.25 |
async await (0) | 2024.10.24 |