Skip to content

[#36] feat: Secret 목록 화면 구현 - #46

Merged
yeseonglee merged 10 commits into
developfrom
feature/#36
Jul 23, 2026
Merged

[#36] feat: Secret 목록 화면 구현#46
yeseonglee merged 10 commits into
developfrom
feature/#36

Conversation

@yeseonglee

@yeseonglee yeseonglee commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

✨ What's this PR?

📌 관련 이슈


🧶 주요 변경 내용

  • Secret 목록 화면 (All/Star/Expired/Deleted/Project 탭, 검색/정렬/우클릭 메뉴)
  • 프로젝트 선택·생성 다이얼로그
  • DVFloatingPanel(화살표 없는 드롭다운 컴포넌트) 추가
  • 검색/정렬/만료 필터링 로직 보강

🧪 테스트 / 검증 내역

  • 테스트 23개 통과
  • Xcode Canvas Preview 확인
  • 실제 앱 실행 검증 — Composition Root 없어서 아직 불가

💬 기타 공유 사항

  • Composition Root 미완성이라 실제 데이터 연동은 후속 PR 필요

Summary by CodeRabbit

  • 새로운 기능
    • 시크릿 목록에 검색/정렬/만료 구분, 프로젝트 추가 및 삭제·복구·영구 삭제 흐름을 추가했습니다.
    • 프로젝트에 시크릿 연결 및 새 프로젝트 생성 화면을 제공합니다.
    • 날짜 포맷터를 제공하고, 화면 외부 클릭으로 닫히는 플로팅 패널을 추가했습니다.
    • 로딩 상태 표현(Idle/Loading/Loaded/Failed)을 도입했습니다.
  • 개선 사항
    • 정렬 버튼 표시가 필요한 경우에만 렌더링되도록 변경했습니다.
    • 오류 매핑 유틸리티의 외부 접근성을 공개 API로 조정했습니다.
    • 만료 정렬/검색 대상에 createdAt/updatedAt 반영 및 검색 결과 정렬을 보강했습니다.
  • 품질
    • 관련 기능(프로젝트 생성/시크릿 목록/프로젝트 추가) 테스트를 추가했습니다.

@yeseonglee yeseonglee self-assigned this Jul 21, 2026
@yeseonglee yeseonglee added the ✨ Feature 새로운 기능 개발 label Jul 21, 2026
@yeseonglee yeseonglee linked an issue Jul 21, 2026 that may be closed by this pull request
3 tasks
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yeseonglee, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 25b72520-effd-4af3-aeb2-ef81532f4747

📥 Commits

Reviewing files that changed from the base of the PR and between 596c1ab and ec870c8.

📒 Files selected for processing (12)
  • .gitignore
  • Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift
  • Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
  • Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectFeature.swift
  • Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectView.swift
  • Projects/DVPresentation/Sources/Features/CreateProject/CreateProjectFeature.swift
  • Projects/DVPresentation/Sources/Features/CreateProject/CreateProjectView.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
  • Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift
  • Projects/DVPresentation/Tests/CreateProject/CreateProjectFeatureTests.swift
  • Projects/DVPresentation/Tests/SecretList/SecretListFeatureTests.swift

Walkthrough

Secret 목록의 검색·정렬·만료 필터와 날짜 검색을 구현하고, 목록 화면에서 프로젝트 생성 및 Secret 연결 흐름을 추가했습니다. macOS floating 패널, 로딩 상태, 공개 오류 매핑 API와 TCA 테스트도 포함됩니다.

Changes

Secret 목록 기능

Layer / File(s) Summary
조회 필터와 날짜 검색 기반
Projects/DVCore/Sources/Formatting/SecretDateFormatter.swift, Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift
날짜를 yyyy.MM.dd 문자열로 변환하고, Secret 조회에 만료 필터·이름 정렬·생성일/수정일 검색을 적용합니다.
Floating 패널과 목록 UI 기반
Projects/DVDesign/Sources/Components/DVFloatingPanel.swift, Projects/DVDesign/Sources/Components/DVTitleBar.swift, Projects/DVDesign/Sources/Components/DVVaultContainer.swift
앵커 아래에 표시되는 NSPanel 기반 floating 패널을 추가하고, 정렬 버튼 조건부 렌더링과 디자인 컬러를 적용합니다.
프로젝트 생성 및 Secret 연결
Projects/DVPresentation/Sources/Features/CreateProject/*, Projects/DVPresentation/Sources/Features/AddToProject/*, Projects/DVPresentation/Sources/Support/LoadingState.swift, Projects/DVPresentation/Project.swift, Projects/DVDomain/Sources/UseCase/Error/*
프로젝트 생성, 프로젝트 선택, Secret 연결을 TCA 리듀서와 SwiftUI 화면으로 구현하고 관련 상태·오류 API·테스트 구성을 추가합니다.
Secret 목록 상태와 비동기 조회
Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift, Projects/DVPresentation/Tests/SecretList/SecretListFeatureTests.swift
검색 디바운스, 정렬 및 만료 컬렉션 쿼리, 조회 취소, mutation 후 재조회, destination 전환을 구현하고 테스트합니다.
Secret 목록 화면과 만료 섹션
Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
검색·정렬 메뉴, 선택 상태, 만료 구간 섹션, 컨텍스트 메뉴, 프로젝트 추가 시트와 프리뷰를 구성합니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SecretListView
  participant SecretListFeature
  participant secretClient
  User->>SecretListView: 검색어 또는 정렬 변경
  SecretListView->>SecretListFeature: didChangeSearchText / didSelectSort
  SecretListFeature->>secretClient: fetchByQuery(query)
  secretClient-->>SecretListFeature: Secret 목록 또는 오류
  SecretListFeature-->>SecretListView: loaded / failed 상태
Loading

Possibly related PRs

Suggested labels: ✅ Test, 🎨 Design

Suggested reviewers: dlguszoo

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 이슈 #36 범위를 넘는 프로젝트 생성/추가 플로우, FloatingPanel, LoadingState, 공개 API 변경 등 부가 변경이 포함됩니다. 이슈 #36만 다룬다면 리스트뷰·검색·소팅에 한정해 분리하고, 프로젝트 추가/생성 및 공용 API 변경은 별도 PR로 나누세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 Secret 목록 화면 구현이라는 핵심 변경을 간결하게 담고 있어 변경 사항과 잘 맞습니다.
Linked Issues check ✅ Passed 직접 이슈 #36의 리스트뷰 레이아웃, 검색, 소팅 요구사항을 Secret 리스트 화면과 관련 기능으로 충족합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#36

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift (1)

256-287: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

SortMenuRowextension SecretListView 밖에 선언돼 있어요.

독립 @State(isHovered)를 가진 하위뷰인데 extension 블록 바깥 최상위에 있습니다. 최소한 extension SecretListView { } 안으로 옮기는 게 스타일 가이드에 맞고, "재사용 필요해지면 DVDesign으로 승격"할 때도 경계가 더 명확해집니다.

As per path instructions, "하위뷰가 extension 밖에 선언되어 있으면 이동을 제안하세요" 그리고 "독립적인 State/로직을 갖거나 ... 별도 View 파일 분리를 제안하세요".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift`
around lines 256 - 287, Move the SortMenuRow nested view into the extension
SecretListView block, preserving its existing `@State`, layout, styling, and
action behavior; do not otherwise refactor the component.

Source: Path instructions

Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift (2)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

import 순서가 스타일 가이드와 다릅니다.

내장 프레임워크(Foundation)를 맨 위에 두고 빈 줄로 서드파티(DVCore, DVDomain)를 구분해야 합니다. 지금은 Foundation이 맨 아래에 있고 구분 줄도 없네요.

🍎 제안
-import DVCore
-import DVDomain
-import Foundation
+import Foundation
+
+import DVCore
+import DVDomain

As per path instructions, "모듈 임포트가 알파벳 순으로 정렬되어 있는지 확인하세요. (내장 프레임워크 먼저, 빈 줄로 서드파티 구분)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift`
around lines 3 - 5, Reorder the imports in InMemorySecretQueryFilter so
Foundation appears first, followed by a blank line and the alphabetically
ordered DVCore and DVDomain imports.

Source: Path instructions


10-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

matchesExpiryDate.now를 직접 참조합니다 — 다른 UseCase의 날짜 주입 패턴과 어긋나요.

DeleteSecretUseCaseImpl 등은 dateProvider()로 현재 시각을 주입받는데, 여기만 Date.now를 하드코딩해서 경계값(만료 시각이 정확히 지금인 케이스) 테스트가 불가능합니다. @Dependency(\.date) 같은 걸 주입받아 일관성을 맞추는 게 좋겠습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift`
around lines 10 - 29, Update SecretQueryFilter’s matchesExpiry to obtain the
current time through the project’s existing injected date dependency, matching
the dateProvider() pattern used by other use cases, instead of directly
referencing Date.now. Preserve the current collection filtering and expiry
comparison behavior while making the time source controllable for boundary-value
tests.
Projects/DVDesign/Sources/Components/DVFloatingPanel.swift (1)

20-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

public extension View 패턴 대신 선언별 접근제어 명시 권장

public extension View { func floatingPanel... } 형태 대신, extension은 기본(internal)으로 두고 floatingPanel 함수에만 public을 명시하는 편이 낫습니다. 다른 헬퍼가 추가될 때 의도치 않게 전부 public 노출되는 걸 방지합니다.

♻️ 제안
-public extension View {
-    func floatingPanel<PanelContent: View>(
+extension View {
+    public func floatingPanel<PanelContent: View>(
         isPresented: Binding<Bool>,
         `@ViewBuilder` content: `@escaping` () -> PanelContent
     ) -> some View {

As per path instructions, "public extension 패턴 대신 각 선언에 직접 접근 제어가 명시되어 있는지 확인하세요."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVDesign/Sources/Components/DVFloatingPanel.swift` around lines 20 -
29, Update the View extension containing floatingPanel to use the default
internal extension access level, and mark only the floatingPanel method as
public. Preserve its existing signature and behavior while preventing future
declarations in the extension from being implicitly public.

Source: Path instructions

Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift (1)

1-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

실패(에러) 경로 테스트 커버리지 부재

두 테스트 파일 모두 성공 경로만 검증하고, 각 리듀서의 실패 분기(.projectsResponse(.failure), .linkResponse(.failure), .createResponse(.failure))에 대한 테스트가 빠져 있습니다. 에러 매핑(ProjectUseCaseError/SecretUseCaseError)이 상태에 올바르게 반영되는지 확인하는 테스트를 추가하면 좋겠습니다.

  • Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift#L1-L127: secretClient.fetchProjects/linkProject가 에러를 던질 때 .projectsResponse(.failure), .linkResponse(.failure) 처리 테스트 추가.
  • Projects/DVPresentation/Tests/CreateProject/CreateProjectFeatureTests.swift#L1-L65: secretClient.createProject가 에러를 던질 때 .createResponse(.failure)isCreating 복귀 테스트 추가.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift`
around lines 1 - 127, 실패 경로 테스트가 누락되어 있으므로
Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift의
AddToProjectFeature 테스트에 fetchProjects 실패 시 .projectsResponse(.failure) 상태 반영과
linkProject 실패 시 .linkResponse(.failure) 처리 테스트를 추가하세요.
Projects/DVPresentation/Tests/CreateProject/CreateProjectFeatureTests.swift의
CreateProjectFeature 테스트에는 createProject 실패 시 .createResponse(.failure)를 처리하고
isCreating이 원래 상태로 복귀하는지 검증하는 테스트를 추가하세요.
Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectFeature.swift (1)

106-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

LoadingStatevalue 프로퍼티를 추가하여 코드를 간결하게 정리해 보세요.

만약 앞서 리뷰에서 제안해 드린 value 프로퍼티를 LoadingState에 추가하신다면, IIFE(즉시 실행 함수) 블록을 제거하고 닐 코얼레싱(??) 연산자를 이용해 코드를 훨씬 짧고 직관적으로 다듬을 수 있습니다.

♻️ 제안하는 코드 개선안
-        var projects: IdentifiedArrayOf<Project> = {
-          if case let .loaded(projects) = state.projectsState { return projects }
-          return []
-        }()
+        var projects = state.projectsState.value ?? []
         projects.append(project)
         state.projectsState = .loaded(projects)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectFeature.swift`
around lines 106 - 111, LoadingState에 현재 값을 반환하는 value 프로퍼티를 추가한 뒤,
AddToProjectFeature의 projects 초기화 IIFE와 case 분기를 제거하고 state.projectsState.value
?? []를 사용해 기존 loaded 값 또는 빈 배열을 가져오도록 단순화하세요.
Projects/DVPresentation/Sources/Support/LoadingState.swift (1)

7-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

연관 값 추출을 위한 편의 프로퍼티 추가를 고려해 보세요.

현재 구조도 아주 좋습니다만, .loaded 상태의 값을 손쉽게 꺼내 쓸 수 있도록 value 프로퍼티를 추가해 두면 사용하는 곳에서 패턴 매칭 없이 훨씬 깔끔하게 다룰 수 있어 추천합니다.

💡 제안하는 익스텐션 구현
 public enum LoadingState<Value: Equatable, Failure: Equatable>: Equatable {
   case idle
   case loading
   case loaded(Value)
   case failed(Failure)
 }
+
+public extension LoadingState {
+  var value: Value? {
+    guard case let .loaded(val) = self else { return nil }
+    return val
+  }
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVPresentation/Sources/Support/LoadingState.swift` around lines 7 -
12, LoadingState에 loaded 연관 값을 편리하게 추출할 수 있는 value 프로퍼티를 추가하세요. .loaded(Value)인
경우 해당 값을 반환하고, idle·loading·failed 상태에서는 기존 API와 일관된 방식으로 부재를 표현하도록 구현하며,
LoadingState 선언의 Equatable 동작은 유지하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift`:
- Around line 152-156: Update the mutation failure handling in
SecretListFeature’s mutationResponse reducer to use the mapped
SecretUseCaseError and present it through the feature’s `@Presents` alert
destination instead of returning .none. Ensure failures from didTapDelete,
didTapRecover, and didTapDeleteForever produce an appropriate user-visible alert
while preserving the existing success refresh behavior, and add coverage in
SecretListFeatureTests.swift for this failure path.

---

Nitpick comments:
In
`@Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift`:
- Around line 3-5: Reorder the imports in InMemorySecretQueryFilter so
Foundation appears first, followed by a blank line and the alphabetically
ordered DVCore and DVDomain imports.
- Around line 10-29: Update SecretQueryFilter’s matchesExpiry to obtain the
current time through the project’s existing injected date dependency, matching
the dateProvider() pattern used by other use cases, instead of directly
referencing Date.now. Preserve the current collection filtering and expiry
comparison behavior while making the time source controllable for boundary-value
tests.

In `@Projects/DVDesign/Sources/Components/DVFloatingPanel.swift`:
- Around line 20-29: Update the View extension containing floatingPanel to use
the default internal extension access level, and mark only the floatingPanel
method as public. Preserve its existing signature and behavior while preventing
future declarations in the extension from being implicitly public.

In
`@Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectFeature.swift`:
- Around line 106-111: LoadingState에 현재 값을 반환하는 value 프로퍼티를 추가한 뒤,
AddToProjectFeature의 projects 초기화 IIFE와 case 분기를 제거하고 state.projectsState.value
?? []를 사용해 기존 loaded 값 또는 빈 배열을 가져오도록 단순화하세요.

In `@Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift`:
- Around line 256-287: Move the SortMenuRow nested view into the extension
SecretListView block, preserving its existing `@State`, layout, styling, and
action behavior; do not otherwise refactor the component.

In `@Projects/DVPresentation/Sources/Support/LoadingState.swift`:
- Around line 7-12: LoadingState에 loaded 연관 값을 편리하게 추출할 수 있는 value 프로퍼티를 추가하세요.
.loaded(Value)인 경우 해당 값을 반환하고, idle·loading·failed 상태에서는 기존 API와 일관된 방식으로 부재를
표현하도록 구현하며, LoadingState 선언의 Equatable 동작은 유지하세요.

In `@Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift`:
- Around line 1-127: 실패 경로 테스트가 누락되어 있으므로
Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift의
AddToProjectFeature 테스트에 fetchProjects 실패 시 .projectsResponse(.failure) 상태 반영과
linkProject 실패 시 .linkResponse(.failure) 처리 테스트를 추가하세요.
Projects/DVPresentation/Tests/CreateProject/CreateProjectFeatureTests.swift의
CreateProjectFeature 테스트에는 createProject 실패 시 .createResponse(.failure)를 처리하고
isCreating이 원래 상태로 복귀하는지 검증하는 테스트를 추가하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 83ed6e10-503f-4250-be5f-2e44954accbf

📥 Commits

Reviewing files that changed from the base of the PR and between 08faf66 and bc05efa.

📒 Files selected for processing (18)
  • Projects/DVCore/Sources/Formatting/SecretDateFormatter.swift
  • Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift
  • Projects/DVDesign/Sources/Components/DVFloatingPanel.swift
  • Projects/DVDesign/Sources/Components/DVTitleBar.swift
  • Projects/DVDesign/Sources/Components/DVVaultContainer.swift
  • Projects/DVDomain/Sources/UseCase/Error/ProjectUseCaseError.swift
  • Projects/DVDomain/Sources/UseCase/Error/SecretUseCaseError.swift
  • Projects/DVPresentation/Project.swift
  • Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectFeature.swift
  • Projects/DVPresentation/Sources/Features/AddToProject/AddToProjectView.swift
  • Projects/DVPresentation/Sources/Features/CreateProject/CreateProjectFeature.swift
  • Projects/DVPresentation/Sources/Features/CreateProject/CreateProjectView.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
  • Projects/DVPresentation/Sources/Support/LoadingState.swift
  • Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift
  • Projects/DVPresentation/Tests/CreateProject/CreateProjectFeatureTests.swift
  • Projects/DVPresentation/Tests/SecretList/SecretListFeatureTests.swift

Comment on lines +152 to +156
case .mutationResponse(.success):
return fetchSecretsEffect(query: state.query, debounced: false)

case .mutationResponse(.failure):
return .none

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

mutation 실패가 완전히 무시됩니다.

didTapDelete/didTapRecover/didTapDeleteForever가 실패해도 .mutationResponse(.failure)state 변경 없이 .none을 반환해요. 사용자는 삭제/복구가 성공한 것처럼 보이지만 실제로는 아무 일도 일어나지 않은 상태가 됩니다. SecretUseCaseError.map이 이미 세분화된 에러를 주는데(context snippet 참고) 전혀 활용되지 않고 있어요. @Presents var alert destination을 추가해서 실패 시 사용자에게 알려주는 게 필요합니다. SecretListFeatureTests.swift에도 이 실패 경로에 대한 테스트가 하나도 없다는 점도 같은 근본 원인입니다.

🚨 제안 방향
     case .mutationResponse(.failure):
-      return .none
+    case .mutationResponse(.failure(let error)):
+      state.destination = .alert(AlertState { TextState(error.localizedDescription) })
+      return .none
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift`
around lines 152 - 156, Update the mutation failure handling in
SecretListFeature’s mutationResponse reducer to use the mapped
SecretUseCaseError and present it through the feature’s `@Presents` alert
destination instead of returning .none. Ensure failures from didTapDelete,
didTapRecover, and didTapDeleteForever produce an appropriate user-visible alert
while preserving the existing success refresh behavior, and add coverage in
SecretListFeatureTests.swift for this failure path.

@doyeonk429 doyeonk429 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

claude-code 리뷰 항목 중에서 현재 pr에서 개선하면 좋을 것 위주로 comment 남김!

}

func showPanel(with content: PanelContent) {
guard panel == nil, let anchorView, let window = anchorView.window else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. DVFloatingPanel 콘텐츠 갱신 안 됨
  • DVFloatingPanel.swift:73 guard panel == nil 때문에 패널이 열려 있는 동안 SwiftUI 상태 변화가 반영 안 됨.
  • 재현: AddToProject에서 프로젝트 메뉴 열어놓은 상태로 "New Project…"로 새 프로젝트 만들면, 목록에 즉시 안 나타남 (stale). 이 PR의 핵심 플로우가 깨짐.
  • updateNSView에서 열려 있는 경우 hostingView.rootView = content 로 갱신 필요.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. DVFloatingPanel 초기 isPresented=true 상태에서 안 뜸
  • DVFloatingPanel.swift:74 anchorView.window == nil일 때 조용히 return. 첫 렌더에 attach 되기 전이면 그대로 못 뜸.
  • Sheet에서 열자마자 flag가 true인 시나리오나 화면 재진입 시 잠재 버그.

Comment on lines +324 to +337
#Preview("Project - CheerLot에 속한 Secret만") {
SecretListView(
store: Store(
initialState: SecretListFeature.State(
collection: .project(id: [Project].preview[0].id),
projectName: [Project].preview[0].name
)
) {
SecretListFeature()
} withDependencies: {
$0.secretClient = .previewValue
}
)
.frame(width: 300, height: 500)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. [Project].preview 미정의 → Xcode Preview 빌드 실패
  • SecretListView.swift:328-329 — "Project - CheerLot에 속한 Secret만" 프리뷰 자체가 안 뜸. PR body에 "Xcode Canvas Preview 확인" 체크되어 있는데 이 하나는
    컴파일 안 됨.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. 에러 UI가 화면에 없음
  • SecretListFeature가 .failed(error) 상태로 저장은 하는데, SecretListView의 secrets computed가 .loaded만 처리 → 실패 시 "빈 리스트"로 보임. 유저는 로딩
    완료+빈 상태인지, 실패인지 구분 불가.
  • 최소한 .failed 케이스에 재시도 UI 필요.

@doyeonk429 doyeonk429 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍 리뷰 모두 수정된거 확인했고, 나머지는 후속 작업하면서 자연스럽게 수정될 것 같네용

@yeseonglee
yeseonglee merged commit 8aa24c2 into develop Jul 23, 2026
1 check passed
@yeseonglee
yeseonglee deleted the feature/#36 branch July 23, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: 리스트뷰 구현

2 participants