Skip to content

Conversation

@ljdongz
Copy link
Collaborator

@ljdongz ljdongz commented Apr 25, 2025

#️⃣ 연관된 이슈

📝 작업 내용

  • MainView에서 사용될 ViewModel을 정의했습니다.
  • 구현한 ViewModel을 실제 화면과 연결시켰습니다.
  • 앱에 첫 진입 시 데이터를 불러온다는 가정으로 로딩 화면을 구현했습니다.

🎨 스크린샷

2025-04-25.5.50.12.mov

💬 추가 설명

ViewModel을 구현하는 과정에서 TCA, ReactorKit처럼 단방향 흐름을 구현해보고자 Reducer protocol을 정의했습니다.

1. Reducer protocol 정의

protocol Reducer {
  associatedtype State
  associatedtype Action
  
  // View에서 액션을 전달
  func send(_ action: Action)

  // 현재 상태와 전달받은 액션으로 새로운 Action을 생성
  // 현재 상태 + 액션 -> 새로운 액션을 만든다는 reduce 이름에 맞게 순수 함수로 구현하기 위해 
  // State를 inout 파라미터로 전달 받음
  func reduce(state: inout State, action: Action) -> Effect<Action>
}

// 사이드 이펙트를 처리하기 위한 열거형
enum Effect<Action> {
  case none // 사이드 이펙트 없음
  case run(() async -> Action) // 비동기 작업 후 새로운 액션을 생성
}

2. Reducer 프로토콜 구현부 (ViewModel)

@Observable
final class MainViewModel: Reducer {
  struct State {
    var isLoading: Bool = true
  }
  
  enum Action {
    case onAppear
    case refresh
  }
  
  // 상태를 ViewModel 내부에서만 변경할 수 있도록 set에 private 접근 제어자 설정
  private(set) var state: State = .init()
  
  func send(_ action: Action) {
    // 현재 상태와 View에서 입력받은 액션으로 새로운 액션(Effect)을 생성
    let effect = reduce(state: &state, action: action)

    // Effect 처리
    handleEffect(effect)
  }
  
  // 새로운 액션을 생성
  func reduce(state: inout State, action: Action) -> Effect<Action> {
    switch action {
    case .onAppear:
      // 사이드 이펙트 작업은 .run을 통해 비동기 작업으로 진행
      return .run {
        try? await Task.sleep(nanoseconds: 3_000_000_000)
        return .refresh // 비동기 작업 이후 다시 액션을 호출
      }
    case .refresh:
      state.isLoading = false // 상태를 변경
      return .none // 사이드 이펙트 작업을 하지 않음
  }
  
  // 새로 생성된 액션(Effect)을 처리
  private func handleEffect(_ effect: Effect<Action>) {
    switch effect {
    // 사이드 이펙트가 아닌 경우 종료
    case .none: break

    // 비동기 처리 후 해당 작업이 종료되면 새로운 액션 호출
    case .run(let action):
      Task { send(await action()) }
    }
  }
}

@ljdongz ljdongz requested a review from f-lab-barry April 25, 2025 09:14
@ljdongz ljdongz self-assigned this Apr 25, 2025
@ljdongz ljdongz linked an issue Apr 25, 2025 that may be closed by this pull request
Copy link

@f-lab-barry f-lab-barry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@sonarqubecloud
Copy link

@ljdongz ljdongz merged commit 4a7f75b into develop Apr 28, 2025
2 checks passed
@ljdongz ljdongz deleted the feature/#17 branch April 28, 2025 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] MainViewModel 정의

3 participants