Skip to content

Comments

Feat/#114 시술 필터 API 연결 , 다운 타임 설정 구현#122

Merged
wotjs020708 merged 14 commits intodevelopfrom
feat/#114-treatmentfilterapi
Jan 20, 2026
Merged

Feat/#114 시술 필터 API 연결 , 다운 타임 설정 구현#122
wotjs020708 merged 14 commits intodevelopfrom
feat/#114-treatmentfilterapi

Conversation

@wotjs020708
Copy link
Contributor

🔗 연결된 이슈

📄 작업 내용

  • 시술 API 연결
구현 내용 IPhone 16 pro IPhone 13 mini
GIF
검색 기능
다운타임 설정

💻 주요 코드 설명

  • 서버 로직은 기존과 비슷 하여 코드 확인 부탁드립니다.
    TreatmentEntity
  • 다운 타임 설정을 위해 mutating 활용하여 값 변경
struct TreatmentEntity: Identifiable, Equatable, Hashable {
    let id: Int
    let name: String
    let benefits: [String]
    let downtimeMin: Int
    let downtimeMax: Int
    var setDowntime: Int?
    
    mutating func updateDowntime(_ value: Int) {
            setDowntime = value
        }
}

@wotjs020708 wotjs020708 requested a review from a team January 20, 2026 17:00
@wotjs020708 wotjs020708 self-assigned this Jan 20, 2026
@wotjs020708 wotjs020708 requested review from soseoyo12, sum130 and y-eonee and removed request for a team January 20, 2026 17:00
@wotjs020708 wotjs020708 added 재선🐻 Feature 새로운 기능 구현 및 API 연결 labels Jan 20, 2026
@wotjs020708 wotjs020708 linked an issue Jan 20, 2026 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

시술 필터링 API 연계 및 다운타임 설정을 위한 도메인/데이터/프레젠테이션 계층의 모델·유스케이스·리포지토리·뷰/뷰모델 변경을 도입합니다. 구체적으로 새로운 DTO·API 엔드포인트·리포지토리 메서드·유스케이스 추가와 뷰/뷰모델의 바인딩·상태/검증 로직 변경이 포함됩니다.

Changes

코호트 / 파일(들) 변경 요약
데이터 - API
Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift, Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/OnboardingAPI.swift
TreatmentAPIfetchProcedures(userId:id:text:) 케이스 추가(경로, 헤더, 쿼리 파라미터, 인코딩). OnboardingAPI.queryParameters 타입을 [String: Any]?로 변경.
데이터 - DTO/매퍼
Cherrish-iOS/Cherrish-iOS/Data/Model/ProcedureResponseDTO.swift
ProcedureDTO, ProceduresResponseDTO 추가 및 ProcedureDTO.toEntity()TreatmentEntity 매핑 구현.
데이터 - 리포지토리
Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift
fetchTreatment(id:keyword:) async throws -> [TreatmentEntity] 추가(기본/목 구현).
데이터 - DI(포맷)
Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift
TreatmentInterface 등록 뒤 포맷(빈 라인) 변경(로직 미변경).
도메인 - 인터페이스/유스케이스/의존성
Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift, Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchTreatmentsUseCase.swift, Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift
인터페이스에 fetchTreatment(id:keyword:) 추가, FetchTreatmentsUseCase 프로토콜 및 DefaultFetchTreatmentsUseCase 구현 추가, Domain DI에 해당 유스케이스 등록 및 변수명 조정.
도메인 - 엔티티
Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift
id를 계산속성에서 Int 저장속성으로 변경, setDowntime: Int? 추가, updateDowntime(_:) 추가, 기존 mockData 제거.
프레젠테이션 - 뷰/시트 바인딩
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift, .../DowntimeBottomSheetView.swift
DownTimeSettingView.treatments@Binding으로 변경. DowntimeBottomSheetView@Binding var treatment@Binding var selectedTreatment을 받아 downtime 업데이트 로직을 수행하도록 변경.
프레젠테이션 - 필터/검색 UI 변경
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift, .../Treatment/TreatmentView.swift, .../NoTreatment/NoTreatmentFilterView.swift, .../NoTreatment/NoTreatmentView.swift, .../TreatmentSearchBarTextField.swift, .../TargetDdaySettingView.swift
검색 호출(onTap) 핸들러명 변경, 필터 로직 단순화(항상 viewModel.treatments 사용), 뷰 로드 시 .task로 fetch호출 추가, TreatmentView의 viewModel을 @StateObject로 변경, 레이아웃 패딩 조정 등.
프레젠테이션 - 뷰모델(치료)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift
fetchTreatmentsUseCase 주입 추가, treatmentspublic private(set)으로 변경, fetchTreatments() 비동기 추가, canProceed 다운타임 검증 업데이트 및 선택 관리 헬퍼 추가(확장으로 이동).
프레젠테이션 - 뷰모델(비시술)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift, 삭제된 확장 파일들
treatments, selectedTreatmentsfetchTreatmentsUseCase 추가, fetchCategories()/fetchNoTreatments() 도입, canProceed 검증 수정. 기존 별도 확장 파일(필터/검색) 다수 삭제 및 관련 메서드 재배치.
프레젠테이션 - DI
Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift
FetchTreatmentsUseCase를 Resolve해 NoTreatmentViewModelTreatmentViewModel에 주입하도록 변경(해결 실패 시 조기 반환).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TreatmentView
    participant TreatmentViewModel
    participant FetchTreatmentsUseCase
    participant TreatmentRepository
    participant TreatmentAPI
    participant Server
    participant ProcedureDTO
    participant TreatmentEntity

    User->>TreatmentView: 뷰 로드 / 검색 요청
    TreatmentView->>TreatmentViewModel: fetchTreatments(id?, keyword?)
    TreatmentViewModel->>FetchTreatmentsUseCase: execute(id?, keyword?)
    FetchTreatmentsUseCase->>TreatmentRepository: fetchTreatment(id?, keyword?)
    TreatmentRepository->>TreatmentAPI: 요청: fetchProcedures(userId, id, text)
    TreatmentAPI->>Server: GET /api/procedures?...
    Server-->>TreatmentAPI: JSON (ProceduresResponseDTO)
    TreatmentAPI-->>TreatmentRepository: 데이터 전달
    TreatmentRepository->>ProcedureDTO: 디코딩
    ProcedureDTO->>TreatmentEntity: toEntity()
    TreatmentRepository-->>FetchTreatmentsUseCase: [TreatmentEntity]
    FetchTreatmentsUseCase-->>TreatmentViewModel: [TreatmentEntity]
    TreatmentViewModel-->>TreatmentView: `@Published` 업데이트 (뷰 갱신)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related PRs

Suggested reviewers

  • sum130
  • soseoyo12
  • y-eonee
🚥 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
Title check ✅ Passed PR 제목은 주요 변경 사항(시술 필터 API 연결, 다운타임 설정 구현)을 명확하게 요약하고 있어 변경 사항의 핵심을 잘 전달합니다.
Description check ✅ Passed PR 설명은 연결된 이슈, 작업 내용, 구현 예시, 시각 자료 등을 포함하여 변경 사항과 관련된 충분한 정보를 제공합니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 #114 이슈의 요구 사항(시술 필터링 API 연결, 검색 기능)을 충족합니다. API 통합, 데이터 모델, 뷰모델, UI 컴포넌트가 모두 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #114 이슈의 범위 내입니다. 시술 API 연결, 검색, 다운타임 설정, DI 구성 등 모든 변경이 이슈 요구사항에 직접적으로 관련됩니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#114-treatmentfilterapi

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 and usage tips.

Copy link

@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: 11

🤖 Fix all issues with AI agents
In `@Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift`:
- Around line 38-39: Remove the unnecessary blank lines introduced in
DataDependencyAssembler.swift to restore original formatting and consistency;
locate the extra empty lines around the DataDependencyAssembler declaration (or
within its surrounding top-level declarations) and delete them so the file has
no redundant blank lines while preserving existing code structure and spacing
elsewhere.

In `@Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift`:
- Around line 66-74: In the .fetchProcedures case in TreatmentAPI (where params
is built), avoid adding keyword when text is empty or only whitespace; instead
trim text with text.trimmingCharacters(in: .whitespacesAndNewlines) and only set
params["keyword"] when the trimmed string is not empty, leaving params unset for
blank inputs so no empty "keyword=" query is sent.

In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift`:
- Around line 38-40: The mock TreatmentRepository's fetchTreatment(id: Int?,
keyword: String?) currently returns an empty array causing blank UI/previews;
update fetchTreatment(id:keyword:) in TreatmentRepository to return a small set
of representative TreatmentEntity instances (e.g., one or two sample
TreatmentEntity objects with realistic title, description, id and any required
fields) when running in mock or preview mode so previews and QA show data;
ensure the sample data covers cases like lookup by id and simple keyword
filtering (use TreatmentEntity's initializer and any existing sample factory
helpers if present).

In `@Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift`:
- Around line 10-13: Rename the protocol method fetchTreatment to the plural
form fetchTreatments to reflect its array return type and improve consistency
with fetchCategories; update the TreatmentInterface protocol declaration
(replace func fetchTreatment(id: Int?, keyword: String?) async throws ->
[TreatmentEntity] with func fetchTreatments(id: Int?, keyword: String?) async
throws -> [TreatmentEntity]) and then propagate that rename to all conforming
types, callers, and any tests that reference fetchTreatment so signatures and
call sites use TreatmentInterface.fetchTreatments and the TreatmentEntity return
type consistently.

In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift`:
- Around line 16-20: updateDowntime currently assigns value directly to
setDowntime allowing out-of-range values; change updateDowntime(_ value: Int) to
validate against the entity's downtimeMin and downtimeMax (or the UI bounds like
CherrishPicker 1...30) and either clamp the input into the valid range or ignore
invalid values before assigning to setDowntime, e.g., compute validated =
min(max(value, downtimeMin), downtimeMax) and set setDowntime = validated (or
return/throw on invalid input) so the domain model invariants are preserved.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift`:
- Around line 14-22: The search button is currently disabled because
TreatmentSearchBarTextField is instantiated with isDisabled: true; change this
to isDisabled: false to enable the button and update the enter handler (the
closure passed as enter that calls await viewModel.fetchTreatments()) to guard
against empty searchText — e.g., check viewModel.searchText is non‑empty before
calling fetchTreatments(), or add that check inside viewModel.fetchTreatments()
itself; reference TreatmentSearchBarTextField, viewModel.searchText, and the
enter closure/viewModel.fetchTreatments() when making the change.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift`:
- Line 13: The view declares `@StateObject` var viewModel: TreatmentViewModel but
the reviewer questions ownership intent; if the view should observe an
externally provided, replaceable instance switch to `@ObservedObject` for
viewModel in TreatmentView so updates propagate without owning it, otherwise if
the view must own and instantiate the TreatmentViewModel initialize it
explicitly in the view's initializer using _viewModel =
StateObject(wrappedValue: ...) to ensure correct lifecycle; locate the
TreatmentView and TreatmentViewModel symbols and apply the appropriate change
based on whether the instance is injected or constructed inside the view.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift`:
- Around line 63-69: In NoTreatmentViewModel’s fetchCategories() replace the
empty catch with explicit error handling: on failure (from
fetchCategoriesUseCase.execute()) reset or set a safe default for the categories
property (e.g., [] or a single “failed to load” category), set an error state
property (e.g., categoriesError or isCategoriesLoadingFailed) or message so the
UI can render an error/empty state, and ensure these state updates happen on the
main actor/queue; keep fetchCategories()’s signature but populate the error
state and default categories so the UI doesn’t silently remain in a previous
state.
- Around line 72-77: The fetchNoTreatments() method currently calls
fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "") which may
send an empty string to the server; change the call to pass nil instead of an
empty string when the keyword is empty (e.g., compute a local keywordParam =
keyword.isEmpty ? nil : keyword) so execute receives nil for no filter, and
update the invocation of fetchTreatmentsUseCase.execute(id:
selectedCategory?.id, keyword: keywordParam); ensure this logic is applied
inside fetchNoTreatments() and any related caller that constructs the keyword
parameter.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift`:
- Around line 51-58: The fetchTreatments() call currently passes searchText
directly to fetchTreatmentsUseCase.execute, which may send an empty string to
the server; update fetchTreatments() to detect an empty searchText and pass nil
instead (e.g., let keyword = searchText.isEmpty ? nil : searchText) when calling
fetchTreatmentsUseCase.execute(id: nil, keyword: keyword) and keep the existing
error fallback that sets treatments = []; ensure references: fetchTreatments(),
searchText, fetchTreatmentsUseCase.execute(id:keyword:), and treatments are
updated accordingly.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`:
- Around line 56-58: The guard that resolves FetchTreatmentsUseCase via
DIContainer.shared.resolve currently returns silently on failure; update it to
mirror the other guards by logging an error when resolution fails (e.g., use the
same logger or logging function used elsewhere in
PresentationDependencyAssembler) and then return. Locate the guard referencing
FetchTreatmentsUseCase and DIContainer.shared.resolve(type:
FetchTreatmentsUseCase.self), add an error log message including the type name
to aid debugging, and keep the subsequent return behavior unchanged.

Comment on lines +38 to +39


Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

단순 포맷팅 변경입니다.

불필요한 빈 줄이 추가되었습니다. 코드 품질에 영향은 없으나, 일관성을 위해 제거를 고려해 주세요.

♻️ 빈 줄 제거
         DIContainer.shared.register(type: TreatmentInterface.self) {
             return DefaultTreatmentRepository(networkService: self.networkService, userDefaultService: self.userDefaultService)
         }
-        
-        
     }
📝 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
DIContainer.shared.register(type: TreatmentInterface.self) {
return DefaultTreatmentRepository(networkService: self.networkService, userDefaultService: self.userDefaultService)
}
}
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift` around lines 38
- 39, Remove the unnecessary blank lines introduced in
DataDependencyAssembler.swift to restore original formatting and consistency;
locate the extra empty lines around the DataDependencyAssembler declaration (or
within its surrounding top-level declarations) and delete them so the file has
no redundant blank lines while preserving existing code structure and spacing
elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

화긴!!

Comment on lines +66 to +74
case .fetchProcedures(_, let id, let text):
var params: [String: Any] = [:]
if let id = id {
params["worryId"] = id
}
if let text = text {
params["keyword"] = "\(text)"
}
return params
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

빈 검색어도 쿼리로 전송됨

text가 빈 문자열이면 keyword=가 전송됩니다. 백엔드가 빈 키워드를 필터로 해석하면 전체 결과가 비워질 수 있으니 공백/빈 문자열은 제외하는 게 안전합니다.

🔧 제안 수정안
 case .fetchProcedures(_, let id, let text):
     var params: [String: Any] = [:]
     if let id = id {
         params["worryId"] = id
     }
-    if let text = text {
-        params["keyword"] = "\(text)"
-    }
-    return params
+    if let text = text?.trimmingCharacters(in: .whitespacesAndNewlines),
+       !text.isEmpty {
+        params["keyword"] = text
+    }
+    return params.isEmpty ? nil : params
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift` around
lines 66 - 74, In the .fetchProcedures case in TreatmentAPI (where params is
built), avoid adding keyword when text is empty or only whitespace; instead trim
text with text.trimmingCharacters(in: .whitespacesAndNewlines) and only set
params["keyword"] when the trimmed string is not empty, leaving params unset for
blank inputs so no empty "keyword=" query is sent.

Comment on lines +38 to +40
func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] {
return []
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Mock 데이터가 비어 있어 UI/미리보기에서 공백 화면 가능성

Mock 저장소가 항상 빈 배열을 반환하면 검색/필터 UI가 빈 상태로만 보여 QA/미리보기에서 흐름 확인이 어렵습니다. 필요하다면 최소 샘플 데이터를 제공하는 방향을 고려해 주세요.

🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift` around
lines 38 - 40, The mock TreatmentRepository's fetchTreatment(id: Int?, keyword:
String?) currently returns an empty array causing blank UI/previews; update
fetchTreatment(id:keyword:) in TreatmentRepository to return a small set of
representative TreatmentEntity instances (e.g., one or two sample
TreatmentEntity objects with realistic title, description, id and any required
fields) when running in mock or preview mode so previews and QA show data;
ensure the sample data covers cases like lookup by id and simple keyword
filtering (use TreatmentEntity's initializer and any existing sample factory
helpers if present).

Comment on lines 10 to 13
protocol TreatmentInterface {
func fetchCategories() async throws -> [TreatmentCategoryEntity]
func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity]
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM! 프로토콜 확장이 깔끔합니다.

새로운 fetchTreatment 메서드가 기존 fetchCategories와 일관된 async/throws 패턴을 따르고 있습니다. 선택적 파라미터를 통해 유연한 필터링이 가능합니다.

사소한 제안: 메서드가 배열을 반환하므로 fetchTreatments (복수형)으로 이름을 변경하면 일관성이 향상될 수 있습니다.

🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift` around
lines 10 - 13, Rename the protocol method fetchTreatment to the plural form
fetchTreatments to reflect its array return type and improve consistency with
fetchCategories; update the TreatmentInterface protocol declaration (replace
func fetchTreatment(id: Int?, keyword: String?) async throws ->
[TreatmentEntity] with func fetchTreatments(id: Int?, keyword: String?) async
throws -> [TreatmentEntity]) and then propagate that rename to all conforming
types, callers, and any tests that reference fetchTreatment so signatures and
call sites use TreatmentInterface.fetchTreatments and the TreatmentEntity return
type consistently.

Comment on lines +16 to +20
var setDowntime: Int?

mutating func updateDowntime(_ value: Int) {
setDowntime = value
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# TreatmentEntity.swift 파일 위치 찾기
find . -name "TreatmentEntity.swift" -type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 132


🏁 Script executed:

# 파일이 발견되면 내용 확인
find . -name "TreatmentEntity.swift" -type f -exec wc -l {} \;

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 135


🏁 Script executed:

# updateDowntime와 setDowntime 사용처 검색
rg -n --type=swift "\.updateDowntime\(|setDowntime\s*=" -C 2

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 2667


🏁 Script executed:

# TreatmentEntity.swift의 downtime 관련 모든 속성 확인
rg -n --type=swift "downtime" -i

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 12636


🏁 Script executed:

cat -n ./Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 641


🏁 Script executed:

head -n 220 ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift | tail -n 100

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3288


🏁 Script executed:

# DowntimeBottomSheetView에서 selectedDowntime과 downtimeMin/Max 관계 확인
rg -A 10 -B 10 "CherrishPicker.*selectedDowntime" ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 697


🏁 Script executed:

cat ./Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift | head -n 180

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 5830


🏁 Script executed:

# CherrishPicker의 정의 확인
find . -name "*.swift" -type f -exec grep -l "struct CherrishPicker" {} \;

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 149


🏁 Script executed:

cat ./Cherrish-iOS/Cherrish-iOS/Presentation/Global/Components/CherrishPicker.swift

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 3270


updateDowntime 메서드에서 범위 검증 필요

updateDowntime이 입력값을 검증 없이 그대로 할당하므로, downtimeMindowntimeMax 범위를 벗어난 값이 저장될 수 있습니다. 특히:

  • updateDowntime(0) 호출 시 downtimeMin > 0이면 범위 위반
  • UI의 CherrishPicker가 1...30 고정이므로 downtimeMax를 초과할 수 있음

도메인 모델의 불변성을 보장하려면 최소한의 범위 검증이 필요합니다.

🔧 제안 수정안
mutating func updateDowntime(_ value: Int) {
-    setDowntime = value
+    let clamped = min(max(value, downtimeMin), downtimeMax)
+    setDowntime = clamped
}
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift` around lines 16
- 20, updateDowntime currently assigns value directly to setDowntime allowing
out-of-range values; change updateDowntime(_ value: Int) to validate against the
entity's downtimeMin and downtimeMax (or the UI bounds like CherrishPicker
1...30) and either clamp the input into the valid range or ignore invalid values
before assigning to setDowntime, e.g., compute validated = min(max(value,
downtimeMin), downtimeMax) and set setDowntime = validated (or return/throw on
invalid input) so the domain model invariants are preserved.

@EnvironmentObject private var calendarCoordinator: CalendarCoordinator
@EnvironmentObject private var tabBarCoordinator: TabBarCoordinator
@ObservedObject var viewModel: TreatmentViewModel
@StateObject var viewModel: TreatmentViewModel
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

@StateObject 소유권 의도 확인 필요
외부에서 주입되는 인스턴스가 교체될 수 있다면 @ObservedObject가 안전합니다. 뷰 소유가 의도라면 초기화 경로를 명확히 해주세요.

🔧 명시적 소유권 초기화 예시
-    `@StateObject` var viewModel: TreatmentViewModel
+    `@StateObject` private var viewModel: TreatmentViewModel
+
+    init(viewModel: TreatmentViewModel) {
+        _viewModel = StateObject(wrappedValue: viewModel)
+    }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift`
at line 13, The view declares `@StateObject` var viewModel: TreatmentViewModel but
the reviewer questions ownership intent; if the view should observe an
externally provided, replaceable instance switch to `@ObservedObject` for
viewModel in TreatmentView so updates propagate without owning it, otherwise if
the view must own and instantiate the TreatmentViewModel initialize it
explicitly in the view's initializer using _viewModel =
StateObject(wrappedValue: ...) to ensure correct lifecycle; locate the
TreatmentView and TreatmentViewModel symbols and apply the appropriate change
based on whether the instance is injected or constructed inside the view.

Comment on lines +63 to +69
func fetchCategories() async {
do {
categories = try await fetchCategoriesUseCase.execute()
} catch {

}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

카테고리 로딩 실패 처리 방식 개선 고려
에러 발생 시 UI가 이전 상태를 유지할 수 있어, 기본값 초기화나 오류 상태 전달을 고려해 주세요.

🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift`
around lines 63 - 69, In NoTreatmentViewModel’s fetchCategories() replace the
empty catch with explicit error handling: on failure (from
fetchCategoriesUseCase.execute()) reset or set a safe default for the categories
property (e.g., [] or a single “failed to load” category), set an error state
property (e.g., categoriesError or isCategoriesLoadingFailed) or message so the
UI can render an error/empty state, and ensure these state updates happen on the
main actor/queue; keep fetchCategories()’s signature but populate the error
state and default categories so the UI doesn’t silently remain in a previous
state.

Comment on lines +72 to 77
func fetchNoTreatments() async {
do {
treatments = try await fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "")
} catch {
treatments = []
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

keyword 빈 문자열 처리 계약 확인 필요
서버가 빈 문자열을 필터로 취급할 수 있으니, 비어있으면 nil로 전달하는지 확인 권장합니다.

🔧 예시 수정안
-            treatments = try await fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "")
+            treatments = try await fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: nil)
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift`
around lines 72 - 77, The fetchNoTreatments() method currently calls
fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "") which may
send an empty string to the server; change the call to pass nil instead of an
empty string when the keyword is empty (e.g., compute a local keywordParam =
keyword.isEmpty ? nil : keyword) so execute receives nil for no filter, and
update the invocation of fetchTreatmentsUseCase.execute(id:
selectedCategory?.id, keyword: keywordParam); ensure this logic is applied
inside fetchNoTreatments() and any related caller that constructs the keyword
parameter.

Comment on lines 51 to 58
@MainActor
func fetchTreatments() async {
do {
treatments = try await fetchTreatmentsUseCase.execute(id: nil, keyword: searchText)
} catch {
treatments = []
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

검색어 비어 있을 때 keyword 처리 확인 필요
서버가 ""를 필터로 해석하면 초기 로드가 비어질 수 있습니다. 계약상 nil이 맞는지 확인 후 처리 분기 권장합니다.

🔧 예시 수정안
-           treatments = try await fetchTreatmentsUseCase.execute(id: nil, keyword: searchText)
+           let keyword = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
+           treatments = try await fetchTreatmentsUseCase.execute(
+               id: nil,
+               keyword: keyword.isEmpty ? nil : keyword
+           )
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift`
around lines 51 - 58, The fetchTreatments() call currently passes searchText
directly to fetchTreatmentsUseCase.execute, which may send an empty string to
the server; update fetchTreatments() to detect an empty searchText and pass nil
instead (e.g., let keyword = searchText.isEmpty ? nil : searchText) when calling
fetchTreatmentsUseCase.execute(id: nil, keyword: keyword) and keep the existing
error fallback that sets treatments = []; ensure references: fetchTreatments(),
searchText, fetchTreatmentsUseCase.execute(id:keyword:), and treatments are
updated accordingly.

Comment on lines +56 to +58
guard let fetchTreatmentsUseCase = DIContainer.shared.resolve(type: FetchTreatmentsUseCase.self) else {
return
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

DI 해상 실패 시 로그 누락

다른 guard 구문은 실패 시 에러 로깅을 남기는데, 여기만 조용히 return 합니다. 원인 파악이 어려워질 수 있으니 동일한 로깅 패턴을 적용하는 편이 좋습니다.

🔧 제안 수정안
 guard let fetchTreatmentsUseCase = DIContainer.shared.resolve(type: FetchTreatmentsUseCase.self) else {
+    CherrishLogger.error(CherrishError.DIFailedError)
     return
 }
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`
around lines 56 - 58, The guard that resolves FetchTreatmentsUseCase via
DIContainer.shared.resolve currently returns silently on failure; update it to
mirror the other guards by logging an error when resolution fails (e.g., use the
same logger or logging function used elsewhere in
PresentationDependencyAssembler) and then return. Locate the guard referencing
FetchTreatmentsUseCase and DIContainer.shared.resolve(type:
FetchTreatmentsUseCase.self), add an error log message including the type name
to aid debugging, and keep the subsequent return behavior unchanged.

Copy link
Contributor

@soseoyo12 soseoyo12 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다
image

Copy link
Contributor

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

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

굿 수고하셨습니당

Comment on lines +38 to +39


Copy link
Contributor

Choose a reason for hiding this comment

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

화긴!!

Comment on lines +10 to +20
struct ProcedureDTO: Decodable {
let id: Int
let name: String
let worries: [TreatmentCategoryResponseDTO]
let minDowntimeDays: Int
let maxDowntimeDays: Int
}

struct ProceduresResponseDTO: Decodable {
let procedures: [ProcedureDTO]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

dto간 위계를 생각하면 responsedto가 위에 있는게 좋을 것 같아요

Comment on lines 15 to +21
var basePath: String {
switch self {
case .fetchCategories:
return "/api"

case .fetchProcedures:
return "/api"
Copy link
Contributor

Choose a reason for hiding this comment

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

걍 return "/api" 해줘도 되지않아여?


func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] {
let userId: Int = userDefaultService.load(key: .userID) ?? 1
let response = try await networkService.request(TreatmentAPI.fetchProcedures(userId: userId,id: id, text: keyword), decodingType: ProceduresResponseDTO.self)
Copy link
Contributor

Choose a reason for hiding this comment

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

개행~~

}

struct MockTreatmentRepository: TreatmentInterface {
func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] {
Copy link
Contributor

Choose a reason for hiding this comment

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

혹시모르니까 목데이터 추가도.. ㅎㅎ

do {
categories = try await fetchCategoriesUseCase.execute()
} catch {

Copy link
Contributor

Choose a reason for hiding this comment

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

CherrishLogger.error(error) 추가해주세욤

do {
treatments = try await fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "")
} catch {
treatments = []
Copy link
Contributor

Choose a reason for hiding this comment

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

여기도 에러 로그 찍어주세욤

@@ -9,19 +9,22 @@ import SwiftUI
import Combine
Copy link
Contributor

Choose a reason for hiding this comment

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

컴바인 이제 지워도되지않나여

do {
treatments = try await fetchTreatmentsUseCase.execute(id: nil, keyword: searchText)
} catch {
treatments = []
Copy link
Contributor

Choose a reason for hiding this comment

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

에러로그추가


DIContainer.shared.register(type: NoTreatmentViewModel.self) {
return NoTreatmentViewModel(fetchCategoriesUseCase: fetchTreatmentCategoriesUseCase)
return NoTreatmentViewModel(fetchCategoriesUseCase: fetchTreatmentCategoriesUseCase, fetchTreatmentsUseCase: fetchTreatmentsUseCase)
Copy link
Contributor

Choose a reason for hiding this comment

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

ㄱㅐ행 한번만염

Copy link

@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: 2

🤖 Fix all issues with AI agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift`:
- Around line 36-46: The ForEach currently uses id: \.self which is brittle when
treatment properties (like setDowntime) change; switch to a stable identifier
(e.g., treatment.id or another unique immutable property) and update the ForEach
to use id: \.id (or the chosen property) so SwiftUI can reliably track rows;
also ensure Treatment model conforms to Identifiable or expose that stable key
and change any comparisons (such as selectedTreatment == treatment) to compare
identifiers (selectedTreatment?.id == treatment.id) so selection and animations
remain stable when setDowntime changes.

In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift`:
- Around line 55-58: Remove the nested Task inside the SwiftUI .task modifier in
TreatmentFilterView and call viewModel.fetchTreatments() directly so
cancellation propagates; replace the inner Task { try await
viewModel.fetchTreatments() } with a direct await inside .task, e.g. use do {
try await viewModel.fetchTreatments() } catch { /* handle or log error via
viewModel or a logger */ } to perform proper error handling while keeping the
.task lifecycle semantics unchanged.
♻️ Duplicate comments (2)
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift (1)

51-58: 빈 검색어는 nil로 정규화 필요
공백/빈 문자열이 그대로 전달되면 서버가 필터로 해석해 초기 로드가 비어질 수 있습니다.

🔧 수정 제안
-           treatments = try await fetchTreatmentsUseCase.execute(id: nil, keyword: searchText)
+           let keyword = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
+           treatments = try await fetchTreatmentsUseCase.execute(
+               id: nil,
+               keyword: keyword.isEmpty ? nil : keyword
+           )
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift (1)

13-13: @StateObject 소유권 의도 재확인 필요
외부 주입 인스턴스를 교체 가능하게 유지해야 한다면 @ObservedObject가 안전합니다. 뷰가 소유하는 것이 맞다면 초기화를 명시하세요.

🔧 수정 제안(뷰가 소유하는 경우)
-    `@StateObject` var viewModel: TreatmentViewModel
+    `@StateObject` private var viewModel: TreatmentViewModel
+
+    init(viewModel: TreatmentViewModel) {
+        _viewModel = StateObject(wrappedValue: viewModel)
+    }

Comment on lines +36 to +46
ForEach(treatments, id: \.self) { treatment in
TreatmentRowView(
displayMode: .completeBoxView,
treatmentEntity: treatment,
isSelected: .constant(
selectedTreatment == treatment

),
isCompleted: .constant(
treatment.setDowntime == nil ? false : true
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ForEach 식별자에 \.self 사용은 변경 시 UI 불안정 위험
setDowntime 변경으로 해시/동등성이 바뀌면 셀 식별자가 흔들려 선택 상태/애니메이션이 깨질 수 있습니다. 안정적인 id를 사용하세요.

🔧 수정 제안
-                    ForEach(treatments, id: \.self) { treatment in
+                    ForEach(treatments, id: \.id) { treatment in
                         TreatmentRowView(
                             displayMode: .completeBoxView,
                             treatmentEntity: treatment,
                             isSelected: .constant(
-                                selectedTreatment == treatment
+                                selectedTreatment?.id == treatment.id
                             ),
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift`
around lines 36 - 46, The ForEach currently uses id: \.self which is brittle
when treatment properties (like setDowntime) change; switch to a stable
identifier (e.g., treatment.id or another unique immutable property) and update
the ForEach to use id: \.id (or the chosen property) so SwiftUI can reliably
track rows; also ensure Treatment model conforms to Identifiable or expose that
stable key and change any comparisons (such as selectedTreatment == treatment)
to compare identifiers (selectedTreatment?.id == treatment.id) so selection and
animations remain stable when setDowntime changes.

Comment on lines +55 to +58
.task {
Task {
try await viewModel.fetchTreatments()
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

.task 내부 중첩 Task 제거 권장
중첩 Task는 뷰 생명주기 취소 전파가 끊길 수 있어 비정상 동작 위험이 있습니다. .task에서 직접 await + 오류 처리로 정리하세요.

🔧 수정 제안
-        .task {
-            Task {
-                try await viewModel.fetchTreatments()
-            }
-        }
+        .task {
+            do {
+                try await viewModel.fetchTreatments()
+            } catch {
+                // 필요 시 에러 처리/로깅
+            }
+        }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift`
around lines 55 - 58, Remove the nested Task inside the SwiftUI .task modifier
in TreatmentFilterView and call viewModel.fetchTreatments() directly so
cancellation propagates; replace the inner Task { try await
viewModel.fetchTreatments() } with a direct await inside .task, e.g. use do {
try await viewModel.fetchTreatments() } catch { /* handle or log error via
viewModel or a logger */ } to perform proper error handling while keeping the
.task lifecycle semantics unchanged.

Copy link
Contributor

@sum130 sum130 left a comment

Choose a reason for hiding this comment

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

어푸푸 드립니다~ 화긴 한 번 해주세용

HStack(alignment: .top, spacing: 0) {
TypographyText(
"실제 다운타임 및 회복 과정은 개인에 따라 다를 수 있습니다.",
"",
Copy link
Contributor

Choose a reason for hiding this comment

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

이고 모에요? 확인부탁쓰~~

.id(viewModel.step)
}
.ignoresSafeArea(.keyboard,edges: .bottom)
.ignoresSafeArea(.keyboard ,edges: .bottom)
Copy link
Contributor

Choose a reason for hiding this comment

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

ㅠㅠ

@wotjs020708 wotjs020708 merged commit 46bc95f into develop Jan 20, 2026
5 checks passed
@wotjs020708 wotjs020708 deleted the feat/#114-treatmentfilterapi branch January 20, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature 새로운 기능 구현 및 API 연결 재선🐻

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 시술 필터링 API 연결

4 participants