๋™๊ธฐ & ๋น„๋™๊ธฐ

synchronous๋Š” ์ž‘์—…์„ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰. ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์ด ๋๋‚  ๋•Œ ๊นŒ์ง€ ๋‹ค๋ฅธ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆผ

asynchronous๋Š” ๋น„๋™๊ธฐ๋กœ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์ด ์žˆ์–ด๋„ ๋‹ค๋ฅธ ์ž‘์—…์„ ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•ด์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ํ•„์š”๋กœ ํ•˜๋Š” ์ž‘์—…์ด ๋ฉˆ์ถ”์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.

 

MainActor

UI ๊ด€๋ จ ์ž‘์—…์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ, MainActor๋Š” ์ด๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. UI ์—…๋ฐ์ดํŠธ์™€ ๊ด€๋ จ๋œ ์ž‘์—…์„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•˜๋„๋ก ๋ณด์žฅํ•œ๋‹ค. ๋น„๋™๊ธฐ ์ž‘์—…์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ์˜ ์ ‘๊ทผ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์Šค๋ ˆ๋“œ ์•ˆ์ •์ ์ด๋ฉฐ DispatchQueue.main.async์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์–ด ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. 

struct DefaultProductDetailUseCase: ProductDetailUseCase {
    func fetchProductDetail(idx: Int) async throws -> Product {
        let (data, response) = try await URLSession.shared.data(from: URL(string: "https://dummyjson.com/products/\(idx)")!)
        guard let response = response as? HTTPURLResponse,
              (200..<300).contains(response.statusCode) else {
            throw CustomError.statusCodeError
        }
        
        guard let result = try? JSONDecoder().decode(Product.self, from: data) else {
            throw CustomError.invalidResponse
        }
        
        return result
    }
}

 

๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ ViewModel์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

class ProductDetailViewModel: ObservableObject {
    var useCase: ProductDetailUseCase?
    
    @Published var product: Product?
    
    init(useCase: ProductDetailUseCase) {
        self.useCase = useCase
    }
    
    func getchProductDetail(idx: Int) {
        Task {
            let product = try await self.useCase?.fetchProductDetail(idx: idx)
            await updateUI(with: product)
        }
    }
    
    @MainActor
    private func updateUI(with product: Product?) async {
        self.product = product
    }
}

 

MainActor์„ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ๋น„๋™๊ธฐ ์ž‘์—… ํ›„ UI ์—…๋ฐ์ดํŠธ ์ฝ”๋“œ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

Async

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋ ค๋ฉด async ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ค€๋‹ค. 

    func fetchProductDetail(idx: Int) async -> Product {
		// ์ƒ๋žต
    }

 

async ์ฝ”๋“œ๋Š” concurrent ์ปจํ…์ŠคํŠธ์—์„œ๋งŒ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋‹ค. Task ํ˜น์€ ๋‹ค๋ฅธ async ํ•จ์ˆ˜์—์„œ async ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. 

์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ async throws๋กœ ์ •์˜ํ•ด ์ค€๋‹ค. 

    func fetchProductDetail(idx: Int) async throws -> Product {
		// ์ƒ๋žต
    }

 

Await

        Task {
            let product = try await self.useCase?.fetchProductDetail(idx: idx)
            await updateUI(with: product)
        }

 

async ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ await ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. await์œผ๋กœ ๋งˆํ‚น๋œ ๊ณณ์€ potential suspension point(์ž ์žฌ์ ์ธ ์ผ์‹œ ์ค‘๋‹จ ์ง€์ )์œผ๋กœ ์ง€์ •๋œ๋‹ค. ์ฆ‰ ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๋ถ€๋ถ„๋„ ๋Œ€๊ธฐ ์ƒํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. 

์—ฌ๊ธฐ์„œ suspension point ์˜ suspend๋ž€ ์Šค๋ ˆ๋“œ๋ฅผ block ์‹œํ‚ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปจํŠธ๋กค์„ ํฌ๊ธฐํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์Šค๋ ˆ๋“œ์— ๋Œ€ํ•œ ์ œ์–ด๊ถŒ์„ system์—๊ฒŒ ์ „๋‹ฌํ•˜๊ฒŒ ๋˜๊ณ , ์‹œ์Šคํ…œ์€ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ดํ›„ ์‹œ์Šคํ…œ์€ ๋‹ค๋ฅธ ํŠน์ • ์Šค๋ ˆ๋“œ ์ œ์–ด๊ถŒ์„ ์ „๋‹ฌํ•ด์„œ suspension point ์ดํ›„ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. 

 

task

๋‹ค๋ฅธ Task์™€ ํ•จ๊ป˜ ๋™์‹œ์— ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๋™๊ธฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. SwiftUI์—์„œ๋Š” task view modifier๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. 

    @inlinable public func task(priority: TaskPriority = .userInitiated, _ action: @escaping @Sendable () async -> Void) -> some View

 

task modifier๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค. 

        VStack { ์ƒ๋žต }
        .task {
            do {
                try await vm.getProductDetail(idx: 1)
            } catch {
                print("error!")
            }
        }

 

getProductDetail(idx:)๋Š” error๋ฅผ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ๋Š” async throw ์ด๊ธฐ ๋•Œ๋ฌธ์— task modifier ๋‚ด๋ถ€์—์„œ try catch๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋งŒ์•ฝ task modifier๊ฐ€ ์•„๋‹Œ Button์„ ๋ˆŒ๋ €์„ ๋•Œ์™€ ๊ฐ™์€ ๋™์ž‘ ์ดํ›„์— ์ˆ˜ํ–‰๋˜์•ผ ํ•œ๋‹ค๋ฉด Task.init() ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

@frozen public struct Task<Success, Failure> : Sendable where Success : Sendable, Failure : Error {
}

 

initializer๋Š” task modifier์™€ ๋™์ผํ•˜๋‹ค. 

@discardableResult
public init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async -> Success)

 

 

            Button {
                Task {
                    do {
                        try await vm.getProductDetail(idx: 1)
                    } catch {
                        
                    }
                }
            } label: {
                Text("๋ฒ„ํŠผ์œผ๋กœ ์‹คํ–‰ ํ•  ๊ฒฝ์šฐ")
            }

 

Task๋กœ ์ƒ์„ฑ๋œ ์ž‘์—…์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฆ‰์‹œ ์‹คํ–‰๋˜๋ฉฐ await ํ‚ค์›Œ๋“œ๋กœ ํ•ด๋‹น ์ง€์ ์—์„œ ์™„๋ฃŒ๋œ ๊ฐ’์ด ๋Œ์•„์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ๋‹ค. 

 


inout paramters

concurrency๋ž‘ ๊ด€๋ จ๋œ๊ฑด ์•„๋‹ˆ์ง€๋งŒ ์ž์ฃผ ๋‚˜์™€์„œ ์ •๋ฆฌํ•˜์ž๋ฉด, inout์€ Swift ๋‚ด์— ์ •์˜๋œ ํ‚ค์›Œ๋“œ.

ํ•จ์ˆ˜์— ๊ฐ’์„ ์ „๋‹ฌํ•  ๋•Œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด๋•Œ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด๋„ ์›๋ž˜ ๋ณ€์ˆ˜ ๊ฐ’์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค. inout์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์›๋ณธ ๋ณ€์ˆ˜์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

class ProductDetailViewModel: ObservableObject {
    
    @Published var product: Product?
    
    func removeCurrentProduct() {
        self.removeProductRequest(product: &product)
    }
    
    private func removeProductRequest(product: inout Product?) {
        product = nil
    }
    
    // product๋ฅผ fetchํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ œ์™ธ๋จ
}

 

SwiftUI์˜ View์—์„œ removeCurenProduct๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด viewModel ๋‚ด๋ถ€์— ์ •์˜๋œ private ํ•จ์ˆ˜์ธ removeProductRequest๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ inout ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ์ „๋‹ฌ๋ฐ›์€ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด viewModel์˜ ์›๋ณธ ๋ณ€์ˆ˜์ธ @Published๋กœ ์ •์˜๋œ product์˜ ๊ฐ’์ด nil๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.

 

            Button {
                vm.removeCurrentProduct()
            } label: {
                Text("์ƒํ’ˆ ์ •๋ณด ์ œ๊ฑฐํ•˜๊ธฐ")
            }
            .buttonStyle(.borderedProminent)
            
            Text(vm.product?.title ?? "")

 

View ์ฝ”๋“œ๋กœ ์›๋ณธ product ๊ฐ’์˜ ๋ณ€๊ฒฝ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

removeProductRequest(product: &product)

 

๋งค๊ฐœ๋ณ€์ˆ˜ ์ „๋‹ฌ์‹œ &(ampersand)์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ inout์œผ๋กœ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Œ์„ ๋ช…์‹œ์ ์œผ๋กœ ์•Œ๋ ค์ค€๋‹ค.

 

Isolated Actor

  • ๋™์‹œ์„ฑ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํƒ€์ž…
  • Actor ๋‚ด์˜ ๋ฐ์ดํ„ฐ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋‚˜ ์ปจํ…์ŠคํŠธ์—์„œ ์ ‘๊ทผ ํ•  ์ˆ˜ ์—†์ง€๋งŒ ํŠน์ • ๋ฉ”์„œ๋“œ๋‚˜ ํ”„๋กœํผํ‹ฐ๋ฅผ isolated๋กœ ํ‘œ์‹œํ•˜๋ฉด ํ•ด๋‹น actor์˜ ์ƒํƒœ์— ์ ‘๊ทผํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ
  • ๋ชจ๋“  Actor ํƒ€์ž…์€ ์•”๋ฌต์ ์œผ๋กœ Sendable์„ ๋”ฐ๋ฅธ๋‹ค. actor๋Š” mutable state์— ๋Œ€ํ•œ isolation์„ ๋ณด์žฅํ•œ๋‹ค.

Sendable

  • Sendable ํƒ€์ž…์„ ์ค€์ˆ˜ํ•˜๋Š” ํƒ€์ž…์€ ํ•ด๋‹น ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์Šค๋ ˆ๋“œ ๊ฐ„ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ์Œ
  • Value Type์€ ๋‚ด๋ถ€์ ์œผ๋กœ Sendable ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋ฏ€๋กœ ์Šค๋ ˆ๋“œ์˜ ์•ˆ์ „์„ ๋ณด์žฅํ•œ๋‹ค. 
  • Reference Type์€ final ํด๋ž˜์Šค ํ˜น์€ ํด๋ž˜์Šค ๋‚ด ์ƒ์ˆ˜ ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•˜์—ฌ Sendable ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

(์–ด๋””์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์„์ง€๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค๐Ÿค”)

  public static func run(
    priority: TaskPriority? = nil,
    operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,
    catch handler: (@Sendable (_ error: Error, _ send: Send<Action>) async -> Void)? = nil,
    fileID: StaticString = #fileID,
    line: UInt = #line
  ) -> Self

 

  • TCA์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์ดํ›„ Effect๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด .run์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ด๋•Œ operation์— @Sendable์ด ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค(TCAํ•˜๋‹ค๊ฐ€ ์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹น ใ…‹ใ…‹ใ…‹ ใ… ใ… )
struct SendableLibraryStruct: Sendable {
    var name: String
    var bookCount: Int
}
  • ๊ฐ’ ํƒ€์ž…์ธ ๊ตฌ์กฐ์ฒด๋Š” Sendable์„ ์ค€์ˆ˜ํ•ด์ค€๋‹ค.
final class SendableLibraryClass: Sendable {
    let name: String
    let bookCount: Int
    
    init(name: String, bookCount: Int) {
        self.name = name
        self.bookCount = bookCount
    }
}
  • ์ฐธ์กฐ ํƒ€์ž…์ธ class๋Š” final class์—ฌ์•ผ ํ•˜๊ณ  Sendable์„ ์ค€์ˆ˜ํ•ด ์ค€๋‹ค.
actor SendableTestActor {
    private var libraryStruct = SendableLibraryStruct(name: "", bookCount: 0)
    private var libraryClass = SendableLibraryClass(name: "", bookCount: 0)
    
    func updateDataWithStruct(libraryInfo: SendableLibraryStruct) {
        self.libraryStruct = libraryInfo
    }
    
    func updateDataWithClass(libraryInfo: SendableLibraryClass) {
        self.libraryClass = libraryInfo
    }
    
    func getNameFromClass() async throws -> String {
        try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
        return libraryClass.name
    }
    
}
  • actor๋Š” ๋™์‹œ์„ฑ ์ ‘๊ทผ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•œ๋‹ค.
  • ๋‚ด๋ถ€์˜ ๋ฐ์ดํ„ฐ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋‚˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.
  • ํŠน์ • ๋ฉ”์„œ๋“œ๋‚˜ ํ”„๋กœํผํ‹ฐ๋ฅผ isolated๋กœ ํ‘œ์‹œํ•˜๋ฉด ํ•ด๋‹น actor ์ƒํƒœ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€์™€ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
class SendableTestViewModel: ObservableObject {
    @Published var userName: String = ""
    let testActor = SendableTestActor()
    
    func updateStruct() async {
        let libraryInfo = SendableLibraryStruct(name: "kim", bookCount: 19)
        await testActor.updateDataWithStruct(libraryInfo: libraryInfo)
    }
    
    func updateClass() async {
        let libraryInfo = SendableLibraryClass(name: "lee", bookCount: 20)
        await testActor.updateDataWithClass(libraryInfo: libraryInfo)
    }
    
    @MainActor
    func getName() async {
        do {
            self.userName = try await testActor.getNameFromClass()
        } catch {
            
        }
        
    }
}

 

ViewModel์— actor์ธ SendableTestActor๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์Šค๋ ˆ๋“œ ๊ฐ„์— ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ’์„ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. updateStruct()์™€ updateClass๋Š” ๊ฐ’์„ ์ „๋‹ฌ ๋ฐ›์•„ ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ˆ˜์ •ํ•œ๋‹ค.

 

    func getNameFromClass() async throws -> String {
        try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
        return libraryClass.name
    }

 

actor์ธ SendableTestActor์„ ํ™•์ธํ•ด ๋ณด๋ฉด ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ์ค‘ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ์ด๋ฅผ viewModel์—์„œ ์ „๋‹ฌ๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

    @MainActor
    func getName() async {
        do {
            self.userName = try await testActor.getNameFromClass()
        } catch {
            
        }
    }

 

actor์„ ์‚ฌ์šฉํ•˜๋ฉด actor์˜ state์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋ฉฐ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

References

 

[WWDC21] Use async/await with URLSession์„ ์ ์šฉํ•ด๋ณด์ž..!

ํ•ด๋‹น ๊ธ€์€ [WWDC21] Use async/await with URLSession ๋ณด๊ณ  ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. Swift Concurrency ์— ๋Œ€ํ•œ ์–˜๊ธฐ๊ฐ€ ๋ช‡๋ช‡ ๋‚˜์™”์—ˆ๋‹ค. Swift Concurrency์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•ด๋ณด์ž๋ฉด, ์ฝ”๋“œ๋ฅผ ์„ ํ˜•์ ์ด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , Nat

dev-mandos.tistory.com

 

[Swift] async / await & concurrency

Swift 5.5 ์—์„œ ๋“ฑ์žฅํ•œ ๋น„๋™๊ธฐ์™€ ๋™์‹œ์„ฑ์„ ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…์‹œ๋‹ค

sujinnaljin.medium.com

https://www.hackingwithswift.com/sixty/5/10/inout-parameters

 

[SwiftUI] Sendable

What is the Sendable protocol in Swift? | Swift Concurrency ๋น„๋™๊ธฐ ํ™˜๊ฒฝ(async)์—์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋„ ์•ˆ์ „ํ•œ์ง€ ์ ๊ฒ€ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ ๋ณด์žฅํ•˜๋Š” ์•กํ„ฐ, ํŠน์ • ์•กํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ ์•ˆ

velog.io

 

 

[Swift] Sendable

Sendable ๋ฌธ์„œ์™€ WWDC 22 > Eliminate data races using Swift Concurrency ๋ฅผ ์กฐํ•ฉํ•ด์„œ ์žฌ๊ตฌ์„ฑํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. [1] Sendable = safe to share Sendable ํ”„๋กœํ† ์ฝœ์€ concurrencey ์ƒํ™ฉ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๊ณต์œ ๋  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์„ ๋‚˜ํƒ€๋ƒ…

eunjin3786.tistory.com

+) ChatGPT

์ดํ›„ ๊ณต๋ถ€ํ•  ๊ฒƒ

 

Explore structured concurrency in Swift - WWDC21 - Videos - Apple Developer

When you have code that needs to run at the same time as other code, it's important to choose the right tool for the job. We'll take you...

developer.apple.com

 

 

Sendable and @Sendable closures explained with code examples

The Sendable protocol and @Sendable attribute help to eliminate data races and create thread-safety in Swift Concurrency.

www.avanderlee.com

 

https://pixabay.com/ko/photos/%EC%B9%98%EC%A6%88-%EB%84%A4%EB%8D%9C%EB%9E%80%EB%93%9C-%EC%B9%98%EC%A6%88-%EC%8B%9C%EC%9E%A5-8437668/