Skip to content

Design/#38 - Sidebar UI 및 SidebarFeature 구현#40

Open
dlguszoo wants to merge 5 commits into
developfrom
design/#38
Open

Design/#38 - Sidebar UI 및 SidebarFeature 구현#40
dlguszoo wants to merge 5 commits into
developfrom
design/#38

Conversation

@dlguszoo

@dlguszoo dlguszoo commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

✨ What’s this PR?

📌 관련 이슈 (Related Issue)


🧶 주요 변경 내용 (Summary)

  • SidebarFilter, SidebarSelection enum 정의 및 SidebarFeature 구현
  • DVCategory, DVProjectContainer를 활용한 SidebarView 구현
    • 해당 컴포넌트 자잘 UI 수정
  • MainFeature에 SidebarFeature를 Scope으로 연결, MainView에 SidebarView 주입

📸 스크린샷 (Optional)

스크린샷 2026-07-16 오전 5 23 38

🧪 테스트 / 검증 내역

  • UI 정상 동작 확인
  • 필터(All / Star / Expired / Deleted) 탭 시 선택 상태 전환 확인
  • 프로젝트 행 선택 시 filter 선택 해제되는 것 확인
  • 프로젝트 섹션 접기/펼치기 동작 확인
  • 하단 설정/추가 버튼 탭 확인
  • My Mac, macOS 14.0 환경에서 정상 동작

💬 기타 공유 사항

SidebarSelection enum 도입
filter 선택과 project 선택은 동시에 존재할 수 없는 상호 배타적 상태.
selectedFilter + selectedProjectIndex 두 프로퍼티로 분리하면 둘이 동시에 값을 갖는 잘못된 상태가 모델링 가능하므로, SidebarSelection enum 하나로 통합해 타입 시스템에서 불가능하게 만들었습니다.

List selection Binding 처리
List(selection:)Binding<Int?>를 요구하지만 store의 selection은 SidebarSelection(enum)이라 타입 불일치로 $store 직접 바인딩이 불가합니다.
가이드라인(8.4)를 벗어나지만, 의도적 예외이므로 주석으로 이유를 명시했습니다.

List selection 컬러
macOS List .sidebar 스타일의 selection 하이라이트 색상은 .tint()로 변경 불가 — 시스템 Accent Color를 강제 사용합니다. NSAccentColorName asset 방식도 시도했으나 동작하지 않아 시스템 컬러로 유지합니다.

List 가로 패딩
사이드바 전체에 .padding(.horizontal, 12)를 적용하되, List만 .padding(.horizontal, -12)로 상쇄해 List가 사이드바 전체 너비를 사용하도록 처리했습니다.

아무래도... 다크모드 대응이 필수일 듯 합니다..

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

추후 예정 작업

  • 실제 프로젝트 데이터 연결 (현재 더미 데이터)
  • contextMenu Action 구현 (Rename, New Secret, Delete Project)
  • MainFeature.handleSidebarDelegate 실제 구현 (현재 모두 .none)
  • SidebarFeature TestStore 기반 단위 테스트 작성

Summary by CodeRabbit

  • 새 기능

    • 사이드바에 전체, 즐겨찾기, 만료, 삭제 필터와 프로젝트 목록을 추가했습니다.
    • 프로젝트 섹션을 펼치거나 접고, 프로젝트 추가 및 선택 기능을 사용할 수 있습니다.
    • 설정 및 추가 버튼과 프로젝트별 컨텍스트 메뉴를 제공하고, 메인 화면에 사이드바를 연결했습니다.
  • 스타일 개선

    • 카테고리 아이콘, 텍스트, 선택 상태의 색상과 크기를 조정했습니다.
    • 프로젝트 항목의 여백, 색상, 아이콘 크기를 개선했습니다.
    • 사이드바 너비와 기본 배경 대비를 조정해 가독성을 높였습니다.

@dlguszoo dlguszoo linked an issue Jul 15, 2026 that may be closed by this pull request
5 tasks
@dlguszoo dlguszoo self-assigned this Jul 15, 2026
@dlguszoo dlguszoo added the 🎨 Design UI 디자인 작업 label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1664bdc2-35d1-44b9-92ae-de53ecc085e0

📥 Commits

Reviewing files that changed from the base of the PR and between d673266 and 9b5f61e.

📒 Files selected for processing (6)
  • Projects/DVDesign/Sources/Components/DVCategory.swift
  • Projects/DVDesign/Sources/Components/DVProjectContainer.swift
  • Projects/DVPresentation/Sources/Features/Main/MainFeature.swift
  • Projects/DVPresentation/Sources/Features/Main/MainView.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift
🚧 Files skipped from review as they are similar to previous changes (5)
  • Projects/DVDesign/Sources/Components/DVProjectContainer.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift
  • Projects/DVDesign/Sources/Components/DVCategory.swift
  • Projects/DVPresentation/Sources/Features/Main/MainFeature.swift

Walkthrough

사이드바 필터·프로젝트 선택 기능을 TCA로 구현하고 MainFeature 및 NavigationSplitView에 연결했습니다. 사이드바 UI와 프로젝트 목록을 추가했으며, 카테고리와 프로젝트 컨테이너의 디자인 토큰 및 레이아웃을 조정했습니다.

Changes

메인 및 사이드바 UI

Layer / File(s) Summary
사이드바 상태와 이벤트 리듀서
Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift
필터·프로젝트 선택 모델, 확장 상태, 사용자 액션과 델리게이트를 정의하고 리듀서에서 상태 변경 및 이벤트 전달을 처리합니다.
사이드바 레이아웃과 디자인 시스템 적용
Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift, Projects/DVDesign/Sources/Components/DVCategory.swift, Projects/DVDesign/Sources/Components/DVProjectContainer.swift
필터 그리드, 프로젝트 섹션, 하단 버튼과 미리보기를 추가하고 관련 컴포넌트의 색상·크기·패딩을 조정합니다.
메인 기능과 앱 화면 연결
Projects/DVPresentation/Sources/Features/Main/MainFeature.swift, Projects/DVPresentation/Sources/Features/Main/MainView.swift
MainFeature가 SidebarFeature를 스코프하고, MainView가 SidebarView와 사이드바 컬럼 너비를 NavigationSplitView에 연결합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SidebarView
  participant SidebarFeature
  participant MainFeature
  User->>SidebarView: 필터 또는 프로젝트 선택
  SidebarView->>SidebarFeature: didSelect 전송
  SidebarFeature->>SidebarFeature: selection 갱신
  SidebarFeature->>MainFeature: selectionChanged delegate 전송
  MainFeature-->>SidebarView: 스코프된 상태로 화면 갱신
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 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 사이드바 UI와 SidebarFeature 구현이라는 핵심 변경을 직접적으로 잘 요약했습니다.
Linked Issues check ✅ Passed 로고, 컬렉션 그리드, Project 섹션, 하단 버튼, 선택 상태 강조가 모두 구현되어 이슈 #38 요구사항과 일치합니다.
Out of Scope Changes check ✅ Passed Main 연결과 Sidebar 뷰 구현에 필요한 범위 안에서 변경되었고, 명백한 무관 변경은 보이지 않습니다.
✨ 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 design/#38

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.

🧹 Nitpick comments (1)
Projects/DVDesign/Sources/Components/DVCategory.swift (1)

98-103: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

불필요한 isHovered 분기 제거

isHovered 조건이 참일 때의 반환값과 조건문을 통과한 기본 반환값이 모두 gray200으로 동일합니다. 중복되는 조건 분기를 제거하여 코드를 더 간결하게 유지하는 것을 제안합니다.

💡 제안하는 수정안
     private func backgroundColor(isPressed: Bool) -> Color {
         if isSelected { return Color.dv(.vaultGreen) }
         if isPressed  { return Color.dv(.gray300) }
-        if isHovered  { return Color.dv(.gray200) }
         return Color.dv(.gray200)
     }
🤖 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/DVCategory.swift` around lines 98 - 103,
Remove the redundant isHovered branch from backgroundColor(isPressed:), since it
returns the same gray200 color as the default path; preserve the isSelected and
isPressed priority and keep gray200 as the fallback.
🤖 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/DVDesign/Sources/Components/DVCategory.swift`:
- Around line 98-103: Remove the redundant isHovered branch from
backgroundColor(isPressed:), since it returns the same gray200 color as the
default path; preserve the isSelected and isPressed priority and keep gray200 as
the fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc94e1fd-89d4-4aa9-ba86-7e3f75a636d8

📥 Commits

Reviewing files that changed from the base of the PR and between 9171222 and d673266.

📒 Files selected for processing (11)
  • Projects/DVDesign/Sources/Components/DVCategory.swift
  • Projects/DVDesign/Sources/Components/DVProjectContainer.swift
  • Projects/DVPresentation/Sources/Features/AppFeature.swift
  • Projects/DVPresentation/Sources/Features/AppView.swift
  • Projects/DVPresentation/Sources/Features/Main/MainFeature.swift
  • Projects/DVPresentation/Sources/Features/Main/MainView.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift
  • Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift
  • Projects/DVPresentation/Sources/ProjectSecretRelationDemoView.swift
  • Projects/DVPresentation/Sources/SecretUseCaseDemoView.swift
  • Projects/Devault/Sources/ContentView.swift
💤 Files with no reviewable changes (3)
  • Projects/Devault/Sources/ContentView.swift
  • Projects/DVPresentation/Sources/SecretUseCaseDemoView.swift
  • Projects/DVPresentation/Sources/ProjectSecretRelationDemoView.swift

dlguszoo added 5 commits July 16, 2026 10:25
SidebarFilter, SidebarSelection enum 정의 및 SidebarFeature 구현.
SidebarSelection으로 filter/project 선택을 단일 상태로 표현해 상호 배타성 보장.
Scope으로 SidebarFeature를 MainFeature에 연결하고
SidebarView를 MainView sidebar 컬럼에 주입.
DVCategory, DVProjectContainer 컴포넌트를 활용한 사이드바 UI 구현.
filterGrid, projectSection, bottomBar로 구성.
}
.listStyle(.sidebar)
.scrollContentBackground(.hidden)
.frame(maxHeight: 200)

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.

macOS 앱은 세로로 크게 리사이즈 가능한데, 프로젝트 리스트가 창 높이와 무관하게 최대 200pt로 고정됩니다. 프로젝트가 5개만 넘어가도 즉시 내부 스크롤이 생기고, 아래쪽 Spacer()는 남는 공간을 다 먹습니다. 창이 아무리 커도 UX는 그대로.

제안: .frame(maxHeight: 200) 제거 + 바깥 Spacer() 제거 → List가 남는 세로 공간을 자연스럽게 채우도록. 또는 최소 .frame(minHeight: 100, maxHeight: .infinity) 형태로.

if isPressed { return Color.dv(.gray300) }
if isHovered { return Color.dv(.gray200) }
return Color.dv(.gray100)
return Color.dv(.gray200)

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.

default를 gray100 → gray200으로 올리면서 hover 상태와 색이 동일해졌습니다. .onHover + .animation을 걸어놨지만 시각 변화가 없어 hover 피드백이 죽어있습니다. macOS는 마우스가 항상 존재하므로 hover 피드백이 특히 중요합니다.

Comment on lines +49 to +51
public struct State: Equatable {
var selection: SidebarSelection = .filter(.all)
var isProjectSectionExpanded: Bool = true

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.

State는 public인데 프로퍼티는 internal. 같은 모듈에서만 접근하니 지금은 문제 없지만, 다른 모듈에서 sidebar의 state를 관찰해야 하는 시점이 오면 걸립니다.

public getter만 노출하거나, 추후 수정.

Comment on lines +57 to +73
private var filterGrid: some View {
LazyVGrid(
columns: [GridItem(.flexible()), GridItem(.flexible())],
spacing: 12
) {
ForEach(SidebarFilter.allCases, id: \.self) { filter in
DVCategory(
title: filter.title,
count: 0,
systemImage: filter.icon,
isSelected: store.selection == .filter(filter)
) {
store.send(.didSelect(.filter(filter)))
}
}
}
}

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.

NavigationSplitView의 min: 200, padding.horizontal 122=24를 빼면 그리드는 176pt. 2 flex 컬럼 + spacing 12
→ 각 셀 ~82pt. DVCategory 내부 padding 102=20 → 컨텐츠 62pt. "Expired" 텍스트가 bodyLG로 이 안에 들어가는지 실제로 200pt 폭에서 확인 필요. 위험하면 GridItem(.adaptive(minimum: 90))으로 자동 재배치.

Comment on lines +165 to +170
SidebarView(
store: Store(initialState: SidebarFeature.State()) {
SidebarFeature()
}
)
.frame(width: 240)

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.

min: 200과 max: 300도 프리뷰 추가 권장 (Group { ... }로 세 개 나열).
macOS 서비스 특수성 반영해서!

Comment on lines +24 to +30
var icon: String {
switch self {
case .all: "square.grid.2x2.fill"
case .starred: "star.fill"
case .expired: "exclamationmark"
case .deleted: "trash"
}

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.

아이콘 전용 버튼들 (gearshape, plus, plus.circle, chevron.up/down)에 accessibilityLabel이 없어 VoiceOver로 "button"으로만 읽힙니다. macOS는 접근성 검사가 App Review에서도 이슈가 되므로 지금 추가 권장

Comment on lines +15 to +21
var title: String {
switch self {
case .all: "All"
case .starred: "Star"
case .expired: "Expired"
case .deleted: "Deleted"
}

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.

로컬라이제이션: "All", "Star", "Project", "Rename" 등 모든 사용자 노출 문자열이 하드코딩. 후속 이슈로 관리하고 있는지 확인

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Design UI 디자인 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Design: 사이드바 뷰 구현

2 participants