Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] #376 TapTapUseCase 리팩토링 #377

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from

Conversation

YunKi-H
Copy link
Collaborator

@YunKi-H YunKi-H commented Jan 30, 2025

개요

TapTapUseCase를 테스트하기 좋은 코드로 리팩토링하는 목적입니다.
암튼 목적은 그럼

작업 내용

1. 책임분리

  • TapTapUseCase에서 TempoUseCase 직접 참조하지 않도록 변경
  • TapTapUseCase는 BPM을 계산하는 역할만 맡게 됨

2. ViewModel 수정

  • TapTapUseCase에서 BPM을 받아오고 TempoUseCase를 호출해 BPM을 변경하는 역할을 ViewModel에게 이전
  • MetronomeViewModel에서 쓸모없어진 estimateBpm 삭제

3. tap() 메서드 변경

  • 해당 시점 Date를 주입받는 방식으로 변경
  • Int?(BPM) 를 리턴하도록 수정 - 실패 가능성이 있음 - 실패 가능성이 있긴 한가?
  • 입력과 출력이 명확해져 테스트코드를 작성하기 수월할지도? 아닌가?
  • Int 대신 typealias 같은걸로 bpm이라는 명칭을 사용하도록 변경하는게 가독성이 좋지 않을까 고민

4. ReflectTempoInterface 삭제

  • TapTapUseCase가 사용하려고 만들어진 프로토콜인데 이제 안쓰니까 지웠습니다.
  • 이제 UseCase끼리 참조할 일이 없어졌습니다.

비고

작업 스크린샷

리뷰어에게 남길 말

한번 해봤습니다. 별로일수도 있을거같아요.

CheckList

  • PR의 Target이 올바르게 설정되어 있나요?
  • Assignee는 올바르게 할당되어있나요?
  • Label이 적절하게 설정되어 있나요?
  • 변경사항에 의문이 드는 곳은 없나요?

@YunKi-H YunKi-H added the enhancement New feature or request label Jan 30, 2025
@YunKi-H YunKi-H added this to the 2.4.0 milestone Jan 30, 2025
@YunKi-H YunKi-H self-assigned this Jan 30, 2025
@YunKi-H YunKi-H requested a review from l1004ga as a code owner January 30, 2025 23:38
@YunKi-H YunKi-H linked an issue Jan 30, 2025 that may be closed by this pull request
3 tasks
lastTappedDate = .now
self.timeStampList.append(lastTappedDate)
lastTappedDate = timeStamp
self.timeStampList.append(timeStamp)
Copy link
Collaborator

Choose a reason for hiding this comment

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

lastTappedDate가 Optional이여서 timeStamp로 직접 처리해주셨군요...
lastTappedDate말고 timeStampList자체를 Publisher로 쓰면 안될까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

전혀 생각해보지 않은 접근이라서 잠깐 생각해봤는데요

  1. timeStampList 배열을 Publisher 로 사용하면 배열을 비울때도 Publish 되는 문제가 생긴다.
  2. Published 프로퍼티래퍼를 사용하지 않으면 해결되나? -> 그럼 매번 배열 전체를 Publish하는건 낭비같다
  3. lastTappedDate를 왜 옵셔널로 바꿨더라? -> .now로 초기화되는게 마음에 걸렸다
  4. 근데 잠깐 lastTappedDate 시점을 저장할 필요가 있나? 어차피 Debounce로 6초 후 이벤트발생되는 구조인데?

의 연쇄적인 의문이 들었구요
lastTappedDate에 시점(Date) 정보를 저장할 필요 없이 Void 값을 반환하는 Publisher로 변경해도 아무 문제가 없을거같다는 생각이 들었습니다.

if action != .estimateBpm {
self.taptapUseCase.finishTapping()
}
self.taptapUseCase.finishTapping()
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 코드가 작동하고 있나요? 어떤 상황에서 작동하나요?
MetronomeView에서 View전체에 onTapGesture가 실행중인데 이 외 작동할 부분이 있나요?
.onTapGesture {
self.viewModel.effect(action: .disableEstimateBpm)
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이건 타이니와 같이 구현할때 생겼던 문제가 원인인데요
MetronomeView의 onTapGesture는 배경부분을 탭할때만 처리됩니다.
실제로 다른 액션이 발생할때는 해당 액션이 우선적으로 처리되고 MetronomeView의 VStack에 적용된 onTapGesture가 후순위인것 같더라구요
다른 액션은 Accent 변경이나 소박보기 on/off 같은 액션들이 있었습니다.

따라서 MetronomeViewModel의 effect 부분에서 빠르기찾기 기능이 아닌 다른 기능이 실행될때 Tapping을 끝내는 로직이 필요했고,
각 액션마다 코드를 추가하는것보단 상단에서 한번에 처리하는게 좋을것같아 해당 위치에 코드를 삽입하게 되었습니다.

@@ -99,7 +99,8 @@ extension MetronomeControlViewModel {
case let .roundBpm(currentBpm):
self.tempoUseCase.updateTempo(newBpm: currentBpm)
case .estimateBpm:
self.taptapUseCase.tap()
guard let bpm = self.taptapUseCase.tap() else { break }
self.tempoUseCase.updateTempo(newBpm: bpm)
Copy link
Collaborator

Choose a reason for hiding this comment

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

사실 TapTapUseCase도 TempoUseCase랑 통합되어야 했던건 아닐까요....?
사용자 행위를 기반으로 UseCase에서 계산된 데이터 다른 UseCase에 전달하기 위해서 ViewModel이 사용하는게 적절한건지 잘 모르겠어요....

Copy link
Collaborator Author

@YunKi-H YunKi-H Feb 1, 2025

Choose a reason for hiding this comment

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

TapTapUseCase 에서 Repository로 BPM을 보내는 방식도 생각해봤는데 여전히 tap() 메서드에 사이드이펙트가 발생한다는 문제가 있었습니다.
각 UseCase의 명확한 기능분리를 위해 TapTapUseCase는 bpm을 '계산만' 해주고
TempoUseCase는 bpm을 Repository에 반영하는 기능만 책임지는게 좀더 낫다고 생각했습니다.

TapTapUseCase의 bpm 계산을 TempoUseCase에서 담당하게 되면
bpm에 관한 모든걸 관장하는 UseCase가 되는데 책임이 좀 많아진다는 생각이 들었고
첫줄과 마찬가지로 tap() 메서드가 bpm을 Repository에 반영까지 해준다는걸 class 외부에선 예상하기 어려울것 같습니다

변경의 목적 - 테스트하기 용이하게 - 을 고려할 때
tap() 메서드를 입력, 출력이 명확하게 변경하는게 좋겠다는 생각이 들었고
tap(Date) -> BPM 형식의 메서드라면 출력된 BPM을 어딘가에서 TempoUseCase 또는 Repository로 전달해야 합니다.

  1. TapTapUseCase와 TempoUseCase를 모두 참조하는 상위 UseCase를 새로 만든다
  2. ViewModel에서 bpm을 받아 전달한다

두가지 방안이 떠올랐고, 상위 UseCase를 만드는것보단 ViewModel에서 처리하는게 낫다는 결론을 내렸습니다.

@YunKi-H
Copy link
Collaborator Author

YunKi-H commented Feb 1, 2025

기절하는바람에 댓글이 늦은점 죄숭하구요
이 PR은 굳이 반영해야만 한다기보다는 관련해서 의견을 나누고 싶었습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Refactor] TapTapUseCase
2 participants