Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ final class DataDependencyAssembler: DependencyAssembler {
DIContainer.shared.register(type: TreatmentInterface.self) {
return DefaultTreatmentRepository(networkService: self.networkService, userDefaultService: self.userDefaultService)
}


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.

화긴!!

}
}
36 changes: 36 additions & 0 deletions Cherrish-iOS/Cherrish-iOS/Data/Model/ProcedureResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// ProcedureResponseDTO.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/20/26.
//

import Foundation

struct ProcedureDTO: Decodable {
let id: Int
let name: String
let worries: [TreatmentCategoryResponseDTO]
let minDowntimeDays: Int
let maxDowntimeDays: Int
}

struct ProceduresResponseDTO: Decodable {
let procedures: [ProcedureDTO]
}
Comment on lines +10 to +20
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가 위에 있는게 좋을 것 같아요



extension ProcedureDTO {
func toEntity() -> TreatmentEntity {
return TreatmentEntity(
id: id,
name: name,
benefits: worries.map {
$0.toEntity().title
},
downtimeMin: minDowntimeDays,
downtimeMax: maxDowntimeDays,
setDowntime: nil
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ enum OnboardingAPI: EndPoint {
return JSONEncoding.default
}

var queryParameters: [String: String]? {
var queryParameters: [String: Any]? {
return nil
}

Expand Down
27 changes: 24 additions & 3 deletions Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,46 @@
//

import Foundation

import Alamofire

enum TreatmentAPI: EndPoint {
case fetchCategories(userId: Int)
case fetchProcedures(userId: Int, id: Int? = nil, text: String? = nil)

var basePath: String {
switch self {
case .fetchCategories:
return "/api"

case .fetchProcedures:
return "/api"
Comment on lines 15 to +21
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" 해줘도 되지않아여?

}
}

var path: String {
switch self {
case .fetchCategories:
return "/worries"
case .fetchProcedures:
return "/procedures"
}
}

var method: Alamofire.HTTPMethod {
switch self {
case .fetchCategories:
return .get
case .fetchProcedures:
return .get
}
}

var headers: HeaderType {
switch self {
case .fetchCategories(let userId):
return .withAuth(userID: userId)
case .fetchProcedures(let userId, _, _):
return .withAuth(userID: userId)
}
}

Expand All @@ -45,21 +54,33 @@ enum TreatmentAPI: EndPoint {
switch self {
case .fetchCategories:
return URLEncoding.default
case .fetchProcedures:
return URLEncoding.default
}
}

var queryParameters: [String : Any]? {
switch self {
case .fetchCategories:
return nil
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
Comment on lines +66 to +74
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.

}
}

var bodyParameters: Alamofire.Parameters? {
switch self {
case .fetchCategories:
return .none
case .fetchProcedures:
return .none
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,19 @@ struct DefaultTreatmentRepository: TreatmentInterface {
)
return response.map { $0.toEntity() }
}

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.

개행~~

return response.procedures.map { $0.toEntity() }
}
}

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.

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

return []
}
Comment on lines +38 to +40
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).


func fetchCategories() async throws -> [TreatmentCategoryEntity] {
return [
TreatmentCategoryEntity(id: 1, title: "피부결 ∙ 각질"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ final class DomainDependencyAssembler: DependencyAssembler {
return DefaultCreateProfileUseCase(repository: onboardingRepository)
}

guard let treatmentCategoryRepository = DIContainer.shared.resolve(type: TreatmentInterface.self) else {
guard let treatmentRepository = DIContainer.shared.resolve(type: TreatmentInterface.self) else {
return
}

DIContainer.shared.register(type: FetchTreatmentCategoriesUseCase.self) {
return DefaultFetchTreatmentCategoriesUseCase(repository: treatmentCategoryRepository)
return DefaultFetchTreatmentCategoriesUseCase(repository: treatmentRepository)
}

DIContainer.shared.register(type: FetchTreatmentsUseCase.self) {
return DefaultFetchTreatmentsUseCase(repository: treatmentRepository)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import Foundation

protocol TreatmentInterface {
func fetchCategories() async throws -> [TreatmentCategoryEntity]
func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity]
}
Comment on lines 10 to 13
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.

102 changes: 8 additions & 94 deletions Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,103 +8,17 @@
import Foundation

struct TreatmentEntity: Identifiable, Equatable, Hashable {
var id: Self { self }
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
}
Comment on lines +16 to +20
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.

}

extension TreatmentEntity {
static let mockData: [TreatmentEntity] = [
// 피부결 · 각질
TreatmentEntity(
name: "아쿠아필",
benefits: ["각질 제거", "모공 청소", "피부결 개선"],
downtimeMin: 0,
downtimeMax: 1
),
TreatmentEntity(
name: "크리스탈 필링",
benefits: ["각질 제거", "피부 톤 개선", "잔주름 완화"],
downtimeMin: 1,
downtimeMax: 3
),

// 색소 · 잡티
TreatmentEntity(
name: "피코토닝",
benefits: ["색소 침착 개선", "잡티 제거", "피부 톤 균일화"],
downtimeMin: 0,
downtimeMax: 1
),
TreatmentEntity(
name: "레이저토닝",
benefits: ["기미 개선", "색소 침착 완화", "피부 톤 업"],
downtimeMin: 1,
downtimeMax: 2
),

// 홍조
TreatmentEntity(
name: "브이빔 레이저",
benefits: ["홍조 완화", "혈관 축소", "피부 진정"],
downtimeMin: 1,
downtimeMax: 3
),
TreatmentEntity(
name: "엑셀브이",
benefits: ["홍조 개선", "안면홍조 치료", "혈관 병변 제거"],
downtimeMin: 2,
downtimeMax: 5
),

// 탄력 · 주름
TreatmentEntity(
name: "울쎄라",
benefits: ["리프팅", "탄력 개선", "주름 완화"],
downtimeMin: 3,
downtimeMax: 7
),
TreatmentEntity(
name: "써마지",
benefits: ["피부 탄력", "콜라겐 생성", "처진 피부 개선"],
downtimeMin: 0,
downtimeMax: 2
),
TreatmentEntity(
name: "보톡스",
benefits: ["주름 개선", "이마 주름", "미간 주름"],
downtimeMin: 0,
downtimeMax: 1
),

// 모공
TreatmentEntity(
name: "프락셀",
benefits: ["모공 축소", "피부 재생", "흉터 개선"],
downtimeMin: 5,
downtimeMax: 7
),
TreatmentEntity(
name: "모공보톡스",
benefits: ["모공 축소", "피지 조절", "매끈한 피부"],
downtimeMin: 0,
downtimeMax: 1
),

// 트러블
TreatmentEntity(
name: "PDT",
benefits: ["여드름 치료", "피지 조절", "염증 완화"],
downtimeMin: 2,
downtimeMax: 5
),
TreatmentEntity(
name: "압출 관리",
benefits: ["여드름 제거", "모공 청소", "피부 진정"],
downtimeMin: 1,
downtimeMax: 3
)
]
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// FetchTreatmentsUseCase.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/20/26.
//

import Foundation

protocol FetchTreatmentsUseCase {
func execute(id: Int?, keyword: String?) async throws -> [TreatmentEntity]
}

struct DefaultFetchTreatmentsUseCase: FetchTreatmentsUseCase {

private let repository: TreatmentInterface

init(repository: TreatmentInterface) {
self.repository = repository
}

func execute(id: Int?, keyword: String?) async throws -> [TreatmentEntity] {
return try await repository.fetchTreatment(id: id, keyword: keyword)
}
}
Loading