ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TCA] Sharing logic with actions
    TCA 2023. 8. 14. 10:57

    μ•‘μ…˜μ„ μ‚¬μš©ν•˜μ—¬ λ¦¬λ“€μ„œμ˜ μ—¬λŸ¬ λΆ€λΆ„μ—μ„œ λ‘œμ§μ„ κ³΅μœ ν•˜λŠ” 일반적인 νŒ¨ν„΄μ΄ μžˆλŠ”λ°, μ΄λŠ” λΉ„νš¨μœ¨μ μΈ νŒ¨ν„΄μ΄λ‹€.

     

    μ•‘μ…˜μ„ μ „μ†‘ν•˜λŠ” 것은 ν΄λž˜μŠ€μ—μ„œ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” κ²ƒλ§ŒνΌ κ°€λ²Όμš΄ μž‘μ—…μ΄ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€. μ•‘μ…˜μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ—¬λŸ¬ λ ˆμ΄μ–΄λ₯Ό ν†΅κ³Όν•˜λ©° 각 λ ˆμ΄μ–΄λ§ˆλ‹€ λ¦¬λ“€μ„œκ°€ μ•‘μ…˜μ„ κ°€λ‘œμ±„κ³  μž¬ν•΄μ„ν•  수 μžˆλ‹€.

    λŒ€μ‹ , Reducerλ₯Ό μ€€μˆ˜ν•˜λ©΄μ„œ κ°„λ‹¨ν•œ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‘œμ§μ„ κ³΅μœ ν•˜λŠ” 것이 훨씬 더 쒋은 방법이닀. 이 λ©”μ„œλ“œλŠ” 변이λ₯Ό ν•΄μ•Όν•  경우, Stateλ₯Ό inout 인자둜 λ°›μ•„μ„œ μ‚¬μš©ν•  수 있고, Effect<Action>을 λ°˜ν™˜ν•  수 μžˆλ‹€. 이λ₯Ό 톡해 λΆˆν•„μš”ν•œ μ•‘μ…˜μ„ μ „μ†‘ν•˜λŠ” λΉ„μš©μ„ λ°œμƒμ‹œν‚€μ§€ μ•Šκ³  λ‘œμ§μ„ κ³΅μœ ν•  수 μžˆλ‹€.

     

    예λ₯Ό λ“€μ–΄, 피쳐에 3개의 UI μ»΄ν¬λ„ŒνŠΈκ°€ 있고 μ–΄λ–€ μ»΄ν¬λ„ŒνŠΈκ°€ λ³€κ²½λ˜λ©΄ ν•΄λ‹Ή μƒνƒœ ν•„λ“œλ₯Ό μ—…λ°μ΄νŠΈν•˜κ³ , 변이λ₯Ό μˆ˜ν–‰ν•˜μ—¬ μ΄νŽ™νŠΈλ₯Ό μ‹€ν–‰ν•˜λ €κ³  ν•œλ‹€κ³  κ°€μ •ν•΄λ³΄μž. μ΄λŸ¬ν•œ κ³΅ν†΅λœ 변이와 μ΄νŽ™νŠΈλ₯Ό λ³„λ„μ˜ μ•‘μ…˜μœΌλ‘œ μ •μ˜ν•˜κ³  각 μ‚¬μš©μž μ•‘μ…˜μ€ ν•΄λ‹Ή 곡유 μ•‘μ…˜μ„ μ¦‰μ‹œ μ‹€ν–‰ν•˜λŠ” μ΄νŽ™νŠΈλ₯Ό λ°˜ν™˜ν•  수 μžˆλ‹€.

    struct Feature: Reducer {
      struct State {
        // ...
      }
      enum Action {
        // ...
      }
    
      func reduce(into state: inout State, action: Action) -> Effect<Action> {
        switch action {
        case .buttonTapped:
          state.count += 1
          return .send(.sharedComputation)
    
        case .toggleChanged:
          state.isEnabled.toggle()
          return .send(.sharedComputation)
    
        case let .textFieldChanged(text):
          state.description = text
          return .send(.sharedComputation)
    
        case .sharedComputation:
          // Some shared work to compute something.
          return .run { send in
            // A shared effect to compute something
          }
        }
      }
    }
    

    μ΄λŠ” 둜직과 μ΄νŽ™νŠΈλ₯Ό κ³΅μœ ν•˜λŠ” λ°©λ²•μ΄μ§€λ§Œ, μ‚¬μš©μžκ°€ ν•˜λ‚˜μ˜ μ•‘μ…˜μ„ μˆ˜ν–‰ν–ˆμŒμ—λ„ λΆˆκ΅¬ν•˜κ³  2개의 μ•‘μ…˜μ— λŒ€ν•œ λΉ„μš©μ΄ λ°œμƒν•˜κ³  μžˆμœΌλ―€λ‘œ λΉ„νš¨μœ¨μ μ΄λ‹€.

    μ΄λŸ¬ν•œ μ„±λŠ₯ 문제 외에도 이 νŒ¨ν„΄μ„ λ”°λ₯΄μ§€ μ•Šμ•„μ•Ό ν•˜λŠ” 2κ°€μ§€ μ΄μœ κ°€ μžˆλ‹€.

     

    첫째, 이런 ν˜•μ‹μ˜ 둜직 곡유 방식은 μœ μ—°μ„±μ΄ λΆ€μ‘±ν•˜λ‹€. 곡유 둜직이 λ³„λ„μ˜ μ•‘μ…˜μœΌλ‘œ λΆ„λ¦¬λ˜κΈ° λ•Œλ¬Έμ— 항상 초기 둜직 이후에 μ‹€ν–‰λ˜μ–΄μ•Ό ν•œλ‹€. ν•˜μ§€λ§Œ 초기 둜직 μ‹€ν–‰ 전에 일뢀 곡유 λ‘œμ§μ„ μ‹€ν–‰ν•΄μ•Ό ν•  경우 μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒ? 이런 κ²½μš°μ—λŠ” 이 νŒ¨ν„΄μ„ μ μš©ν•˜κΈ° μ–΄λ ΅λ‹€.

    λ‘˜μ§Έ, 이런 ν˜•μ‹μ˜ 둜직 곡유 방식은 ν…ŒμŠ€νŠΈλ₯Ό μ–΄λ ΅κ²Œ λ§Œλ“ λ‹€. μ‚¬μš©μž μ•‘μ…˜μ„ 전솑할 λ•Œ 곡유 μ•‘μ…˜μ„ μˆ˜μ‹ ν•˜κ³  μƒνƒœ 변경을 확인해야 ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν…ŒμŠ€νŠΈκ°€ λΆˆν•„μš”ν•œ λ‚΄λΆ€ μ„ΈλΆ€μ‚¬ν•­μœΌλ‘œ 팽창되고, ν…ŒμŠ€νŠΈκ°€ μ‚¬μš©μžκ°€ κΈ°λŠ₯을 μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”μ§€μ— λŒ€ν•œ λͺ…ν™•ν•œ 슀크립트둜 μ½νžˆμ§€ μ•Šκ²Œ λœλ‹€.

    let store = TestStore(initialState: Feature.State()) {
      Feature()
    }
    
    store.send(.buttonTapped) {
      $0.count = 1
    }
    store.receive(.sharedComputation) {
      // Assert on shared logic
    }
    store.send(.toggleChanged) {
      $0.isEnabled = true
    }
    store.receive(.sharedComputation) {
      // Assert on shared logic
    }
    store.send(.textFieldChanged("Hello") {
      $0.description = "Hello"
    }
    store.receive(.sharedComputation) {
      // Assert on shared logic
    }

    λ”°λΌμ„œ λ¦¬λ“€μ„œ λ‚΄μ—μ„œ μ „μš© μ•‘μ…˜μ„ μ‚¬μš©ν•˜μ—¬ 동기적 μ΄νŽ™νŠΈλ₯Ό μ‹€ν–‰ν•˜λŠ” λ°©μ‹μœΌλ‘œ λ‘œμ§μ„ κ³΅μœ ν•˜λŠ” νŒ¨ν„΄μ€ μΆ”μ²œν•˜μ§€ μ•ŠλŠ”λ‹€.

     

    λŒ€μ‹ , ν”Όμ³μ˜ λ¦¬λ“€μ„œμ— μ •μ˜λœ λ©”μ„œλ“œλ‘œ λ‘œμ§μ„ κ³΅μœ ν•˜λŠ” 것을 μΆ”μ²œν•œλ‹€. 이 λ©”μ„œλ“œλŠ” λͺ¨λ“  쒅속성에 λŒ€ν•œ 전체 μ•‘μ„ΈμŠ€ κΆŒν•œμ„ κ°–κ³ , μƒνƒœλ₯Ό λ³€κ²½ν•΄μ•Ό ν•˜λŠ” 경우 inout Stateλ₯Ό μ·¨ν•˜κ³ , μ΄νŽ™νŠΈλ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•  경우 Effect<Action>을 λ°˜ν™˜ν•  수 μžˆλ‹€.

    μœ„μ˜ μ˜ˆμ œλŠ” λ‹€μŒκ³Ό 같이 λ¦¬νŒ©ν„°λ§ν•  수 μžˆλ‹€.

    struct Feature: Reducer {
      struct State {
        // ...
      }
      enum Action {
        // ...
      }
    
      func reduce(into state: inout State, action: Action) -> Effect<Action> {
        switch action {
        case .buttonTapped:
          state.count += 1
          return self.sharedComputation(state: &state)
    
        case .toggleChanged:
          state.isEnabled.toggle()
          return self.sharedComputation(state: &state)
    
        case let .textFieldChanged(text):
          state.description = text
          return self.sharedComputation(state: &state)
        }
      }
    
      func sharedComputation(state: inout State) -> Effect<Action> {
        // Some shared work to compute something.
        return .run { send in
          // A shared effect to compute something
        }
      }
    }

    이 방식은 이전과 λ™μΌν•˜κ²Œ λ™μž‘ν•˜μ§€λ§Œ, μ‚¬μš©μž μ•‘μ…˜μ΄ 전솑될 λ•Œ λͺ¨λ“  둜직이 좔가적인 μ•‘μ…˜μ„ 보내지 μ•Šκ³  ν•œ λ²ˆμ— μ‹€ν–‰λœλ‹€. λ˜ν•œ μœ„μ—μ„œ μ–ΈκΈ‰ν•œ λ‹€λ₯Έ λ¬Έμ œλ“€λ„ ν•΄κ²°ν•œλ‹€.

     

    예λ₯Ό λ“€μ–΄ 핡심 λ‘œμ§λ³΄λ‹€ 곡유 λ‘œμ§μ„ λ¨Όμ € μ‹€ν–‰ν•΄μ•Ό ν•˜λŠ” 경우, λ‹€μŒκ³Ό 같이 μ‹€ν–‰ν•  수 μžˆλ‹€.

    case .buttonTapped:
      let sharedEffect = self.sharedComputation(state: &state)
      state.count += 1
      return sharedEffect

    이런 μ‹μœΌλ‘œ 곡유 λ‘œμ§μ„ μ–Έμ œ, μ–΄λ–»κ²Œ, μ–΄λ””μ„œ μ‹€ν–‰ν• μ§€λ₯Ό μœ μ—°ν•˜κ²Œ κ²°μ •ν•  수 μžˆλ‹€.

     

    λ˜ν•œ μ „μ†‘λ˜λŠ” 곡유 μ•‘μ…˜μ˜ μΆ”κ°€ λ‚΄λΆ€ 세항을 확인할 ν•„μš”κ°€ μ—†μœΌλ―€λ‘œ ν…ŒμŠ€νŠΈκ°€ 더 간단해진닀. ν…ŒμŠ€νŠΈλŠ” μ‚¬μš©μžκ°€ κΈ°λŠ₯μ—μ„œ 무엇을 μˆ˜ν–‰ν•˜λŠ”μ§€μ— λŒ€ν•œ 슀크립트처럼 읽히게 λœλ‹€.

    let store = TestStore(initialState: Feature.State()) {
      Feature()
    }
    
    store.send(.buttonTapped) {
      $0.count = 1
      // Assert on shared logic
    }
    store.send(.toggleChanged) {
      $0.isEnabled = true
      // Assert on shared logic
    }
    store.send(.textFieldChanged("Hello") {
      $0.description = "Hello"
      // Assert on shared logic
    }

     

    원문

    https://pointfreeco.github.io/swift-composable-architecture/1.0.0/documentation/composablearchitecture/performance/#Sharing-logic-with-actions

    λŒ“κΈ€

Designed by Tistory.