Skip to content

Feature/#69~72 - CreateSecret 화면 구현 완성 (Live 연결·감지·에러·로딩) - #73

Merged
doyeonk429 merged 7 commits into
developfrom
feature/#69
Aug 8, 2026
Merged

Feature/#69~72 - CreateSecret 화면 구현 완성 (Live 연결·감지·에러·로딩)#73
doyeonk429 merged 7 commits into
developfrom
feature/#69

Conversation

@doyeonk429

@doyeonk429 doyeonk429 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

✨ What's this PR?

📌 관련 이슈 (Related Issue)


🧶 주요 변경 내용 (Summary)


📸 스크린샷 (Optional)


🧪 테스트 / 검증 내역

  • CreateSecretFeatureTests — save 성공/실패, 프로젝트 로드, 자동 감지, 에러 alert 흐름 통과
  • MainFeatureTests — typeSelected / secretCreated / cancelled 라우팅 3개 추가 테스트 통과
  • SidebarFeatureTests — delete 흐름 테스트 통과
  • 앱 실행 후 Add 버튼 → 타입 선택 → CreateSecret 화면 진입 동작 확인
  • 취소 시 타입 선택 화면으로 복귀 (선택값 초기화) 확인

💬 기타 공유 사항

  • task effect의 CancellationError 명시적 catch 추가 — ifLet 해제 시 발생하는 취소가 에러로 오인되지 않도록 처리
  • Live Client 간 Repository 인스턴스를 LiveRepositories enum으로 통합해 SwiftData modelContainer 공유 일관성 확보

🙇🏻‍♀️ 리뷰 가이드 (선택)

  • MainFeature.swift: cancelled 핸들러에서 selectSecretType = .init() (nil이 아닌 초기화) — 타입 선택 화면으로 되돌아가는 의도된 동작
  • LiveRepositories.swift: 새 파일 — Composition Root 전체에서 Repository 인스턴스 공유 구조

- DetectionClient TCA 의존성 정의 및 liveValue 조립 (DetectSecretUseCaseImpl 래핑)
- SecretMetaFields에 primaryDetectionValue / primaryDetectionFieldID 추가
- binding에서 DetectionClient 동기 호출 → serviceCandidates / detectedServices 갱신
- CreateSecretFeature 전체 구현 (isLoadingProjects, 에러 매핑, 감지 연결)
- CreateSecretFeature 단위 테스트 추가 (감지·에러·로딩·delegate 포함)
- CreateSecretError: SecretUseCaseError → 표시용 에러 매핑 + AlertState 프리셋
- ProjectLoadError: ProjectUseCaseError → 표시용 에러 매핑 + AlertState 프리셋
- FormLayoutMode에 isProjectLoading 환경 키 추가
- ProjectFieldView: 프로젝트 로딩 중 ProgressView 표시
- CreateSecretView: 저장 중 스크롤 비활성화 + 반투명 오버레이 + ProgressView
- SecretManagementClient / ProjectClient / SecretClient liveValue 조립 (UseCase 기반)
- MainFeature: SelectSecretType → CreateSecret NavigationSplitView 전환 연결
- MainView: CreateSecretView를 detail 컬럼에서 화면 교체 방식으로 표시
- AddToProjectFeatureTests: ProjectItem 타입 수정 및 sidebarClient 의존성 픽스
- MainFeatureTests / SidebarFeatureTests: 기존 테스트 시나리오 픽스
- mockdata_for_ui_test.txt: UI 수동 테스트용 시크릿 타입별 샘플 데이터
- cancelled 핸들러에서 selectSecretType을 nil 대신 .init()으로 리셋해 타입 선택 화면으로 복귀
- typeSelected / secretCreated / cancelled 라우팅 3개 테스트 커버리지 추가
- ifLet 해제 시 발생하는 취소가 오류로 오인되지 않도록 CancellationError catch 분리
- LiveRepositories enum으로 secret/project Repository 인스턴스 통합해 modelContainer 공유
- 각 Live Client의 불필요한 do-catch 래핑 제거 (UseCase가 이미 도메인 에러로 매핑)
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

CreateSecret 기능에 자동 감지, 프로젝트 로딩 표시, 저장 오류 알림을 추가했습니다. 라이브 의존성을 구성하고, 메인 화면에서 생성 화면의 진입·완료·취소 흐름을 연결했습니다. 관련 상태 전이와 오류 매핑 테스트도 확장했습니다.

Changes

CreateSecret 기능 통합

Layer / File(s) Summary
감지 및 오류 모델
Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift, Projects/DVPresentation/Sources/Features/CreateSecret/Model/*
DetectionClient를 추가했습니다. 시크릿 유형별 감지 값과 필드 ID를 매핑합니다. 프로젝트 로드 및 시크릿 생성 오류를 AlertState로 변환합니다.
CreateSecret 상태 및 Reducer
Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift, Projects/DVPresentation/Tests/Features/CreateSecret/*
프로젝트 로딩 상태를 처리합니다. 입력 변경 시 감지 결과를 갱신합니다. 저장 실패와 프로젝트 조회 실패를 오류별 알림으로 표시합니다. 관련 상태 전이, 검증, 재시도, 취소 테스트를 추가했습니다.
라이브 의존성 구성
Projects/Devault/Sources/Composition/Dependencies/*, Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift
감지, 프로젝트, 시크릿, 시크릿 관리 클라이언트의 liveValue를 연결했습니다. 공유 저장소 구성을 추가하고 프로젝트 조회 의존성을 정리했습니다.
로딩 및 저장 UI
Projects/DVPresentation/Sources/Features/CreateSecret/Components/*, Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift
프로젝트 조회 중 프로젝트 필드에 ProgressView를 표시합니다. 저장 중 폼을 비활성화하고 진행 오버레이를 표시합니다.
메인 화면 라우팅
Projects/DVPresentation/Sources/Features/Main/*, Projects/DVPresentation/Tests/Main/*, Projects/DVPresentation/Tests/Sidebar/*
시크릿 생성 상태와 액션을 MainFeature에 연결했습니다. 생성 화면의 진입, 완료, 취소 흐름을 구현하고 관련 테스트를 추가했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant 사용자
  participant MainView
  participant CreateSecretFeature
  participant DetectionClient
  participant ProjectClient
  participant SecretManagementClient

  사용자->>MainView: 시크릿 유형 선택
  MainView->>CreateSecretFeature: 생성 화면 표시
  CreateSecretFeature->>ProjectClient: 프로젝트 목록 조회
  사용자->>CreateSecretFeature: 시크릿 필드 입력
  CreateSecretFeature->>DetectionClient: 감지 값 전달
  CreateSecretFeature->>SecretManagementClient: 시크릿 생성 요청
  SecretManagementClient-->>CreateSecretFeature: 생성 결과 반환
  CreateSecretFeature-->>MainView: 생성 완료 또는 취소 전달
Loading

Possibly related PRs

Suggested labels: ✨ Feature

Suggested reviewers: yeseonglee

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed #69~72의 live client 연결, 자동 감지, 에러 처리, 로딩 UI, 라우팅 요구사항을 모두 구현하고 관련 테스트를 추가했습니다.
Out of Scope Changes check ✅ Passed 변경 사항은 연결된 이슈의 기능 구현, 의존성 공유, 라우팅 및 회귀 테스트 범위에 포함됩니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed CreateSecret 화면 완성과 Live 연결, 감지, 오류 처리, 로딩 등 변경 사항의 주요 내용을 명확하게 요약합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#69

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.

@doyeonk429 doyeonk429 self-assigned this Aug 7, 2026
@doyeonk429 doyeonk429 changed the title [#69~72] Feature: CreateSecret 화면 구현 완성 Feature/#69~72 - CreateSecret 화면 구현 완성 (Live 연결·감지·에러·로딩) Aug 7, 2026
@doyeonk429
doyeonk429 requested review from dlguszoo and yeseonglee and removed request for dlguszoo August 7, 2026 15:14
@doyeonk429 doyeonk429 added the ✨ Feature 새로운 기능 개발 label Aug 7, 2026

@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.

🧹 Nitpick comments (5)
Projects/DVPresentation/Tests/Main/MainFeatureTests.swift (1)

43-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

사이드바 선택 시 createSecret 종료도 검증하세요.

handleSidebarDelegate는 선택 변경 시 createSecret = nil을 수행합니다. 현재 테스트는 selectSecretType만 검증합니다. 생성 폼이 열린 상태를 초기값에 추가하고 createSecret이 제거되는지 검증하세요.

제안 변경
     initial.selectSecretType = .init()
+    initial.createSecret = CreateSecretFeature.State(secretType: .apiKeyToken)
     initial.sidebar.isCreatingSecret = true
     initial.sidebar.selection = .filter(.starred)
@@
     await store.receive(.sidebar(.delegate(.selectionChanged(.filter(.all))))) {
       $0.selectSecretType = nil
+      $0.createSecret = nil
     }
🤖 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/Main/MainFeatureTests.swift` around lines 43 -
54, Update the MainFeature test setup around initial.sidebar.selection to
initialize the createSecret state as open, then extend the
.sidebar(.delegate(.selectionChanged(.filter(.all)))) assertion to verify
createSecret is cleared to nil alongside selectSecretType. Keep the existing
sidebar selection assertions unchanged.
Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ProjectFieldView.swift (1)

30-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

로딩 뷰와 선택 뷰를 body 밖으로 분리하세요.

Line 31-52는 body에 두 개의 중첩 레이아웃을 직접 포함합니다. loadingContentprojectSelectionContent를 extension의 private var로 분리하세요. body에는 상태 분기만 유지하세요.

제안 변경
 var body: some View {
     if isProjectLoading {
-        DVLabeledField(.module("Project"), size: size) {
-            HStack(spacing: 6) {
-                ProgressView()
-                    .controlSize(.small)
-                Spacer()
-            }
-        }
+        loadingContent
     } else {
-        DVLabeledField(.module("Project"), size: size) {
-            DVMultiSelectDropdown(...)
-        }
+        projectSelectionContent
     }
 }
+
+extension ProjectFieldView {
+    private var loadingContent: some View { /* existing loading layout */ }
+    private var projectSelectionContent: some View { /* existing dropdown layout */ }
+}

As per path instructions, "var body 안에 중첩 레이아웃이 직접 구현되어 있으면 extension의 private var/func로 분리를 제안하세요."

🤖 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/CreateSecret/Components/Fields/ProjectFieldView.swift`
around lines 30 - 53, Extract the loading layout from the isProjectLoading
branch into a private loadingContent computed property, and extract the
project-selection DVMultiSelectDropdown layout into a private
projectSelectionContent computed property in an extension. Keep body limited to
selecting between these two properties based on isProjectLoading, preserving all
existing configuration and behavior.

Source: Path instructions

Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

내장 모듈을 먼저 배치하세요.

세 파일이 Foundation을 다른 모듈 뒤에 배치합니다. Foundation을 먼저 두고 빈 줄 뒤에 ComposableArchitecture, DVDomain을 유지하세요.

  • Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift#L3-L5: Foundation을 첫 import로 이동하세요.
  • Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreateSecretError.swift#L3-L5: Foundation을 첫 import로 이동하세요.
  • Projects/DVPresentation/Sources/Features/CreateSecret/Model/ProjectLoadError.swift#L3-L5: Foundation을 첫 import로 이동하세요.

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/DVPresentation/Sources/Dependencies/DetectionClient.swift` around
lines 3 - 5, Reorder imports so the built-in Foundation import appears first,
followed by a blank line and the alphabetized ComposableArchitecture and
DVDomain imports. Apply this change in
Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift (lines 3-5),
Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreateSecretError.swift
(lines 3-5), and
Projects/DVPresentation/Sources/Features/CreateSecret/Model/ProjectLoadError.swift
(lines 3-5).

Source: Path instructions

Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift (1)

144-154: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

자동 감지 범위를 secret-content 변경으로 제한하세요.

.binding fallthrough는 이름, 메모, 서비스, 프로젝트 선택 변경에도 detectionClient.detect를 실행합니다. 실제 필드 바인딩은 $store.meta.content.typed(...)로 전달되므로, \.meta.content 변경에서만 감지하고 그 외 binding에서는检测结果을 유지되도록 해주세요.

🤖 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/CreateSecret/CreateSecretFeature.swift`
around lines 144 - 154, Update the .binding handling to run
detectionClient.detect only when the binding represents a change to
\.meta.content, matching the $store.meta.content.typed(...) binding. Preserve
existing serviceCandidates and detectedServices for all other binding changes,
while retaining the current empty-content clearing behavior.
Projects/Devault/Sources/Composition/Dependencies/SecretClient+Live.swift (1)

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

두 파일에서 내장 프레임워크 import 순서를 수정하세요.

두 파일 모두 Foundation을 다른 모듈 뒤에 import합니다. Foundation을 첫 그룹으로 이동하고 빈 줄로 나머지 모듈과 구분하세요.

  • Projects/Devault/Sources/Composition/Dependencies/SecretClient+Live.swift#L3-L7: Foundation을 첫 import로 이동하세요.
  • Projects/Devault/Sources/Composition/Dependencies/SecretManagementClient+Live.swift#L3-L7: Foundation을 첫 import로 이동하세요.

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/Devault/Sources/Composition/Dependencies/SecretClient`+Live.swift
around lines 3 - 7, 두 파일의 import 블록을 수정하세요:
Projects/Devault/Sources/Composition/Dependencies/SecretClient+Live.swift 3-7행과
Projects/Devault/Sources/Composition/Dependencies/SecretManagementClient+Live.swift
3-7행에서 Foundation을 첫 번째 내장 프레임워크 import로 이동하고, Foundation과 나머지 모듈 사이에 빈 줄을
추가하세요. 각 그룹 내 import는 알파벳순으로 유지하세요.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@Projects/Devault/Sources/Composition/Dependencies/SecretClient`+Live.swift:
- Around line 3-7: 두 파일의 import 블록을 수정하세요:
Projects/Devault/Sources/Composition/Dependencies/SecretClient+Live.swift 3-7행과
Projects/Devault/Sources/Composition/Dependencies/SecretManagementClient+Live.swift
3-7행에서 Foundation을 첫 번째 내장 프레임워크 import로 이동하고, Foundation과 나머지 모듈 사이에 빈 줄을
추가하세요. 각 그룹 내 import는 알파벳순으로 유지하세요.

In `@Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift`:
- Around line 3-5: Reorder imports so the built-in Foundation import appears
first, followed by a blank line and the alphabetized ComposableArchitecture and
DVDomain imports. Apply this change in
Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift (lines 3-5),
Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreateSecretError.swift
(lines 3-5), and
Projects/DVPresentation/Sources/Features/CreateSecret/Model/ProjectLoadError.swift
(lines 3-5).

In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ProjectFieldView.swift`:
- Around line 30-53: Extract the loading layout from the isProjectLoading branch
into a private loadingContent computed property, and extract the
project-selection DVMultiSelectDropdown layout into a private
projectSelectionContent computed property in an extension. Keep body limited to
selecting between these two properties based on isProjectLoading, preserving all
existing configuration and behavior.

In
`@Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift`:
- Around line 144-154: Update the .binding handling to run
detectionClient.detect only when the binding represents a change to
\.meta.content, matching the $store.meta.content.typed(...) binding. Preserve
existing serviceCandidates and detectedServices for all other binding changes,
while retaining the current empty-content clearing behavior.

In `@Projects/DVPresentation/Tests/Main/MainFeatureTests.swift`:
- Around line 43-54: Update the MainFeature test setup around
initial.sidebar.selection to initialize the createSecret state as open, then
extend the .sidebar(.delegate(.selectionChanged(.filter(.all)))) assertion to
verify createSecret is cleared to nil alongside selectSecretType. Keep the
existing sidebar selection assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 69e0b949-3575-4110-af43-890752a14aea

📥 Commits

Reviewing files that changed from the base of the PR and between df907f7 and 55b4f19.

📒 Files selected for processing (20)
  • Projects/DVPresentation/Sources/Dependencies/DetectionClient.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ProjectFieldView.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/Components/FormLayoutMode.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreateSecretError.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/Model/ProjectLoadError.swift
  • Projects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields.swift
  • Projects/DVPresentation/Sources/Features/Main/MainFeature.swift
  • Projects/DVPresentation/Sources/Features/Main/MainView.swift
  • Projects/DVPresentation/Tests/AddToProject/AddToProjectFeatureTests.swift
  • Projects/DVPresentation/Tests/Features/CreateSecret/CreateSecretFeatureTests.swift
  • Projects/DVPresentation/Tests/Main/MainFeatureTests.swift
  • Projects/DVPresentation/Tests/Sidebar/SidebarFeatureTests.swift
  • Projects/Devault/Sources/Composition/Dependencies/DetectionClient+Live.swift
  • Projects/Devault/Sources/Composition/Dependencies/LiveRepositories.swift
  • Projects/Devault/Sources/Composition/Dependencies/ProjectClient+Live.swift
  • Projects/Devault/Sources/Composition/Dependencies/SecretClient+Live.swift
  • Projects/Devault/Sources/Composition/Dependencies/SecretManagementClient+Live.swift
  • Projects/Devault/Sources/Composition/Dependencies/SidebarClient+Live.swift

@doyeonk429
doyeonk429 merged commit 3bd416a into develop Aug 8, 2026
1 check passed
@doyeonk429
doyeonk429 deleted the feature/#69 branch August 8, 2026 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 개발

Projects

None yet

2 participants