Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (13)
📝 WalkthroughWalkthrough사용자 정보 조회 API를 마이페이지에 연결하는 기능을 구현합니다. 데이터, 도메인, 프레젠테이션 계층에 걸쳐 저장소, 유스케이스, 뷰모델 및 관련 의존성 주입 설정을 추가합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant View as MyPageView
participant ViewModel as MyPageViewModel
participant UseCase as FetchUserInfoUseCase
participant Repository as DefaultMyPageRepository
participant UserDefaults as UserDefaultService
participant API as MyPageAPI
View->>ViewModel: .task { fetchUserInfo() }
ViewModel->>UseCase: execute()
UseCase->>Repository: fetchUserInfo()
Repository->>UserDefaults: userID 조회
UserDefaults-->>Repository: userID (기본값: 1)
Repository->>API: MyPageAPI.users(userID)
API-->>Repository: UserInfoResponseDTO
Repository->>Repository: toEntity() 변환
Repository-->>UseCase: UserInfoEntity
UseCase-->>ViewModel: UserInfoEntity
ViewModel->>ViewModel: `@Published` name, day 업데이트
ViewModel-->>View: 상태 변경 알림
View->>View: name, day 화면 렌더링
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly Related PRs
Suggested Reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Model/UserInfoResponseDTO.swift`:
- Around line 10-18: The DTO currently risks decoding failures because API uses
snake_case keys; update UserInfoResponseDTO to explicitly map days_since_signup
to daysSinceSignup by adding a CodingKeys enum (case name, daysSinceSignup =
"days_since_signup") inside the struct so Decodable uses the correct key, and
keep the toEntity() conversion unchanged; alternatively ensure NetworkService's
JSONDecoder uses keyDecodingStrategy = .convertFromSnakeCase if you prefer a
global fix.
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/MyPageRepository.swift`:
- Around line 22-28: In fetchUserInfo(), stop defaulting userID to 1
(userDefaultService.load(key: .userID) ?? 1) because it can fetch another user's
data; instead validate the optional result from userDefaultService.load(key:
.userID) and if missing either throw a clear error (e.g., define and throw a
MissingUserID error) or propagate a failure to the caller before calling
MyPageAPI.users(userID:), ensuring the function does not proceed with a
hardcoded fallback.
In `@Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchUserInfoUseCase.swift`:
- Around line 14-22: Rename the struct DefaultFetchUserInfoUserCase to
DefaultFetchUserInfoUseCase throughout the codebase and update all references
(including initializers, injections, protocol conformance to
FetchUserInfoUseCase, and any places that instantiate or type-annotate it) so
the identifier matches the corrected spelling; ensure the initializer
init(repository: MyPageInterface), the execute() async throws -> UserInfoEntity
method, and any tests or DI registrations are updated to the new name to avoid
compilation errors or mismatches.
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/MyPage/MyPageView.swift`:
- Around line 11-12: 현재 MyPageView에 외부에서 주입된 `@StateObject` var viewModel:
MyPageViewModel 사용은 부모가 인스턴스를 교체할 때 업데이트를 놓칠 수 있으니, 주입된 뷰모델이면 선언을
`@ObservedObject로` 변경하거나 뷰가 소유해야 할 경우에는 private `@StateObject로` 만들고 init(viewModel:
MyPageViewModel)에서 _viewModel = StateObject(wrappedValue: viewModel)로 명시적으로
초기화하여 소유권을 선언하십시오; 관련 심볼: MyPageView, MyPageViewModel, `@StateObject`,
`@ObservedObject`, init, _viewModel = StateObject(wrappedValue:).
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/MyPage/MyPageViewModel.swift`:
- Around line 10-23: The `@Published` properties name and day in MyPageViewModel
are being updated off the main thread inside fetchUserInfo(), so either mark the
view model class with `@MainActor` (e.g., `@MainActor` final class MyPageViewModel)
or ensure the assignments to name and day happen on the main actor by wrapping
them in await MainActor.run { ... } after calling
fetchUserInfoUseCase.execute(); update the implementation of fetchUserInfo()
accordingly to guarantee main-thread updates for the `@Published` properties.
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`:
- Around line 60-66: The guard early-returns silently when
DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) yields nil, which
later leads to a fatalError in makeMyPageView(); update the guard to log an
error (using the same logger/mechanism used for other DI failures) that includes
the resolved type and context before returning so the DI failure is visible and
traceable; ensure the log references fetchUserInfoUseCase,
DIContainer.shared.resolve, MyPageViewModel and makeMyPageView() to make the
cause clear.
| struct UserInfoResponseDTO: Decodable { | ||
| let name: String | ||
| let daysSinceSignup: Int | ||
| } | ||
|
|
||
| extension UserInfoResponseDTO { | ||
| func toEntity() -> UserInfoEntity { | ||
| .init(name: name, daysSinceSignup: daysSinceSignup) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the file and examine its context
find . -name "UserInfoResponseDTO.swift" -type fRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 134
🏁 Script executed:
# Search for test files related to this DTO
find . -name "*UserInfo*" -type f | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 266
🏁 Script executed:
# Look for API response examples, mock data, or test fixtures
find . -type f \( -name "*.json" -o -name "*Mock*" -o -name "*Test*" \) | grep -i user | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Search for actual usage of UserInfoResponseDTO to understand the API response format
rg "UserInfoResponseDTO" -A 5 -B 5 --type swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 2597
🏁 Script executed:
# Look for any API documentation or response mapping
find . -name "*.swift" -type f | xargs rg -l "days_since_signup\|daysSinceSignup" | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Find MyPageAPI definition to understand the endpoint
rg "enum MyPageAPI\|case users" -A 10 --type swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Look for API documentation, OpenAPI specs, or response examples
find . -type f \( -name "*.md" -o -name "*.yaml" -o -name "*.yml" -o -name "*.json" \) | xargs rg "days_since_signup\|daysSinceSignup" 2>/dev/null | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Check the network service implementation to understand how JSONDecoder is configured
rg "JSONDecoder\|keyDecodingStrategy" -B 3 -A 5 --type swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Look for any test files or fixtures that might have actual API responses
find . -type f -name "*Test*" -o -name "*Fixture*" -o -name "*Mock*" | xargs rg "name\|daysSinceSignup" 2>/dev/null | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# Check if there's API documentation or README
find . -name "README*" -o -name "API*" | xargs cat 2>/dev/null | grep -A 10 -B 2 -i "user\|response" | head -40Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 52
🏁 Script executed:
# List files in the data layer to understand structure
find ./Cherrish-iOS/Cherrish-iOS/Data -type f -name "*.swift" | head -20Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1194
🏁 Script executed:
# Find API definitions
find . -name "*API.swift" -o -name "*api.swift" | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 199
🏁 Script executed:
# Look at MyPageRepository more carefully
cat ./Cherrish-iOS/Cherrish-iOS/Data/Repository/MyPageRepository.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 877
🏁 Script executed:
# Search for any configuration or comments about API response format
rg "name|days|signup" -B 3 -A 3 --type swift | head -50Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 5072
🏁 Script executed:
# Check if there are any unit tests
find . -name "*Test*.swift" -o -name "*test.swift" | head -10Repository: TEAM-Cherrish/Cherrish-iOS
Length of output: 133
🏁 Script executed:
# Examine MyPageAPI.swift
cat ./Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/MyPageAPI.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 1039
🏁 Script executed:
# Examine NetworkService.swift
cat ./Cherrish-iOS/Cherrish-iOS/Data/Network/NetworkService.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 4441
🏁 Script executed:
# Check the single test file to see if there are response examples
cat ./Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TestAPI.swiftRepository: TEAM-Cherrish/Cherrish-iOS
Length of output: 269
API 응답 키 형식과의 매핑이 명시되지 않아 디코딩 실패 위험.
daysSinceSignup은 서버 응답이 snake_case 형식(예: days_since_signup)일 경우 디코딩에 실패합니다. NetworkService에서 JSONDecoder()를 기본 설정으로 사용하고 있어서 키명이 정확히 일치해야 하는데, 실제 API 응답 형식이 코드에 명시되지 않았습니다. CodingKeys를 추가하거나 keyDecodingStrategy를 설정하여 안전하게 처리해주세요.
수정안
struct UserInfoResponseDTO: Decodable {
let name: String
let daysSinceSignup: Int
+
+ private enum CodingKeys: String, CodingKey {
+ case name
+ case daysSinceSignup = "days_since_signup"
+ }
}📝 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.
| struct UserInfoResponseDTO: Decodable { | |
| let name: String | |
| let daysSinceSignup: Int | |
| } | |
| extension UserInfoResponseDTO { | |
| func toEntity() -> UserInfoEntity { | |
| .init(name: name, daysSinceSignup: daysSinceSignup) | |
| } | |
| struct UserInfoResponseDTO: Decodable { | |
| let name: String | |
| let daysSinceSignup: Int | |
| private enum CodingKeys: String, CodingKey { | |
| case name | |
| case daysSinceSignup = "days_since_signup" | |
| } | |
| } | |
| extension UserInfoResponseDTO { | |
| func toEntity() -> UserInfoEntity { | |
| .init(name: name, daysSinceSignup: daysSinceSignup) | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Model/UserInfoResponseDTO.swift` around lines
10 - 18, The DTO currently risks decoding failures because API uses snake_case
keys; update UserInfoResponseDTO to explicitly map days_since_signup to
daysSinceSignup by adding a CodingKeys enum (case name, daysSinceSignup =
"days_since_signup") inside the struct so Decodable uses the correct key, and
keep the toEntity() conversion unchanged; alternatively ensure NetworkService's
JSONDecoder uses keyDecodingStrategy = .convertFromSnakeCase if you prefer a
global fix.
| func fetchUserInfo() async throws -> UserInfoEntity { | ||
| let userID: Int = userDefaultService.load(key: .userID) ?? 1 | ||
| let response = try await networkService.request( | ||
| MyPageAPI.users(userID: userID), | ||
| decodingType: UserInfoResponseDTO.self) | ||
|
|
||
| return response.toEntity() |
There was a problem hiding this comment.
userID 기본값 1은 타 사용자 정보 노출 위험.
Line 23에서 userID가 없을 때 1로 대체하면 다른 유저의 정보를 가져올 수 있습니다. 필수 값이 없으면 오류를 던지거나 상위로 처리하도록 변경하세요.
🔧 권장 수정안
func fetchUserInfo() async throws -> UserInfoEntity {
- let userID: Int = userDefaultService.load(key: .userID) ?? 1
+ guard let userID: Int = userDefaultService.load(key: .userID) else {
+ throw MyPageRepositoryError.missingUserID
+ }
let response = try await networkService.request(
MyPageAPI.users(userID: userID),
decodingType: UserInfoResponseDTO.self)
return response.toEntity()
}
+
+enum MyPageRepositoryError: Error {
+ case missingUserID
+}🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Data/Repository/MyPageRepository.swift` around
lines 22 - 28, In fetchUserInfo(), stop defaulting userID to 1
(userDefaultService.load(key: .userID) ?? 1) because it can fetch another user's
data; instead validate the optional result from userDefaultService.load(key:
.userID) and if missing either throw a clear error (e.g., define and throw a
MissingUserID error) or propagate a failure to the caller before calling
MyPageAPI.users(userID:), ensuring the function does not proceed with a
hardcoded fallback.
| struct DefaultFetchUserInfoUserCase: FetchUserInfoUseCase { | ||
| private let repository: MyPageInterface | ||
|
|
||
| init(repository: MyPageInterface) { | ||
| self.repository = repository | ||
| } | ||
|
|
||
| func execute() async throws -> UserInfoEntity { | ||
| return try await repository.fetchUserInfo() |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
타입명 오타(UserCase) 정리 권장.
Line 14의 DefaultFetchUserInfoUserCase는 UseCase 오타로 보입니다. 추후 혼동을 줄이기 위해 DefaultFetchUserInfoUseCase로 정리하는 것을 권장합니다(관련 참조들도 함께 변경).
♻️ 권장 수정안
-struct DefaultFetchUserInfoUserCase: FetchUserInfoUseCase {
+struct DefaultFetchUserInfoUseCase: FetchUserInfoUseCase {🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchUserInfoUseCase.swift` around
lines 14 - 22, Rename the struct DefaultFetchUserInfoUserCase to
DefaultFetchUserInfoUseCase throughout the codebase and update all references
(including initializers, injections, protocol conformance to
FetchUserInfoUseCase, and any places that instantiate or type-annotate it) so
the identifier matches the corrected spelling; ensure the initializer
init(repository: MyPageInterface), the execute() async throws -> UserInfoEntity
method, and any tests or DI registrations are updated to the new name to avoid
compilation errors or mismatches.
| @StateObject var viewModel: MyPageViewModel | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
주입된 뷰모델은 @ObservedObject(또는 명시적 StateObject 초기화) 권장.
Line 11-12의 @StateObject는 외부에서 주입된 인스턴스를 부모가 교체할 때 업데이트가 무시될 수 있습니다. 주입된 객체라면 @ObservedObject로 바꾸거나, init에서 _viewModel = StateObject(wrappedValue:)로 명시적으로 소유권을 선언하는 쪽이 안전합니다.
♻️ 권장 수정안
- `@StateObject` var viewModel: MyPageViewModel
+ `@ObservedObject` var viewModel: MyPageViewModel또는
`@StateObject` private var viewModel: MyPageViewModel
init(viewModel: MyPageViewModel) {
_viewModel = StateObject(wrappedValue: viewModel)
}🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/MyPage/MyPageView.swift`
around lines 11 - 12, 현재 MyPageView에 외부에서 주입된 `@StateObject` var viewModel:
MyPageViewModel 사용은 부모가 인스턴스를 교체할 때 업데이트를 놓칠 수 있으니, 주입된 뷰모델이면 선언을
`@ObservedObject로` 변경하거나 뷰가 소유해야 할 경우에는 private `@StateObject로` 만들고 init(viewModel:
MyPageViewModel)에서 _viewModel = StateObject(wrappedValue: viewModel)로 명시적으로
초기화하여 소유권을 선언하십시오; 관련 심볼: MyPageView, MyPageViewModel, `@StateObject`,
`@ObservedObject`, init, _viewModel = StateObject(wrappedValue:).
There was a problem hiding this comment.
요 방법이 맞는 거 같기는 한데 한번 확인 부탁드려요!
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/MyPage/MyPageViewModel.swift
Show resolved
Hide resolved
| guard let fetchUserInfoUseCase = DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) else { | ||
| return | ||
| } | ||
|
|
||
| DIContainer.shared.register(type: MyPageViewModel.self) { | ||
| return MyPageViewModel(fetchUserInfoUseCase: fetchUserInfoUseCase) | ||
| } |
There was a problem hiding this comment.
DI 실패 로그 누락으로 원인 추적이 어려움
fetchUserInfoUseCase가 nil일 때 조용히 반환해 이후 makeMyPageView()에서 fatalError로 이어질 수 있습니다. 다른 DI 실패와 동일하게 로그를 남겨 원인 파악 가능하게 해주세요.
🛠️ 제안 수정안
- guard let fetchUserInfoUseCase = DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) else {
- return
- }
+ guard let fetchUserInfoUseCase = DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) else {
+ CherrishLogger.error(CherrishError.DIFailedError)
+ return
+ }📝 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.
| guard let fetchUserInfoUseCase = DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) else { | |
| return | |
| } | |
| DIContainer.shared.register(type: MyPageViewModel.self) { | |
| return MyPageViewModel(fetchUserInfoUseCase: fetchUserInfoUseCase) | |
| } | |
| guard let fetchUserInfoUseCase = DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) else { | |
| CherrishLogger.error(CherrishError.DIFailedError) | |
| return | |
| } | |
| DIContainer.shared.register(type: MyPageViewModel.self) { | |
| return MyPageViewModel(fetchUserInfoUseCase: fetchUserInfoUseCase) | |
| } |
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift`
around lines 60 - 66, The guard early-returns silently when
DIContainer.shared.resolve(type: FetchUserInfoUseCase.self) yields nil, which
later leads to a fatalError in makeMyPageView(); update the guard to log an
error (using the same logger/mechanism used for other DI failures) that includes
the resolved type and context before returning so the DI failure is visible and
traceable; ensure the log references fetchUserInfoUseCase,
DIContainer.shared.resolve, MyPageViewModel and makeMyPageView() to make the
cause clear.
| struct DefaultFetchUserInfoUserCase: FetchUserInfoUseCase { | ||
| private let repository: MyPageInterface | ||
|
|
||
| init(repository: MyPageInterface) { | ||
| self.repository = repository | ||
| } | ||
|
|
||
| func execute() async throws -> UserInfoEntity { | ||
| return try await repository.fetchUserInfo() |
| @StateObject var viewModel: MyPageViewModel | ||
|
|
There was a problem hiding this comment.
요 방법이 맞는 거 같기는 한데 한번 확인 부탁드려요!
Cherrish-iOS/Cherrish-iOS/Presentation/Feature/MyPage/MyPageViewModel.swift
Show resolved
Hide resolved
sum130
left a comment
There was a problem hiding this comment.
굿입니다용 재선이가 리뷰남긴 것들!! 저도 눈 이모지 남겨놨어요 함 확인해주세용
c3e0cb8 to
366c107
Compare

🔗 연결된 이슈
📄 작업 내용