Skip to content

Conversation

@juri123123
Copy link
Collaborator

@juri123123 juri123123 commented Jan 8, 2025

이해한 MVI에 대해...

State -> Model

  • ViewModel에서 Published로 관리하는 변수 느낌인데 이제 상태(로딩, 에러)를 곁들인,

Intent

  • 사용자가 할 수 있는 행동의 모든 경우의 수

Store

  • ViewModel 느낌..
  • MVVM 처럼 State를 private(set)으로 설정해서 접근 제어하여 단방향 data flow 구현
  • dispatch 함수를 통해 intent를 switch로 관리하는데 여기서 실질적인 로직 작성

-> 사실 조금 더 분리된 MVVM이라고 느껴짐 .......



궁금한 점

  • effect를 선언하고 사용하지 않고 있어서 용도를 생각해보았는데 store의 dispatch에서 intent를 처리할 때 effect를 사용할 수 있을 것 같습니다만... 굳이 사용해야하는 이유를 모르겠어서 저도 쓰지 않았습니다!
  • 지훈샘의 intent는 onAppear, refresh, movieSelected, loadMovies가 있는데 dispatch에서 사용할 때 onAppear, refresh, loadMovies는 결국 같은 메소드를 호출하는데 나눈 이유가 뭘까요.. 명시적으로 사용자의 어떤 행동에 대해 1:1로 대응하기 위해서일까요 ?
  • 추가로 switch문으로 관리할 때 이런식으로 처리한 이유도 궁금합니다.. (case .onAppear, .refresh, .loadMovies: 에서 한번에 처리해도 됐지 않았나? 싶은 )
case .onAppear, .refresh:
   dispatch(.loadMovies)




actor

  • service를 class 대신 actor를 사용해서 data race를 방지했는데
  • 기존의 Response type을 반환하던 것을 View에서 직접 사용하는 type으로 반환하는 방향으로 바꿈



네비게이션에 MVI 적용 시도

  • NavigationPath를 적용하려면 NavigationStack의 path 매개변수에 바인딩값을 전달해야하는데 만약 path를 State로 private(set)하게 관리하게되면 사용이 불가능함
  • 따라서 path만 @published로 선언해서 관리하도록 함 -> 스푸니에 적용한다면 selectedTab정도만 State로 관리할 수 있을 듯
  • push, pop과 같은 메소드는 intent로 관리
.onTapGesture {
   navigationManager.dispatch(.push(.detail(code: movie.movieCd)))
}

@juri123123 juri123123 self-assigned this Jan 8, 2025
@juri123123 juri123123 requested a review from hooni0918 January 8, 2025 10:38
Copy link

@hooni0918 hooni0918 left a comment

Choose a reason for hiding this comment

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

LGTM


import Foundation

// 아마 selectedTab 정도는 여기서 관리할 수 있을 듯

Choose a reason for hiding this comment

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

👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

다시보니 TabView의 selection 매개변수도 binding 값을 전달해주어야해서 navigation에서는 intent 정도만 사용할 수 있을 것 같우네요~~~~ㅜ

Choose a reason for hiding this comment

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

하나를 알려주면 MVI를 통으로 이해하는 주리 미쳣다

Comment on lines +27 to +33
func dispatch(_ intent: NavigationIntent) {
switch intent {
case .push(let view):
path.append(view)
case .pop(let depth):
path.removeLast(depth)
}

Choose a reason for hiding this comment

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

LGTM👍


import Foundation

// viewModel에서 published로 선언하는 변수 느낌이라고 이해함

Choose a reason for hiding this comment

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

정확합니다. 정확하게는 구조체로 선언해서 한군데에서 관리하기에 UI의 가능한 모든 상태를 명확하게 표현하는 타입이라고 이해하시면 되는데 한군데서 모아둿다 생각해도 되긴함미다

var error: String? = nil
}

// 사용자가 할 수 있는 행동의 모든 경우의 수 (action)

Choose a reason for hiding this comment

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

Yes!

Comment on lines +11 to +13
// 변수 부분을 state로 관리하고
// 함수 부분을 intent로 관리하는 느낌이랄까 ? ? ?
@MainActor

Choose a reason for hiding this comment

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

정확합니다.
이미 구조체로 선언을 다 해줫기때문에 모든 로직이 예측 가능해지는 장점이 있는거죠

@hooni0918
Copy link

effect를 선언하고 사용하지 않고 있어서 용도를 생각해보았는데 store의 dispatch에서 intent를 처리할 때 effect를 사용할 수 있을 것 같습니다만... 굳이 사용해야하는 이유를 모르겠어서 저도 쓰지 않았습니다!

저도 코드를 작성할때는 이유를 잘 몰랏었는데 말그대로 부수 효과들을 직접 처리하지 않고 한번더 이펙트로 빼서 사용하면 됩니다

  • 네트워크 요청 (영화 데이터 가져오기)
  • 에러 처리
  • 로딩 상태 관리

등이 예시가 될 수 있겟죠!
스크린샷 2025-01-07 오후 3 08 01
우리 아키텍쳐 구조를 보듯이 API Call을 예시로 들면 되겟습니다!

지훈샘의 intent는 onAppear, refresh, movieSelected, loadMovies가 있는데 dispatch에서 사용할 때 onAppear, refresh, loadMovies는 결국 같은 메소드를 호출하는데 나눈 이유가 뭘까요.. 명시적으로 사용자의 어떤 행동에 대해 1:1로 대응하기 위해서일까요 ?
맞습니다.
단일책임 원칙을 지킬 수 있다. 라는것을 보여주기 위해 작성했습니다. 앱잼을 하면서 결국 화면이 처음 로드될 때만 필요한 작업, 수동으로 새로고침할떄 필요한 작업, 실제 데이터 로딩 시점 등등 전부 다를수 있기에 명확하게 각 단위를 분리한다. Srp( 단일책임원 )로써 하나의 intent는 하나의 작업을 한다는것을 알수있게 하기 위함이였습니다

추가로 switch문으로 관리할 때 이런식으로 처리한 이유도 궁금합니다.. (case .onAppear, .refresh, .loadMovies: 에서 한번에 처리해도 됐지 않았나? 싶은 )
case .onAppear, .refresh:
dispatch(.loadMovies)

이렇게 해도 좋지만 위에서 말한것과 같이 "정확하게 의도를 드러내야하기 때문에" 한번에 하나씩만 언급했습니다.
그리고 추후에 각 작업들이 분리될 요소가 있기 때문이죠. 이부분은 개인의 컨벤션의 영역으로 보아도 무방합니다.

actor
service를 class 대신 actor를 사용해서 data race를 방지했는데
기존의 Response type을 반환하던 것을 View에서 직접 사용하는 type으로 반환하는 방향으로 바꿈

맞습니다. 하지만 현재는 복잡한 UI를 그리는 내용도 없고 (메인 쓰레드에 다 떄려박아도 성능저하는 없는정도,,) 여러개의 api를 0.5초단위로 불러온다던지 등의 작업은 없기때문에 현재단계에서는 actor를 굳이 안쓰셔도 됩니다 합숙끝나고 actor 관련해서 한번더 미미나 해드릴게요

네비게이션에 MVI 적용 시도
NavigationPath를 적용하려면 NavigationStack의 path 매개변수에 바인딩값을 전달해야하는데 만약 path를 State로 private(set)하게 관리하게되면 사용이 불가능함
따라서 path만 @published로 선언해서 관리하도록 함 -> 스푸니에 적용한다면 selectedTab정도만 State로 관리할 수 있을 듯

완벽한 이해입니다 진짜 멋져!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants