문제: @Sendable 클로저 캡처 경고
TCA Reducer에서 @Dependency를 선언하고 .run { send in ... } 내부에서 self로 접근하면 Swift Concurrency 경고가 발생합니다.
// ❌ 잘못된 패턴
struct HomeFeature: Reducer {
@Dependency(\.childRepository) var childRepo
func reduce(into state: inout State, action: Action) -> Effect<Action> {
case .loadChildren:
return .run { send in
// ⚠️ Capture of 'self' with non-sendable type in @Sendable closure
let children = try await self.childRepo.fetchAll()
await send(.childrenLoaded(children))
}
}
}
문제 원인: @Dependency 프로퍼티가 Sendable을 보장하지 않는 구조체에 속해 있는데, 이를 @Sendable 클로저인 .run { } 안에서 캡처하려 하기 때문입니다.
해결 1: 로컬 let으로 먼저 바인딩
// ✅ 클로저 외부에서 먼저 캡처
func reduce(into state: inout State, action: Action) -> Effect<Action> {
case .loadChildren:
let repo = childRepo // ← 로컬 변수로 먼저 꺼내기
return .run { send in
let children = try await repo.fetchAll()
await send(.childrenLoaded(children))
}
}
해결 2: Repository 자체를 Sendable로 설계
// ✅ 가장 근본적인 해결 — Repository를 Sendable struct로
struct ChildRepository: Sendable {
var fetchAll: @Sendable () async throws -> [Child]
var create: @Sendable (ChildCreate) async throws -> Child
var delete: @Sendable (String) async throws -> Void
}
// Sendable이므로 캡처 리스트로 안전하게 전달
return .run { [childRepo] send in
let children = try await childRepo.fetchAll()
await send(.childrenLoaded(children))
}
TCA 공식 권장 패턴: struct + @Sendable 클로저
// TCA 스타일 — 모든 Repository/Client를 이렇게 정의
struct ScheduleRepository: Sendable {
var fetchToday: @Sendable () async throws -> [Schedule]
var create: @Sendable (ScheduleCreate) async throws -> Schedule
var update: @Sendable (String, ScheduleUpdate) async throws -> Schedule
var delete: @Sendable (String) async throws -> Void
static var live: Self {
Self(
fetchToday: { try await APIClient.shared.request("schedules/today") },
create: { try await APIClient.shared.request("schedules/", method: "POST", body: ...) },
update: { id, body in try await APIClient.shared.request("schedules/\(id)", method: "PUT", body: ...) },
delete: { id in try await APIClient.shared.request("schedules/\(id)", method: "DELETE") }
)
}
}
교훈
TCA와 Swift Concurrency를 함께 쓸 때는 Sendable 준수 여부에 항상 주의해야 합니다. Repository/Service 타입을 struct + @Sendable 클로저 프로퍼티로 설계하면 TCA @Dependency로 사용할 때 캡처 문제가 근본적으로 해결됩니다.