Skip to content

Refactor/#84 - 소팅 메뉴 재설계 및 닫힘 버그 수정 - #98

Merged
yeseonglee merged 1 commit into
developfrom
refactor/#84
Aug 16, 2026
Merged

Refactor/#84 - 소팅 메뉴 재설계 및 닫힘 버그 수정#98
yeseonglee merged 1 commit into
developfrom
refactor/#84

Conversation

@yeseonglee

@yeseonglee yeseonglee commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

✨ What's this PR?

📌 관련 이슈 (Related Issue)


🧶 주요 변경 내용 (Summary)

1. SecretQuery.Sort를 (기준 × 방향) 구조로 재설계

기존 recentlyAdded/oldestFirst/expiringSoon/nameAscending/nameDescending 5케이스는 정렬 기준과 방향을 한 enum에 묶어놔서, 3기준(시간/만료/이름) × 2방향(오름/내림) = 6개 조합 중 5개만 존재했습니다. 만료 내림차순(만료 늦은 순)을 표현할 방법이 없었습니다.

Sortkey(time/expiry/name) + direction(ascending/descending) 구조체로 바꿔 6개 조합을 전부 표현할 수 있게 했습니다.

2. 정렬 기준 "시간"을 updatedAt으로 통일

목록 행에는 updatedAt이 표시되는데(SecretListView.swift) recentlyAdded 정렬은 createdAt 기준이었습니다. 사용자가 보는 날짜와 정렬 기준이 어긋나던 문제를 고쳤습니다.

3. 만료 정렬에서 만료일 없는 항목을 방향과 무관하게 뒤로

SwiftData SortDescriptor는 옵셔널을 nil < .some으로만 비교합니다. 그래서 만료 오름차순 정렬에서 만료일 없는 Secret이 맨 앞에 쌓이는 문제가 있었습니다. SortDescriptor 하나로는 "방향과 무관하게 nil을 뒤로"를 표현할 수 없어서, InMemorySecretQueryFilter가 SwiftData 결과를 받아 nil을 항상 뒤로 보내는 후처리를 추가했습니다.

4. 정렬 메뉴를 시스템 Menu로 교체 — 바깥 클릭 시 안 닫히던 버그 수정

기존 메뉴는 ZStack + Color.clear(바깥 클릭 감지) + 손으로 그린 SortMenuRow로 만든 커스텀 오버레이였습니다. Color.clear가 목록 컬럼 안에만 있어서 사이드바·상세 컬럼을 클릭하면 메뉴가 안 닫혔습니다.

DVTitleBar를 시스템 Menu 기반으로 바꿔(onSortTapped 콜백 → sortMenuContent 클로저) 바깥 클릭·ESC·포커스 상실 처리를 OS(NSMenu)에 위임했습니다. 메뉴 안은 Picker(.inline) 2개(기준/방향, Divider로 구분)로 구성했습니다. 커스텀 SortMenuRow, isSortMenuPresented 상태는 전부 제거됐습니다.

5. DVData에 테스트 타깃 신설

SecretFetchDescriptorBuilder.sortDescriptors는 private 함수인 데다 SortDescriptor가 직접 비교(Equatable)를 지원하지 않아, 정렬 로직을 실제로 검증하려면 in-memory SwiftData 컨테이너로 fetch까지 돌려봐야 했습니다. DVData에 테스트 타깃 자체가 없어서(Tests/.gitkeep만 존재) 새로 만들었습니다.


📸 스크린샷 (Optional)


🧪 테스트 / 검증 내역

  • xcodebuild test DVDomain — 182 tests passed (Sort 6개 조합 표현 가능 여부 포함)
  • xcodebuild test DVData — 신규 6 tests passed (in-memory SwiftData로 time/expiry/name × 오름/내림 실제 fetch 검증, 만료일 없는 항목 후순위 포함)
  • xcodebuild test DVPresentation — 160 tests passed
  • xcodebuild build DVDesign — BUILD SUCCEEDED
  • CODING_GUIDELINES.md / TCA_GUIDELINES.md 대조 자체 리뷰

💬 기타 공유 사항

  • ExpiryBucket(Expired 탭 섹션 구분, 7일/30일 리터럴)은 이번에 손대지 않았습니다. 다른 PR(Feature/#85 - 만료 배지 tooltip 추가 / 임계값(3/7/30일) SecretExpiryPolicy로 통합 #97, SecretExpiryPolicy)이 정리할 부분이라 범위 밖으로 뒀습니다.
  • DVTitleBar.sortMenuContent(() -> AnyView)? 타입 소거를 씁니다. 옵셔널 제네릭 클로저 파라미터가 Swift에서 표현하기 까다로워 택한 절충입니다. 정렬 버튼 하나뿐이라 실질 영향은 없다고 판단했습니다 — 제네릭 구조체(DVTitleBar<SortMenu: View>)로 가는 게 낫다면 알려주세요.
  • Picker의 레이블("Sort by", "Direction")이 하드코딩 영어 문자열입니다. .pickerStyle(.inline)이라 화면엔 안 보이지만 VoiceOver엔 노출됩니다. 같은 파일의 기존 문자열도 전부 미번역 상태라 기존 관례를 따랐고, l10n은 별도 이슈(Feature: Localization (ko/en) 적용 #87) 범위로 남겨뒀습니다.

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

  • SecretQuery.swiftSort 구조체 설계 — key/direction을 nested enum으로 둔 것이 적절한지
  • InMemorySecretQueryFilter.sortedByExpiry — SwiftData 레벨 정렬 결과를 무시하고 메모리에서 다시 정렬하는 방식이 타당한지, 데이터 규모가 커지면 성능 이슈가 될지
  • DVTitleBar.swiftMenu + AnyView 타입 소거 — 이대로 갈지 제네릭화할지
  • Projects/DVData/Tests/RepositoryImpl/Secret/SecretSortingTests.swift — in-memory ModelContainer로 실제 fetch를 검증하는 접근이 DVData 테스트 패턴으로 적절한지 (이 모듈의 첫 테스트라 선례가 없습니다)

Summary by CodeRabbit

  • 새로운 기능

    • 보안 항목 정렬에서 시간, 만료일, 이름 기준과 오름차순·내림차순을 자유롭게 선택할 수 있습니다.
    • 만료일이 없는 항목은 만료일 정렬 시 항상 마지막에 표시됩니다.
    • 정렬 메뉴가 시스템 기본 메뉴 방식으로 제공되어 더 일관된 사용 경험을 제공합니다.
  • 개선

    • 만료된 항목은 만료일이 빠른 순서로 정렬됩니다.
    • 정렬 동작과 다양한 정렬 조합에 대한 검증을 강화했습니다.

@yeseonglee yeseonglee self-assigned this Aug 16, 2026
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b1d8cbe-736f-47ba-937b-14faa12c4256

📥 Commits

Reviewing files that changed from the base of the PR and between 4e5ea4f and e9c3329.

📒 Files selected for processing (12)
  • Projects/DVData/Project.swift
  • Projects/DVData/Sources/RepositoryImpl/Secret/InMemorySecretQueryFilter.swift
  • Projects/DVData/Sources/RepositoryImpl/Secret/SecretFetchDescriptorBuilder.swift
  • Projects/DVData/Tests/RepositoryImpl/Secret/SecretSortingTests.swift
  • Projects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swift
  • Projects/DVDesign/Sources/Components/DVTitleBar.swift
  • Projects/DVDomain/Sources/Repository/Model/SecretQuery.swift
  • Projects/DVDomain/Tests/Core/Repository/SecretQueryTests.swift
  • Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListFeature.swift
  • Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
  • Projects/DVPresentation/Tests/SecretList/SecretListFeatureTests.swift

Walkthrough

Secret 정렬 모델을 기준·방향 조합으로 변경했습니다. 저장소는 updatedAtexpiresAt 기준을 사용합니다. 정렬 메뉴는 시스템 MenuPicker로 교체했습니다. 관련 테스트와 프리뷰를 갱신했습니다.

Changes

Secret 정렬 흐름

Layer / File(s) Summary
정렬 계약과 저장소 구현
Projects/DVDomain/Sources/Repository/Model/SecretQuery.swift, Projects/DVData/Sources/RepositoryImpl/Secret/*, Projects/DVData/Project.swift
SecretQuery.SortKeyDirection 조합으로 변경했습니다. 저장소는 시간·만료일·이름 정렬을 처리하며, 만료일이 없는 항목을 항상 뒤에 배치합니다.
정렬 메뉴와 화면 연결
Projects/DVDesign/Sources/Components/DVTitleBar.swift, Projects/DVPresentation/Sources/Features/SecretList/*, Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
DVTitleBarsortMenuContent를 추가했습니다. SecretListView는 기준과 방향을 각각 Picker로 제공하고 didSelectSort를 전송합니다.
정렬 계약 검증
Projects/DVDomain/Tests/Core/Repository/SecretQueryTests.swift, Projects/DVData/Tests/RepositoryImpl/Secret/SecretSortingTests.swift
기본 정렬과 6개 조합을 검증합니다. 시간·만료일·이름 정렬 및 nil 만료일 후순위를 검증합니다.
화면 통합과 프리뷰 검증
Projects/DVPresentation/Tests/SecretList/SecretListFeatureTests.swift, Projects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swift
Sort 조합과 만료일 오름차순 조회를 테스트에 반영했습니다. DVTitleBar 프리뷰를 새 메뉴 API에 맞게 변경했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DVTitleBar
  participant SecretListView
  participant SecretListFeature
  participant SecretClient
  User->>DVTitleBar: 정렬 메뉴 열기
  DVTitleBar->>SecretListView: sortMenuContent 표시
  User->>SecretListView: 기준 또는 방향 선택
  SecretListView->>SecretListFeature: didSelectSort 전송
  SecretListFeature->>SecretClient: 변경된 Sort로 조회
Loading

Possibly related PRs

Suggested labels: 🔨 Refactor, 🐞 BugFix, ✅ Test

Suggested reviewers: doyeonk429, dlguszoo

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#84

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.

SecretQuery.Sort가 recentlyAdded/oldestFirst/expiringSoon/nameAscending/
nameDescending 5케이스로 기준과 방향을 한데 묶고 있어 3기준×2방향 조합 중
하나(만료 내림차순)를 표현할 수 없었다. key(time/expiry/name)와
direction(ascending/descending)을 독립된 축으로 분리했다.

- time 기준을 updatedAt으로 통일 — 목록 행에 표시되는 날짜와 정렬 기준이
  달랐던 문제(기존엔 createdAt 기준)를 해소했다.
- SwiftData SortDescriptor는 옵셔널을 nil < .some으로만 비교해 만료
  오름차순에서 만료일 없는 항목이 맨 앞에 오던 문제가 있었다.
  InMemorySecretQueryFilter가 방향과 무관하게 nil을 뒤로 보내는 후처리를
  맡는다.
- 커스텀 오버레이(ZStack + Color.clear + 손으로 그린 SortMenuRow)가
  목록 컬럼 밖 클릭에 안 닫히던 버그의 원인이었다. DVTitleBar를 시스템
  Menu 기반으로 바꿔 바깥 클릭·ESC·포커스 상실 처리를 OS에 위임했다.
  메뉴 안은 Picker(.inline) 2개(기준/방향, divider로 구분)로 구성했다.
- DVData에 테스트 타깃이 없어 sortDescriptors를 직접 검증할 수 없었다.
  in-memory SwiftData 컨테이너로 6개 조합의 실제 fetch 결과를 확인하는
  테스트 타깃을 새로 만들었다.

# Conflicts:
#	Projects/DVData/Project.swift
@yeseonglee
yeseonglee merged commit 5ea050b into develop Aug 16, 2026
1 check was pending
@doyeonk429
doyeonk429 deleted the refactor/#84 branch August 16, 2026 08:26
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.

Refactor: 정렬 메뉴 (기준 × 방향) 재설계 및 메뉴 닫힘 버그 수정

3 participants