Skip to content

[Fix] 토큰 재발급 role 추가#42

Merged
choijungp merged 6 commits intodevelopfrom
fix/reissue
Aug 8, 2025
Merged

[Fix] 토큰 재발급 role 추가#42
choijungp merged 6 commits intodevelopfrom
fix/reissue

Conversation

@choijungp
Copy link
Copy Markdown
Contributor

@choijungp choijungp commented Aug 8, 2025

🌁 Background

  • 토큰 재발급에서 Response로 받은 role을 통해 guest vs user를 구분합니다.
  • 만약 guest 라면 Home 진입이 아닌 Intro 화면을 통해 다시 로그인을 하도록 유도합니다.

👩‍💻 Contents

  • 토큰 재발급 role 분기 처리 추가
  • Home 화면에서 감정 구슬 버튼 클릭 시, 이미 감정 구슬이 등록되었다면 토스트 메시지 뷰 뜨도록 수정
  • 감정 구슬 등록 후 추천 루틴을 선택 후 루틴 등록 화면으로 이동할 시에 추천 루틴 Id 넘겨주도록 수정

Summary by CodeRabbit

  • 신규 기능

    • 추천 루틴 결과 화면에서 선택된 루틴의 ID를 비동기적으로 받아와 루틴 생성 화면으로 이동하는 기능이 추가되었습니다.
    • ViewModel에 선택된 루틴 ID를 발행하는 publisher가 추가되었습니다.
    • 홈 화면에서 감정 등록 버튼의 동작이 UIAction을 활용해 구조화되었습니다.
  • 버그 수정

    • 홈 화면에서 감정 등록 버튼의 토스트 메시지 표시 동작이 개선되었습니다.
  • 기타

    • 로그인 상태 판별이 기존의 단순 로그인 여부에서 사용자, 게스트, 미확인 상태로 세분화되어 처리됩니다.
    • 앱 버전이 0.0.3으로 업데이트되었습니다.

@choijungp choijungp requested a review from taipaise August 8, 2025 06:30
@choijungp choijungp self-assigned this Aug 8, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 8, 2025

Walkthrough

토큰 재발급 로직이 기존의 불리언 반환에서 상세한 사용자 상태(UserState?) 반환으로 변경되었습니다. 이에 따라 SceneDelegate와 관련 저장소, 프로토콜의 메서드 시그니처가 업데이트되었습니다. 홈 뷰의 버튼 액션 처리와 토스트 메시지 표시 방식이 개선되었고, 추천 루틴 결과 화면에서는 루틴 생성 뷰로의 네비게이션이 비동기적·리액티브하게 리팩토링되었습니다. 앱 버전이 0.0.3으로 상향되었습니다.

Changes

Cohort / File(s) Change Summary
토큰 재발급 및 사용자 상태 처리 개선
Projects/App/Sources/SceneDelegate.swift, Projects/DataSource/Sources/Repository/UserDataRepository.swift, Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift
토큰 재발급 함수가 Bool에서 UserState? 반환으로 변경. SceneDelegate에서 사용자 상태에 따라 루트 뷰 컨트롤러 분기 처리. 관련 프로토콜 및 저장소 메서드 시그니처 변경.
홈 뷰 버튼 액션 및 토스트 개선
Projects/Presentation/Sources/Home/View/HomeView.swift
토스트 메시지 크기 상수 추가, 버튼 액션 식별자 도입, 버튼 액션 등록 및 해제 방식 개선, 토스트 표시 로직 분리 및 순서 조정.
추천 루틴 결과 뷰 리팩토링
Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift, Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
루틴 생성 뷰 네비게이션을 비동기적으로 처리하도록 리팩토링. 뷰모델에 루틴 ID 퍼블리셔 및 관련 액션 추가. 뷰에서 해당 퍼블리셔 구독 및 네비게이션 트리거.
앱 버전 업데이트
SupportingFiles/Info.plist
앱 버전(ShortVersionString) 0.0.1 → 0.0.3으로 변경.

Sequence Diagram(s)

sequenceDiagram
    participant SceneDelegate
    participant UserDataRepository
    participant UserState
    participant RootViewController

    SceneDelegate->>UserDataRepository: reissueToken()
    UserDataRepository-->>SceneDelegate: UserState? (user/guest/nil)
    alt UserState == .user
        SceneDelegate->>RootViewController: Onboarding 여부 확인 후 TabBarView or OnboardingView 설정
    else UserState == .guest or nil
        SceneDelegate->>RootViewController: IntroNavigationController 설정
    end
Loading
sequenceDiagram
    participant ResultRecommendedRoutineView
    participant ResultRecommendedRoutineViewModel
    participant RoutineCreationView

    ResultRecommendedRoutineView->>ResultRecommendedRoutineViewModel: action(.fetchSelectedRoutineId)
    ResultRecommendedRoutineViewModel-->>ResultRecommendedRoutineView: selectedRoutineIdPublisher emits routineId
    ResultRecommendedRoutineView->>RoutineCreationView: goToRoutineCreationView(routineId)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20분

Poem

🐰
새싹처럼 토큰도 싱그럽게,
UserState로 분기 타고
홈 버튼엔 토스트가 뿅!
루틴 추천 따라가면
비동기로 길이 열려요.
0.0.3, 한 단계 더 성장한 오늘!
코드를 보며 깡총깡총 춤을 춰요.

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b9bd73 and 8bf4c2f.

📒 Files selected for processing (3)
  • Projects/Presentation/Sources/Home/View/HomeView.swift (5 hunks)
  • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift (2 hunks)
  • Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • Projects/Presentation/Sources/Home/View/HomeView.swift
  • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
  • Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/reissue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or @coderabbitai 요약 to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (2)
Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift (2)

363-365: fetchSelectedRoutinId 오탈자 확인 바랍니다

RoutinRoutine 오타로 보입니다.
메서드·enum 모두 동일 철자 유지가 필요합니다.


368-372: TabBar가 그대로 노출될 가능성 – hidesBottomBarWhenPushed 누락

다른 화면에서 RoutineCreationView 푸시 시 하단 탭바를 숨기고 있는데(예: HomeView), 여기서는 설정이 빠져 있습니다.
일관된 UX 를 위해 아래처럼 추가해 주세요.

 let routineCreationView = RoutineCreationView(viewModel: routineCreationViewModel, recommendRoutineId: routineId)
+routineCreationView.hidesBottomBarWhenPushed = true
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1213290 and 3b9bd73.

📒 Files selected for processing (7)
  • Projects/App/Sources/SceneDelegate.swift (2 hunks)
  • Projects/DataSource/Sources/Repository/UserDataRepository.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1 hunks)
  • Projects/Presentation/Sources/Home/View/HomeView.swift (4 hunks)
  • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift (2 hunks)
  • Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift (3 hunks)
  • SupportingFiles/Info.plist (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
📚 Learning: 2025-07-16T09:21:15.038Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/App/Sources/SceneDelegate.swift
  • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
📚 Learning: 2025-08-01T06:38:18.687Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.687Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.

Applied to files:

  • Projects/Presentation/Sources/Home/View/HomeView.swift
  • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
🔇 Additional comments (3)
SupportingFiles/Info.plist (1)

18-18: 마케팅 버전 0.0.3 갱신 LGTM

기능 변경 규모를 고려하면 버전 상향이 적절합니다. 0.0.1 → 0.0.3로 단조 증가도 충족합니다.

Projects/App/Sources/SceneDelegate.swift (1)

61-78: 로직 전환이 명확하며 문제 없습니다

UserState 스위치로 진입점을 분기한 구조가 직관적입니다.
추가 고려사항 없이 LGTM.

Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift (1)

29-29: 추가된 Output 프로퍼티 방향성 적절

선택된 루틴 ID를 퍼블리셔로 분리해 라우팅 파이프라인을 명확히 한 점 좋습니다.

Comment on lines +25 to 45
func reissueToken() async -> UserState? {
do {
let refreshToken = try tokenManager.loadToken(tokenType: .refreshToken)
let endpoint = AuthEndpoint.reissue(refreshToken: refreshToken)

guard let tokenResponse = try await networkService.request(endpoint: endpoint, type: TokenResponseDTO.self)
else { return false }
guard let loginResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
else { return nil }

try tokenManager.saveToken(token: tokenResponse.accessToken, tokenType: .accessToken)
try tokenManager.saveToken(token: tokenResponse.refreshToken, tokenType: .refreshToken)
try tokenManager.saveToken(token: loginResponse.accessToken, tokenType: .accessToken)
try tokenManager.saveToken(token: loginResponse.refreshToken, tokenType: .refreshToken)

BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(tokenResponse.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(tokenResponse.refreshToken)")
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(loginResponse.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(loginResponse.refreshToken)")
BitnagilLogger.log(logType: .debug, message: "User State: \(loginResponse.userState)")

return true
let userState = UserState(rawValue: loginResponse.userState)
return userState
} catch {
BitnagilLogger.log(logType: .error, message: "\(error.localizedDescription)")
return false
return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

오류 흐름과 반환값 의미를 분리하면 호출부가 더 안전해집니다

현재 실패 시 nil 을 반환하고 동일 타입으로 게스트·미로그인도 표현합니다.
reissueToken() async throws -> UserState? 로 바꾸고,

  • 성공 + 유저/게스트 ⇒ .user / .guest
  • 실패 ⇒ throw Error
    형태로 분리하면 SceneDelegate 등에서 switch 로직이 간결-명확해집니다.
🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/UserDataRepository.swift around lines
25 to 45, change the function signature of reissueToken() to async throws ->
UserState? to separate error handling from the return value. Modify the
implementation so that on failure it throws the error instead of returning nil,
and on success it returns .user or .guest accordingly. This will make the
calling code safer and clearer by handling errors via throws and user states via
the return value.

Comment on lines +36 to +38
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(loginResponse.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(loginResponse.refreshToken)")
BitnagilLogger.log(logType: .debug, message: "User State: \(loginResponse.userState)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

토큰 값을 그대로 로그에 출력하면 보안 위험이 큽니다

accessToken, refreshToken 을 디버그 로그에 남기면 콘솔 공유·크래시 리포트 업로드 시 유출될 수 있습니다.
운영 빌드에서는 마스킹하거나 완전히 제거해 주세요.

🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/UserDataRepository.swift around lines
36 to 38, the accessToken and refreshToken are logged directly, which poses a
security risk. Modify the logging to mask or omit these token values in
production builds by conditionally checking the build configuration and either
removing the tokens from the log messages or replacing them with masked versions
to prevent sensitive data exposure.


/// 토큰 재발급을 진행합니다.
func reissueToken() async -> Bool
func reissueToken() async -> UserState?
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

nil 반환으로는 오류·게스트 구분이 불명확합니다

UserState? 옵셔널만으로 네트워크 오류, 토큰 만료, 디코딩 실패 등과 게스트 상태를 모두 나타내기에는 표현력이 부족합니다.
throws 또는 Result<UserState, Error> 패턴으로 오류를 분리해 주면 호출부가 의도‧실패 원인을 명확히 처리할 수 있습니다.

🤖 Prompt for AI Agents
In Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift
at line 15, the method reissueToken() returns an optional UserState?, which does
not clearly distinguish between errors, guest states, or other failure reasons.
Change the method signature to either throw errors or return a Result<UserState,
Error> to separate success and failure cases explicitly, allowing callers to
handle different error types and guest states clearly.

Comment thread Projects/Presentation/Sources/Home/View/HomeView.swift
Comment on lines 22 to 25
case fetchSelectedRoutinId
case selectRecommendedRoutine(routine: RecommendedRoutine)
case registerRecommendedRoutine
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

네이밍 오탈자: fetchSelectedRoutinId → fetchSelectedRoutineId

공개 API에 가까운 enum 케이스이므로 지금 바로 정정하는 것이 좋습니다. 퍼블리셔 명(selectedRoutineIdPublisher)과도 일관성이 맞습니다.

적용 diff:

 enum Input {
   case fetchResultRecommendedRoutines
-  case fetchSelectedRoutinId
+  case fetchSelectedRoutineId
   case selectRecommendedRoutine(routine: RecommendedRoutine)
   case registerRecommendedRoutine
 }

스위치 분기도 함께 수정 필요:

-    case .fetchSelectedRoutinId:
+    case .fetchSelectedRoutineId:

레포 전체에서 기존 오탈자 참조가 없는지 확인:


🏁 Script executed:

#!/bin/bash
# 레포 내 오탈자 사용처 점검
rg -n "fetchSelectedRoutinId|selectedRoutinIdSubject"

Length of output: 1041


오탈자 정정: fetchSelectedRoutinId → fetchSelectedRoutineId

공개 API 성격의 enum 케이스 및 관련 퍼블리셔 이름에 오탈자가 발견되었습니다. 아래 위치를 모두 수정해 주세요:

• Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift

  • Input enum 케이스 (22행)
  • subject 선언 (37행)
  • publisher 초기화 (48행)
  • switch-case (60행)
  • send 호출 (62행)
    • Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
  • action 호출 (363행)

적용 예시 diff:

--- a/Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
+++ b/Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
@@ -22,7 +22,7 @@ enum Input {
-        case fetchSelectedRoutinId
+        case fetchSelectedRoutineId
@@ -37,7 +37,7 @@ final class ResultRecommendedRoutineViewModel {
-    private let selectedRoutinIdSubject = PassthroughSubject<Int?, Never>()
+    private let selectedRoutineIdSubject = PassthroughSubject<Int?, Never>()
@@ -48,7 +48,7 @@ final class ResultRecommendedRoutineViewModel {
-        selectedRoutineIdPublisher: selectedRoutinIdSubject.eraseToAnyPublisher(),
+        selectedRoutineIdPublisher: selectedRoutineIdSubject.eraseToAnyPublisher(),
@@ -60,7 +60,7 @@ final class ResultRecommendedRoutineViewModel {
-        case .fetchSelectedRoutinId:
+        case .fetchSelectedRoutineId:
@@ -62,7 +62,7 @@ final class ResultRecommendedRoutineViewModel {
-            selectedRoutinIdSubject.send(routineId)
+            selectedRoutineIdSubject.send(routineId)
--- a/Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
+++ b/Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
@@ -363,7 +363,7 @@
-            viewModel.action(input: .fetchSelectedRoutinId)
+            viewModel.action(input: .fetchSelectedRoutineId)

변경 완료 후, 아래 스크립트로 잔여 오탈자 참조가 없는지 한 번 더 확인해 주세요:

rg -n "fetchSelectedRoutinId|selectedRoutinIdSubject"
🤖 Prompt for AI Agents
In
Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
at lines 22, 37, 48, 60, and 62, and in
Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
at line 363, correct the typo "fetchSelectedRoutinId" to
"fetchSelectedRoutineId" in the enum case, subject declaration, publisher
initialization, switch-case, send call, and action call respectively. After
making these changes, run the provided grep command to ensure no remaining
references to the incorrect spelling exist.

Comment on lines +60 to +63
case .fetchSelectedRoutinId:
let routineId = selectedRecommendedRoutineSubject.value.first?.id
selectedRoutinIdSubject.send(routineId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

여러 개 선택 시 임의 first 사용 위험 — 단일 선택 보장/가드 추가 권장

Set.first는 순서가 없어 임의의 요소가 선택됩니다. .emotion 플로우에서는 단일 선택을 유도하지만 코드 레벨에서 보장하지 않으면 예기치 않은 ID가 방출될 수 있습니다. “정확히 1개 선택”일 때만 ID를 방출하고, 그렇지 않으면 nil을 보내도록 가드하세요. 또한 위 네이밍 수정에 맞춰 Subject 이름도 정정합니다.

적용 diff:

-    case .fetchSelectedRoutinId:
-        let routineId = selectedRecommendedRoutineSubject.value.first?.id
-        selectedRoutinIdSubject.send(routineId)
+    case .fetchSelectedRoutineId:
+        let selected = selectedRecommendedRoutineSubject.value
+        let routineId = (selected.count == 1) ? selected.first?.id : nil
+        selectedRoutineIdSubject.send(routineId)

대안: .emotion일 때만 ID를 방출하도록 뷰모델 타입을 가드

case .fetchSelectedRoutineId:
    switch viewModelType {
    case .emotion?:
        let selected = selectedRecommendedRoutineSubject.value
        let routineId = (selected.count == 1) ? selected.first?.id : nil
        selectedRoutineIdSubject.send(routineId)
    default:
        selectedRoutineIdSubject.send(nil)
    }

뷰 측에서 이 액션이 실제로 .emotion 케이스에서만 트리거되는지 함께 확인 부탁드립니다.


🏁 Script executed:

#!/usr/bin/env bash
set -e

echo "1. fetchSelectedRoutinId 위치 검색:"
rg -n "fetchSelectedRoutinId"

echo
echo "2. selectedRoutinIdSubject 위치 검색:"
rg -n "selectedRoutinIdSubject"

echo
echo "3. selectedRecommendedRoutineSubject 위치 검색:"
rg -n "selectedRecommendedRoutineSubject"

echo
echo "4. viewModelType 선언부 및 사용부 검색:"
rg -n "viewModelType"

Length of output: 4359


Set.first 사용 리스크 및 fetchSelectedRoutinId/RoutinIdSubject 명명 오타 수정 요청

UI 상은 단일 선택을 보장해도, Set.first는 순서를 보장하지 않아 여러 항목이 선택된 상태에서 예기치 않은 ID가 발행될 수 있습니다. 또한 enum 케이스와 Subject 이름에 오타(RoutinRoutine)가 있어 관련된 모든 사용 지점을 함께 수정해야 합니다.

수정 필요 위치:

  • ViewModel (ResultRecommendedRoutineViewModel.swift)
    • enum 입력 타입 케이스 변경:
      • line 22: case fetchSelectedRoutinIdcase fetchSelectedRoutineId
    • Subject 프로퍼티 및 퍼블리셔 이름 변경:
      • line 37: private let selectedRoutinIdSubjectselectedRoutineIdSubject
      • line 48: selectedRoutineIdPublisher: selectedRoutinIdSubject.eraseToAnyPublisher()
    • 액션 처리부 가드 추가 및 이름 변경:
      • line 60:
      • .fetchSelectedRoutinId:.fetchSelectedRoutineId:
      • first?.id 대신 let selected = …; let routineId = (selected.count == 1) ? selected.first?.id : nil
      • selectedRoutinIdSubject.send(...)selectedRoutineIdSubject.send(...)
  • View (ResultRecommendedRoutineView.swift)
    • line 363: viewModel.action(input: .fetchSelectedRoutinId).fetchSelectedRoutineId

예시 diff:

--- a/Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
+++ b/Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
@@ -22,7 +22,7 @@ enum ResultRecommendedRoutineInput {
-    case fetchSelectedRoutinId
+    case fetchSelectedRoutineId
 }

 final class ResultRecommendedRoutineViewModel {
@@ -37,7 +37,7 @@ final class ResultRecommendedRoutineViewModel {
-    private let selectedRoutinIdSubject = PassthroughSubject<Int?, Never>()
+    private let selectedRoutineIdSubject = PassthroughSubject<Int?, Never>()

     var selectedRoutineIdPublisher: AnyPublisher<Int?, Never> {
-        selectedRoutinIdSubject.eraseToAnyPublisher()
+        selectedRoutineIdSubject.eraseToAnyPublisher()
     }
@@ -60,8 +60,10 @@ final class ResultRecommendedRoutineViewModel {
-    case .fetchSelectedRoutinId:
-        let routineId = selectedRecommendedRoutineSubject.value.first?.id
-        selectedRoutinIdSubject.send(routineId)
+    case .fetchSelectedRoutineId:
+        let selected = selectedRecommendedRoutineSubject.value
+        let routineId = (selected.count == 1) ? selected.first?.id : nil
+        selectedRoutineIdSubject.send(routineId)
--- a/Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
+++ b/Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift
@@ -363 +363 @@
-        viewModel.action(input: .fetchSelectedRoutinId)
+        viewModel.action(input: .fetchSelectedRoutineId)

추가로, 이 액션이 실제로 .emotion 케이스에서만 호출되는지 한 번 더 검증 부탁드립니다.

🤖 Prompt for AI Agents
In
Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift
around lines 22, 37, 48, and 60, rename the enum case from fetchSelectedRoutinId
to fetchSelectedRoutineId and rename the Subject and Publisher properties from
selectedRoutinIdSubject to selectedRoutineIdSubject and
selectedRoutineIdPublisher respectively. At line 60, replace the use of
Set.first with a guard that checks if exactly one item is selected, then send
its id; otherwise send nil. Also update all related references in the View file
at line 363 to use the corrected enum case. Verify that this action is only
called from the .emotion case as suggested.

<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.1</string>
<string>0.0.3</string>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Info.plist에 버전 하드코딩 대신 MARKETING_VERSION/CURRENT_PROJECT_VERSION 사용 권장

  • 여러 타깃/플랫폼 확장 시 버전 불일치가 생기지 않도록 CFBundleShortVersionString은 $(MARKETING_VERSION)로, CFBundleVersion은 $(CURRENT_PROJECT_VERSION)로 관리하는 것을 권장합니다.
  • TestFlight 업로드 시 CFBundleVersion은 단조 증가해야 하므로, 빌드 넘버 증가 전략(CI에서 자동 증가 등)을 함께 설정하세요.
  • 주의: 프로젝트 Build Settings에 MARKETING_VERSION/CURRENT_PROJECT_VERSION가 정의되어 있어야 합니다.

변경 제안(해당 라인 교체):

-	<string>0.0.3</string>
+	<string>$(MARKETING_VERSION)</string>

CFBundleVersion도 함께 변수화 권장(파일 내 다른 위치, 참고용 XML):

<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>

버전 일관성 검증 스크립트:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Info.plist 버전 값 점검 =="
fd -a 'Info.plist' | while read -r f; do
  echo "---- $f"
  rg -n -A 1 '<key>CFBundleShortVersionString</key>|<key>CFBundleVersion</key>' "$f" || true
done

Length of output: 484


Info.plist 버전 하드코딩 제거 및 변수화 적용 필요

  • SupportingFiles/Info.plist 내에 버전이 하드코딩(0.0.3, 1)되어 있습니다.
    • CFBundleShortVersionString$(MARKETING_VERSION)으로,
    • CFBundleVersion$(CURRENT_PROJECT_VERSION)으로 관리하세요.
  • TestFlight 업로드를 위해 빌드 번호(CFBundleVersion)는 단조 증가 설정(CI 자동 증가 등)도 함께 구성해야 합니다.
  • 프로젝트 Build Settings에 MARKETING_VERSIONCURRENT_PROJECT_VERSION이 정의되어 있는지 확인하세요.

변경 제안(diff):

--- a/SupportingFiles/Info.plist
+++ b/SupportingFiles/Info.plist
@@ -17,4 +17,4 @@
 	<key>CFBundleShortVersionString</key>
-	<string>0.0.3</string>
+	<string>$(MARKETING_VERSION)</string>
@@ -30,4 +30,4 @@
 	<key>CFBundleVersion</key>
-	<string>1</string>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<string>0.0.3</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
🤖 Prompt for AI Agents
In SupportingFiles/Info.plist at line 18, the version string is hardcoded as
"0.0.3". Replace this hardcoded version with the variable reference
$(MARKETING_VERSION) for CFBundleShortVersionString and
$(CURRENT_PROJECT_VERSION) for CFBundleVersion to enable dynamic version
management. Also, ensure that in the project Build Settings, MARKETING_VERSION
and CURRENT_PROJECT_VERSION are properly defined and that the build number
(CURRENT_PROJECT_VERSION) is configured to increment automatically for
TestFlight uploads.

@choijungp choijungp merged commit 1891a62 into develop Aug 8, 2025
2 checks passed
@choijungp choijungp deleted the fix/reissue branch August 8, 2025 06:53
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.

1 participant