Conversation
|
Caution Review failedThe pull request is closed. Walkthrough앱에 Home/Detail 프레젠테이션 모듈과 관련 네트워크·도메인·레포지토리·유스케이스·테스트 인프라를 대규모로 추가하고, 빌드 설정(BASE_URL)과 워크스페이스/스킴/프로젝트 구성을 확장했습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant HomeV as HomeView
participant HomeR as HomeReducer
participant HomeC as HomeCoordinator
participant DetailV as DetailView
participant DetailR as DetailReducer
participant UseCase as MusicSearchUseCase
participant Repo as Repository
participant API as MusicSearchService/API
U->>HomeV: 진입(onAppear)
HomeV->>HomeR: .view(.onAppear)
HomeR->>UseCase: fetchAll / fetchMusic(season)
UseCase->>Repo: searchMusic(query)
Repo->>API: 네트워크 요청 (MusicSearchService.searchMusic)
API-->>Repo: DTO (ITunesSearchResponseDTO)
Repo-->>UseCase: [ITunesTrack]
UseCase-->>HomeR: [MusicItem] (매핑 완료)
HomeR-->>HomeV: 상태 업데이트 (음악 리스트)
U->>HomeV: 음악 카드 탭
HomeV->>HomeR: .navigation(.musicCardTapped)
HomeR->>HomeC: 라우팅(Detail push)
HomeC->>DetailV: 네비게이트 (새 경로)
DetailV->>DetailR: .view(.onAppear)
DetailR->>MusicDetailUseCase: fetchTrackDetail(id)
MusicDetailUseCase->>Repo: fetchDetailMusic(id)
Repo->>API: 네트워크 요청 (detail)
API-->>Repo: DTO
Repo-->>MusicDetailUseCase: [ITunesTrack]
MusicDetailUseCase-->>DetailR: MusicItem
DetailR-->>DetailV: 상태 업데이트 (상세 표시)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes 주의 요지:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (5)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 25
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
Projects/Presentation/Detail/DetailTests/Sources/Test.swift (1)
1-7: 파일 헤더의 프로젝트명이 올바르지 않습니다.헤더 코멘트에 "DDDAttendance"가 명시되어 있으나, 현재 프로젝트는 "MusicBandscape"입니다. 다른 프로젝트에서 복사한 것으로 보입니다.
다음과 같이 수정하세요:
// // base.swift -// DDDAttendance +// MusicBandscape // // Created by Roy on 2025-10-27 // Copyright © 2025 DDD , Ltd. All rights reserved. //Projects/Core/Core/Core.xcodeproj/project.pbxproj (1)
1007-1019: Release 빌드에서 매크로 플러그인 수동 로드(-load-plugin-executable) 제거 필요Release 구성에도 Debug 경로의 플러그인 바이너리를 로드하고 있어 CI/배포에서 실패 위험이 큽니다. Debug 전용으로 한정하거나 SPM/Plugin 설정으로 이관하세요.
예시(Release 구성에서 제거):
- "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro", - "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/CasePathsMacros#CasePathsMacros", - "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/ComposableArchitectureMacros#ComposableArchitectureMacros", - "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/DependenciesMacrosPlugin#DependenciesMacrosPlugin", - "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/PerceptionMacros#PerceptionMacros", - "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/WeaveDIMacros#WeaveDIMacros",Also applies to: 1193-1204, 1321-1332, 1499-1510, 1561-1572, 1633-1638
Projects/App/Derived/InfoPlists/MusicBandscape-Info.plist (2)
36-40: ATS(NSAppTransportSecurity) 전면 허용 해제 필요NSAllowsArbitraryLoads=true 는 보안/심사 리스크가 큽니다. 사용 도메인이 전부 HTTPS라면 해당 키를 제거하세요. 예외가 필요하면 도메인별 예외로 한정하세요.
예시(전면 허용 제거):
- <key>NSAppTransportSecurity</key> - <dict> - <key>NSAllowsArbitraryLoads</key> - <true/> - </dict> + <!-- HTTPS만 사용 시 ATS 설정 제거(기본 차단 정책 유지) -->도메인별 예외가 필요하다면:
<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSIncludesSubdomains</key><true/> <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key><false/> <key>NSTemporaryExceptionMinimumTLSVersion</key><string>TLSv1.2</string> </dict> </dict> </dict>
68-70: UIRequiredDeviceCapabilities ‘armv7’는 iOS 16+에서 부적절배포 타깃(iOS 16.6) 기준 arm64만 지원됩니다. armv7 명시는 불필요/오류 가능. arm64로 교체 또는 키 제거를 권장.
- <key>UIRequiredDeviceCapabilities</key> - <array> - <string>armv7</string> - </array> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array>Projects/App/MusicBandscape.xcodeproj/project.pbxproj (1)
1289-1308: Swift 매크로 플러그인 경로가 모든 구성에서 Debug로 하드코딩됨 (Prod/Stage 빌드 실패)검증 결과, 다음과 같이 확인됨:
- 라인 1289-1308: Prod 구성 (
MusicBandscape-Prod-Info.plist)- 라인 1349-1362: Stage 구성 (
MusicBandscape-Stage-Info.plist)- 라인 1595-1607, 1765-1777: Debug 구성
모든 위치에서
$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/경로가 하드코딩되어 있으므로, Prod와 Stage 빌드는 Debug 폴더에서 플러그인을 찾으려 시도해 실패합니다.다음 블록들을
$(CONFIGURATION)변수로 수정하세요 (모든 위치, 모든 매크로):- "-load-plugin-executable", - "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/CasePathsMacros#CasePathsMacros", + "-load-plugin-executable", + "$BUILD_DIR/$(CONFIGURATION)$EFFECTIVE_PLATFORM_NAME/CasePathsMacros#CasePathsMacros",
ComposableArchitectureMacros,DependenciesMacrosPlugin,LogMacroMacro,PerceptionMacros,WeaveDIMacros에도 동일하게 적용하세요.Projects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxproj (2)
746-764: 플러그인 로드 경로가 Debug로 하드코딩되어 Release 빌드 실패 위험
-load-plugin-executable경로가$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/...로 고정되어 Release에서 플러그인을 못 찾습니다. 구성값을 참조하도록 수정하세요.권장 수정:
- "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/CasePathsMacros#CasePathsMacros", + "$BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/CasePathsMacros#CasePathsMacros",위와 동일하게 모든 매크로 플러그인 항목(ComposableArchitectureMacros, DependenciesMacrosPlugin, LogMacroMacro, PerceptionMacros, WeaveDIMacros)에 적용하세요.
Also applies to: 1059-1070, 1122-1133, 1185-1196, 1245-1256, 1305-1316
771-774: Swift 5.0 설정은 ComposableArchitecture 매크로와 호환되지 않습니다 — Swift 5.9+로 업데이트 필수코드베이스에서
@Reducer매크로가 여러 파일에서 활발히 사용 중이며, Swift 매크로는 Swift 5.9+ 버전을 필요로 합니다. 현재 설정된 Swift 5.0은 이 매크로들과 호환되지 않습니다.10개 위치 모두에서
SWIFT_VERSION = 5.0을SWIFT_VERSION = 5.9이상으로 업데이트하세요:
- 773, 840, 952, 1017, 1080, 1143, 1210, 1265, 1326, 1393줄
모든 타깃(UseCase, Debug, Prod, Stage, Tests)에 동일하게 적용하세요. compatibilityVersion도 검토 필요합니다.
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj (1)
526-533: 테스트 타겟으로 Alamofire 번들만 복사됨런타임에 리소스 접근이 필요한 경로를 재검토하세요. 필요 시 App 타겟으로 이동.
♻️ Duplicate comments (2)
Config/Release.xcconfig (1)
13-13: BASE_URL에 프로토콜이 누락되었습니다.Dev.xcconfig와 동일하게 프로토콜(
https://)이 누락되어 있습니다. 모든 설정 파일에서 일관되게 수정이 필요합니다.Config/Prod.xcconfig (1)
13-13: BASE_URL에 프로토콜이 누락되었습니다.다른 설정 파일들과 동일하게 프로토콜이 누락되어 있습니다.
🧹 Nitpick comments (37)
Projects/Presentation/Home/HomeTests/Sources/Test.swift (1)
1-8: 파일 내용이 비어있음 - 테스트 코드 확인 필요PR 목표에 따르면 "use case and repository tests" 구현이 포함되어야 하는데, 이 파일에는 헤더만 있고 실제 테스트 코드가 없습니다. 이것이 의도적인 템플릿 파일인지, 아니면 테스트 구현이 누락되었는지 확인하세요.
테스트 코드를 작성하시려면 도움을 드릴 수 있습니다.
Projects/Presentation/Detail/Sources/View/Component/NavigationArrowButton.swift (3)
12-20: 초기화 패턴을 단순화할 수 있습니다.
backAction에 기본 빈 클로저가 불필요합니다.init에서 항상 값을 제공하므로 기본값을 제거할 수 있습니다.다음과 같이 개선할 수 있습니다:
- private var backAction: () -> Void = {} + private var backAction: () -> Void public init( backAction: @escaping () -> Void ) { self.backAction = backAction }
28-28: 불필요한 font modifier를 제거하세요.
Image에.font()modifier는 효과가 없습니다. Font modifier는Text뷰에 적용됩니다.Image의 크기는 이미.frame()으로 설정되어 있습니다.다음 diff를 적용하여 불필요한 modifier를 제거하세요:
Image(systemName: "chevron.left") .resizable() .scaledToFit() .frame(width: 12, height: 20) - .font(.pretendardFont(family: .semiBold, size: 20)) .foregroundStyle(.gray) .onTapGesture(perform: backAction)
22-37: 접근성 개선을 고려하세요.현재
Image+onTapGesture를 사용하는 방식은 다음과 같은 접근성 문제가 있습니다:
- VoiceOver 사용자를 위한 적절한 버튼 역할(role)과 특성(traits) 부족
- 키보드 네비게이션 지원 미흡
- 접근성 레이블 누락
Button을 사용하여 접근성을 개선하는 것을 권장합니다.다음과 같이 개선할 수 있습니다:
public var body: some View { HStack { - Image(systemName: "chevron.left") - .resizable() - .scaledToFit() - .frame(width: 12, height: 20) - .font(.pretendardFont(family: .semiBold, size: 20)) - .foregroundStyle(.gray) - .onTapGesture(perform: backAction) + Button(action: backAction) { + Image(systemName: "chevron.left") + .resizable() + .scaledToFit() + .frame(width: 12, height: 20) + .foregroundStyle(.gray) + } + .accessibilityLabel("뒤로 가기") Spacer() } .padding(.horizontal, 16) }Projects/Presentation/Detail/Detail.xcodeproj/xcshareddata/xcschemes/Detail-Prod.xcscheme (1)
25-33: Debug에선 Main Thread Checker 유지 권장disableMainThreadChecker="YES"는 UI 스레드 위반을 놓칠 수 있습니다. Prod 스킴이라도 Debug 구성에서는 활성화를 권장합니다.
MusicBandscape.xcworkspace/contents.xcworkspacedata (1)
76-90: ‘Utill’ 철자(LL) — 네이밍 정합성 확인Shared/Utill 모듈 철자가 일반 관례(“Util”)와 달라 검색/탐색 시 혼선이 생길 수 있습니다. 추후 리네임 계획 여부 검토를 권장합니다(전체 스킴/의존성 영향 점검 필요).
MusicBandscape.xcworkspace/xcshareddata/xcschemes/MusicBandscape-Workspace.xcscheme (2)
2466-2483: 워크스페이스 스킴 실행 대상(Run) 미설정LaunchAction이 API.framework에 MacroExpansion만 지정되어 앱 실행이 불가능합니다. 실행용 스킴이라면 MusicBandscape.app로 Runnable 지정 권장(아니면 “집계용 스킴”으로만 사용). Tuist 사용 시 스킴 설정은 Tuist에서 관리 권장.
아래와 같이 LaunchAction을 교체 예시:
- <LaunchAction - buildConfiguration="Debug" - selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" - selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" - launchStyle="0" - useCustomWorkingDirectory="NO" - ignoresPersistentStateOnLaunch="NO" - debugDocumentVersioning="YES" - debugServiceExtension="internal" - allowLocationSimulation="YES"> - <MacroExpansion> - <BuildableReference - BuildableIdentifier="primary" - BlueprintIdentifier="4D827AB669FBF5A68B49E434" - BuildableName="API.framework" - BlueprintName="API" - ReferencedContainer="container:Projects/Core/Data/API/API.xcodeproj"> - </BuildableReference> - </MacroExpansion> - </LaunchAction> + <LaunchAction + buildConfiguration="Debug" + selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle="0" + useCustomWorkingDirectory="NO" + ignoresPersistentStateOnLaunch="NO" + debugDocumentVersioning="YES" + debugServiceExtension="internal" + allowLocationSimulation="YES"> + <BuildableProductRunnable runnableDebuggingMode="0"> + <BuildableReference + BuildableIdentifier="primary" + BlueprintIdentifier="39535AE41AA9044E866A97B7" + BuildableName="MusicBandscape.app" + BlueprintName="MusicBandscape" + ReferencedContainer="container:Projects/App/MusicBandscape.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction>
2287-2297: 테스트 병렬화 가능 시 parallelizable="YES" 권장(DetailTests/HomeTests)테스트간 격리가 보장된다면 병렬화로 CI 시간을 줄일 수 있습니다.
Also applies to: 2332-2341
Projects/Core/Core/Core.xcodeproj/project.pbxproj (2)
241-252: 테스트 번들 복사 단계(Dependencies) 중복/불필요 가능성테스트 타겟에서 리소스 번들은 Resources 단계에 이미 추가되어 있습니다. Dependencies CopyFiles(특히 dstSubfolderSpec=16) 는 중복/위치 부정확 가능성이 있어 제거를 권장합니다.
40-41: CFNetwork 명시적 링크는 대개 불필요현대 iOS SDK에선 Foundation/URLSession 경유 시 별도 링크 없이도 충분합니다. 유지해도 문제는 없으나 불필요 의존은 정리 권장.
Also applies to: 100-101, 342-346
Projects/Core/Network/Networking/Networking.xcodeproj/project.pbxproj (3)
136-145: Alamofire_Alamofire.bundle 이중 추가 가능성NetworkingTests 타겟에서 Dependencies 복사 단계와 Resources 둘 다에 같은 번들을 추가했습니다. 하나로 통일하세요(보통 Resources로 충분).
Also applies to: 470-476
281-288: 그룹명 오타: ‘Exorted’ → ‘Exported’그룹명 오타는 탐색 혼란을 유발합니다. ‘Exported’로 정정 권장.
-9D14E7A747B12AB9A274617A /* Exorted */ +9D14E7A747B12AB9A274617A /* Exported */
180-192: OSLog/CFNetwork 명시적 링크 최소화 검토불필요시 제거해 링크 표면적을 줄이세요.
Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj (1)
49-58: Dependencies CopyFiles 단계 과다AsyncMoya/Alamofire 번들/프레임워크를 여러 CopyFiles 단계로 분산 복사 중입니다. 실제 필요한 타겟에 최소 단계만 유지하세요(일반적으로 Resources/Embed Frameworks로 충분).
Also applies to: 60-68, 70-78, 109-118, 140-148
Projects/App/Derived/InfoPlists/MusicBandscape-Info.plist (1)
77-78: 강제 Light 모드는 선택 사항디자인 요구가 아니라면 시스템 다크 모드 존중을 검토하세요.
Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Project+Templete/Project+Template.swift (1)
210-213: 구성(configuration)이 "Debug"로 하드코딩되어 있습니다.모든 액션에서 "Debug" 구성을 사용하고 있습니다. 향후 다른 구성에서도 테스트 플랜을 실행해야 할 경우를 대비해
target파라미터를 활용하는 것을 고려해보세요.필요한 경우 다음과 같이 수정할 수 있습니다:
public static func makeTestPlanScheme(target: ConfigurationName, name: String) -> Scheme { return Scheme.scheme( name: name, shared: true, buildAction: .buildAction(targets: ["\(name)", "\(name)Tests"]), testAction: .testPlans(["\(name)Tests/Sources/\(name)TestPlan.xctestplan"]), - runAction: .runAction(configuration: "Debug"), - archiveAction: .archiveAction(configuration: "Debug"), - profileAction: .profileAction(configuration: "Debug"), - analyzeAction: .analyzeAction(configuration: "Debug") + runAction: .runAction(configuration: target), + archiveAction: .archiveAction(configuration: target), + profileAction: .profileAction(configuration: target), + analyzeAction: .analyzeAction(configuration: target) ) }Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home.xcscheme (1)
3-3: Xcode 버전이 오래되었습니다.
LastUpgradeVersion = "1010"은 Xcode 10.1을 나타냅니다. 최신 Xcode 버전으로 scheme을 업그레이드하는 것을 권장합니다.Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home-Prod.xcscheme (1)
3-3: Xcode 버전이 오래되었습니다.
LastUpgradeVersion = "1010"은 Xcode 10.1을 나타냅니다. 최신 Xcode 버전으로 업그레이드하는 것을 권장합니다.Projects/Core/Data/Repository/RepositoryTests/Sources/MusicSearchRepositoryTests.swift (1)
27-43: 중복된 목 데이터를 헬퍼 함수로 추출할 수 있습니다.두 테스트에서 동일한 ITunesTrack 목 데이터를 생성하고 있습니다. ITunesTrack에 이미
.mock()팩토리 메서드가 있다면 이를 활용하거나, 테스트 파일에 헬퍼 함수를 추가하여 중복을 제거할 수 있습니다.다음과 같이 리팩토링할 수 있습니다:
// 테스트 suite 내에 헬퍼 함수 추가 private func createMockTrack() -> ITunesTrack { .init( wrapperType: "track", kind: "song", artistId: 1, collectionId: 10, trackId: 100, artistName: "IU", collectionName: "Spring Album", trackName: "Spring Love", previewUrl: URL(string: "https://example.com/prev.m4a"), artworkUrl100: URL(string: "https://example.com/art.jpg"), releaseDate: "2024-03-18T12:00:00Z", primaryGenreName: "K-Pop", collectionCensoredName: "봄 사랑 벚꽃 말고 - Single" ) }또는 ITunesTrack.mock() 메서드가 이미 존재한다면:
let mockTracks: [Model.ITunesTrack] = [.mock(id: 100)]Also applies to: 81-97
Projects/Core/Data/Service/Sources/MusicSearchService.swift (2)
22-29: 불필요한 바인딩 제거
.detailMusic(let id)에서id를 사용하지 않습니다. 경고 제거를 위해 패턴 바인딩을 제거하세요.적용 예:
- switch self { - case .searchMusic: - return .searchMusic - case .detailMusic(let id): - return .searchDetailMusic - } + switch self { + case .searchMusic: + return .searchMusic + case .detailMusic: + return .searchDetailMusic + }
45-55:country하드코딩 → 구성/로케일 기반으로 전환 권장KR 고정 값은 글로벌/빌드 타깃 간 불일치 위험이 있습니다. 빌드 설정(예: Info.plist/xcconfig) 또는
Locale.current.region?.identifier기반으로 주입하세요.예시:
- "country": "KR", + "country": AppConfig.countryCode, // xcconfig 또는 DI로 주입또는
let country = Locale.current.region?.identifier ?? "KR"Also applies to: 56-62
Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home-Debug.xcscheme (1)
25-33: Debug에서 Main Thread Checker 비활성화 해제 권장
disableMainThreadChecker="YES"는 UI/동시성 버그 탐지를 약화시킵니다. Debug 스킴에서는NO로 두는 것을 권장합니다.- disableMainThreadChecker = "YES"> + disableMainThreadChecker = "NO">Projects/App/MusicBandscape.xcodeproj/project.pbxproj (1)
1498-1501: 링커 플래그-all_load제거 권장
-all_load는 불필요한 기호 로드로 바이너리 팽창/중복 심볼 충돌 위험을 높입니다. 일반적으로-ObjC만으로 충분합니다(정적 라이브러리 카테고리 로딩 목적).예시:
- OTHER_LDFLAGS = "-ObjC -all_load"; + OTHER_LDFLAGS = "-ObjC";해당 설정이 필요한 특정 라이브러리가 없다면 전 구성에서 제거를 권장합니다.
Also applies to: 2299-2303, 2370-2373, 2610-2613
Projects/Core/Domain/Entity/Sources/MusicMockItem.swift (1)
11-16: 카테고리별 목데이터 사용성을 높이는 경량 API 제안Season → [MusicItem] 매핑 혹은 팩토리 메서드로 접근점을 하나로 모으면 호출부가 단순해집니다.
예:
public enum Season { case spring, summer, autumn, winter, coda } public extension MusicItem { static func seasonal(_ s: Season) -> [MusicItem] { switch s { case .spring: return springMockItems case .summer: return summerMockItems case .autumn: return autumnMockItems case .winter: return winterMockItems case .coda: return codaMock } } }Also applies to: 105-110, 208-214, 311-316, 414-420
Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (1)
10-16: 테스트 병렬 실행 대비: 스레드 안전성과 Sendable 개선을 권장합니다.
receivedQueries와result를 여러 Task에서 접근하면 경쟁 상태가 날 수 있습니다. actor로 감싸면 간단히 해소됩니다.아래처럼 전환하면 호출부 변경 없이 안전성이 올라갑니다.
- public class MockMusicSearchRepository: MusicSearchRepositoryProtocol { + public final actor MockMusicSearchRepository: MusicSearchRepositoryProtocol { - public var result: Result<[Model.ITunesTrack], Error> - public private(set) var receivedQueries: [String] = [] + public var result: Result<[Model.ITunesTrack], Error> + public private(set) var receivedQueries: [String] = [] - public init(result: Result<[Model.ITunesTrack], Error> = .success([])) { - self.result = result - } + public init(result: Result<[Model.ITunesTrack], Error> = .success([])) { + self.result = result + } - public func setResult(_ newResult: Result<[Model.ITunesTrack], Error>) { - self.result = newResult - } + public func setResult(_ newResult: Result<[Model.ITunesTrack], Error>) { + self.result = newResult + } - public func fetchMusic(search: String) async throws -> [Model.ITunesTrack] { - self.receivedQueries.append(search) - return try result.get() - } + public func fetchMusic(search: String) async throws -> [Model.ITunesTrack] { + self.receivedQueries.append(search) + return try result.get() + }추가로 테스트 편의용
reset()(queries 초기화)도 고려해 주세요.Also applies to: 21-23, 25-33
Projects/Presentation/Detail/Sources/View/Component/ScalingHeaderDetailView.swift (2)
66-89: URL이 nil일 때 무한 Skeleton 표시 — 실패(혹은 플레이스홀더)로 폴백 필요
headerURL == nil이면AsyncImage가.empty상태에서 계속 머무를 수 있습니다. 로딩이 끝났는데도 이미지를 못 받아온 경우엔 아이콘/플레이스홀더로 폴백하세요.다음과 같이 nil 분기를 먼저 처리하면 명확합니다:
@@ @ViewBuilder private var headerContent: some View { if isLoading { SkeletonView(width: nil, height: headerHeight) - } else { - AsyncImage(url: headerURL) { phase in + } else if let url = headerURL { + AsyncImage(url: url) { phase in switch phase { case .empty: SkeletonView(width: nil, height: headerHeight) case .success(let image): image .resizable() .scaledToFill() case .failure: Image(systemName: "music.note") .resizable() .scaledToFit() .foregroundColor(.gray) @unknown default: Color.clear } } + } else { + Image(systemName: "music.note") + .resizable() + .scaledToFit() + .foregroundColor(.gray) } }
33-46: .global 좌표계 의존 — 네임드 스크롤 좌표계로 전환 권장
GeometryReader에서.frame(in: .global)은 상위 컨텍스트 변화에 취약합니다. 스크롤 뷰에 네임드 좌표계를 부여하고 그 좌표계를 사용하면 안정적입니다.@@ - ScrollView(showsIndicators: false) { + ScrollView(showsIndicators: false) { VStack(spacing: 0) { GeometryReader { geo in - let offset = geo.frame(in: .global).minY + let offset = geo.frame(in: .named("scroll")).minY let height = offset > 0 ? headerHeight + offset : headerHeight headerContent .frame(width: outerGeo.size.width, height: height) .clipped() .offset(y: offset > 0 ? -offset : 0) } .frame(height: headerHeight) // 헤더 영역 고정 @@ - } + } + .coordinateSpace(name: "scroll")Also applies to: 54-54
Projects/Presentation/Home/Sources/Coordinator/Reducer/HomeCoordinator.swift (1)
21-27: 공개 API 의도에 따라routes접근 제어 재검토 제안
State는public인데routes는 기본 내부 접근입니다. 외부 모듈에서 초기 라우트 검사/프리시딩이 필요하다면public private(set)으로 노출을 고려할 수 있습니다. 현재 모듈 내부에서만 사용한다면 현 상태 유지 OK.Projects/Core/Network/Foundations/Sources/DomainType.swift (1)
41-46: 헤더 캐시를 불변으로 고정
APIHeaders.cached가var이면 런타임에 우발적 변경 및 데이터 레이스 소지가 있습니다. 불변let으로 고정하세요.-enum APIHeaders { - static var cached: [String: String] = [ +enum APIHeaders { + static let cached: [String: String] = [ "Content-Type": "application/json" ] }Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (3)
21-25: nonisolated 수식자 불필요struct 컨텍스트의 init에 nonisolated는 의미가 없습니다(actors에서 격리 해제 시 사용). 제거하여 가독성을 높이세요.
- nonisolated public init( + public init( repository: MusicSearchRepositoryProtocol ) { self.repository = repository }
32-39: 빈 결과를 badServerResponse로 매핑하는 것은 과도함결과가 비어있는 경우는 “NotFound” 성격입니다. 네트워크/서버 결함을 의미하는 URLError(.badServerResponse) 대신 도메인 에러(예: MusicSearchError.notFound)로 표현하는 게 적절합니다.
필요 시 간단한 도메인 에러 타입 추가를 제안할 수 있습니다.
27-30: 반환 타입 표기 일관성searchMusic은 [Entity.MusicItem], fetchTrackDetail은 MusicItem을 반환합니다. 모듈 임포트가 되어 있으므로 둘 다 MusicItem으로 통일하면 API 표면이 깔끔해집니다.
- public func searchMusic(searchQuery: String) async throws -> [Entity.MusicItem] { + public func searchMusic(searchQuery: String) async throws -> [MusicItem] {Also applies to: 32-33
Projects/Core/Data/API/API.xcodeproj/project.pbxproj (1)
55-64: Framework 타겟에서 타 프레임워크 복사(Dependencies phase) 지양Framework(게다가
MACH_O_TYPE = staticlib) 타겟에서 다른 프레임워크/번들을 Copy Files로 복사하는 패턴은 권장되지 않습니다. 임베드는 최종 App 타겟에서만 수행하고, 여기서는 링크만 하세요. 잘못된 복사는 중복/서명 문제를 유발합니다.
- Dependencies Copy Files phase 제거
- 필요 시 App 타겟의 “Embed Frameworks”로 이동
Also applies to: 75-84, 95-104, 105-114, 145-154
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (2)
31-34: iTunes 스키마 대비 과한 non-optional로 디코딩 실패 위험
collectionCensoredName, 일부 메타 필드는 응답에 누락될 수 있습니다. 현재 non-optional이라 전체 디코딩이 실패합니다. 안전하게 optional로 두는 것을 권장합니다. releaseDate도 포맷이 변동될 수 있으니 필요 시 String? 또는 Date 변환 레이어로 처리하세요.- public let releaseDate: String - public let primaryGenreName: String - public let collectionCensoredName: String + public let releaseDate: String + public let primaryGenreName: String + public let collectionCensoredName: String? ... - collectionCensoredName: String + collectionCensoredName: String?실제 샘플 JSON으로 누락 필드 케이스를 확인해 주세요.
Also applies to: 42-70
74-102: mock 팩토리의 기본값 하드코딩테스트 편의성은 좋으나, 호출자 측에서 덮어쓰기 어려운 값(artistId, collectionId 등)이 고정입니다. 필요 시 파라미터로 노출해 유연성을 확보하세요.
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj (1)
115-124: Framework 타겟에서의 Dependencies Copy Files 사용Repository 프레임워크 타겟에서 다른 프레임워크/번들을 복사하고 있습니다. 링크만 하고 임베드는 최종 App에서 처리하세요. 정적 프레임워크(
MACH_O_TYPE = staticlib) 설정과도 충돌 소지.Also applies to: 136-146, 148-158, 160-170
Projects/Core/Data/Service/Service.xcodeproj/project.pbxproj (1)
66-75: Framework 타겟에서 Dependencies Copy Files로 외부 프레임워크/번들 복사Service 프레임워크에서도 동일 패턴입니다. 링크만 유지하고 임베드는 최종 App에서 처리하세요.
Also applies to: 118-129, 130-141, 152-163
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (107)
Config/Dev.xcconfig(1 hunks)Config/Prod.xcconfig(1 hunks)Config/Release.xcconfig(1 hunks)Config/Stage.xcconfig(1 hunks)MultiModuleTemplate.xcworkspace/contents.xcworkspacedata(1 hunks)MultiModuleTemplate.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings(0 hunks)MusicBandscape.xcworkspace/contents.xcworkspacedata(2 hunks)MusicBandscape.xcworkspace/xcshareddata/xcschemes/MultiModuleTemplate-Workspace.xcscheme(0 hunks)MusicBandscape.xcworkspace/xcshareddata/xcschemes/MusicBandscape-Workspace.xcscheme(4 hunks)Plugins/DependencyPlugin/ProjectDescriptionHelpers/TargetDependency+Module/Modules.swift(1 hunks)Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Project+Templete/Project+Template.swift(8 hunks)Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Setting/Project+Settings.swift(1 hunks)Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/infoPlist/Project+InfoPlist.swift(1 hunks)Projects/App/Derived/InfoPlists/MusicBandscape-Debug-Info.plist(1 hunks)Projects/App/Derived/InfoPlists/MusicBandscape-Info.plist(1 hunks)Projects/App/Derived/InfoPlists/MusicBandscape-Prod-Info.plist(1 hunks)Projects/App/Derived/InfoPlists/MusicBandscape-Stage-Info.plist(1 hunks)Projects/App/MusicBandscape.xcodeproj/project.pbxproj(27 hunks)Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme(3 hunks)Projects/App/MusicBandscapeTests/Sources/MusicBandTestPlan.xctestplan(1 hunks)Projects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan(1 hunks)Projects/App/Project.swift(1 hunks)Projects/App/Sources/Di/DIRegistry.swift(3 hunks)Projects/App/Sources/Reducer/AppReducer.swift(4 hunks)Projects/App/Sources/View/AppView.swift(1 hunks)Projects/App/Sources/View/ContentView.swift(1 hunks)Projects/Core/Core/Core.xcodeproj/project.pbxproj(22 hunks)Projects/Core/Core/Sources/Exported/CoreExport.swift(1 hunks)Projects/Core/Data/API/API.xcodeproj/project.pbxproj(35 hunks)Projects/Core/Data/API/Derived/InfoPlists/API-Debug-Info.plist(2 hunks)Projects/Core/Data/API/Derived/InfoPlists/API-Info.plist(2 hunks)Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist(2 hunks)Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist(2 hunks)Projects/Core/Data/API/Project.swift(1 hunks)Projects/Core/Data/API/Sources/API/BaseAPI.swift(1 hunks)Projects/Core/Data/API/Sources/API/MusicScapeDomain.swift(1 hunks)Projects/Core/Data/API/Sources/API/MusicSearchAPI.swift(1 hunks)Projects/Core/Data/API/Sources/Base.swift(0 hunks)Projects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj(4 hunks)Projects/Core/Data/DataInterface/Sources/Base.swift(0 hunks)Projects/Core/Data/DataInterface/Sources/Exported/DataInterfaceExported.swift(1 hunks)Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift(1 hunks)Projects/Core/Data/Model/Model.xcodeproj/project.pbxproj(5 hunks)Projects/Core/Data/Model/Sources/Base.swift(0 hunks)Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift(1 hunks)Projects/Core/Data/Repository/Project.swift(1 hunks)Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj(31 hunks)Projects/Core/Data/Repository/RepositoryTests/Sources/MusicSearchRepositoryTests.swift(1 hunks)Projects/Core/Data/Repository/Sources/Base.swift(0 hunks)Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift(1 hunks)Projects/Core/Data/Repository/Sources/MusicSearchRepository.swift(1 hunks)Projects/Core/Data/Service/Project.swift(1 hunks)Projects/Core/Data/Service/Service.xcodeproj/project.pbxproj(33 hunks)Projects/Core/Data/Service/Sources/Base.swift(0 hunks)Projects/Core/Data/Service/Sources/MusicSearchService.swift(1 hunks)Projects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxproj(5 hunks)Projects/Core/Domain/DomainInterface/Sources/Base.swift(0 hunks)Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift(1 hunks)Projects/Core/Domain/Entity/Entity.xcodeproj/project.pbxproj(6 hunks)Projects/Core/Domain/Entity/Sources/Base.swift(0 hunks)Projects/Core/Domain/Entity/Sources/Mapper/Extension+ITunesTrack.swift(1 hunks)Projects/Core/Domain/Entity/Sources/MusicItemEntity.swift(1 hunks)Projects/Core/Domain/Entity/Sources/MusicMockItem.swift(1 hunks)Projects/Core/Domain/UseCase/Sources/Base.swift(0 hunks)Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift(1 hunks)Projects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxproj(15 hunks)Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift(1 hunks)Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj(34 hunks)Projects/Core/Network/Foundations/Project.swift(1 hunks)Projects/Core/Network/Foundations/Sources/Base.swift(0 hunks)Projects/Core/Network/Foundations/Sources/DomainType.swift(1 hunks)Projects/Core/Network/Foundations/Sources/NetworkError.swift(1 hunks)Projects/Core/Network/Networking/Networking.xcodeproj/project.pbxproj(25 hunks)Projects/Core/Network/Networking/Sources/Exorted/NetworkExported.swift(1 hunks)Projects/Presentation/Detail/Derived/InfoPlists/Detail-Debug-Info.plist(1 hunks)Projects/Presentation/Detail/Derived/InfoPlists/Detail-Info.plist(1 hunks)Projects/Presentation/Detail/Derived/InfoPlists/Detail-Prod-Info.plist(1 hunks)Projects/Presentation/Detail/Derived/InfoPlists/Detail-Stage-Info.plist(1 hunks)Projects/Presentation/Detail/Derived/InfoPlists/DetailTests-Info.plist(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/project.xcworkspace/contents.xcworkspacedata(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/xcshareddata/xcschemes/Detail-Debug.xcscheme(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/xcshareddata/xcschemes/Detail-Prod.xcscheme(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/xcshareddata/xcschemes/Detail-Stage.xcscheme(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/xcshareddata/xcschemes/Detail.xcscheme(1 hunks)Projects/Presentation/Detail/DetailTests/Sources/Test.swift(1 hunks)Projects/Presentation/Detail/Project.swift(1 hunks)Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift(1 hunks)Projects/Presentation/Detail/Sources/View/Component/NavigationArrowButton.swift(1 hunks)Projects/Presentation/Detail/Sources/View/Component/ScalingHeaderDetailView.swift(1 hunks)Projects/Presentation/Detail/Sources/View/Component/Skeleton/DetailSkeletonViews.swift(1 hunks)Projects/Presentation/Detail/Sources/View/DetailView.swift(1 hunks)Projects/Presentation/Home/Derived/InfoPlists/Home-Debug-Info.plist(1 hunks)Projects/Presentation/Home/Derived/InfoPlists/Home-Info.plist(1 hunks)Projects/Presentation/Home/Derived/InfoPlists/Home-Prod-Info.plist(1 hunks)Projects/Presentation/Home/Derived/InfoPlists/Home-Stage-Info.plist(1 hunks)Projects/Presentation/Home/Derived/InfoPlists/HomeTests-Info.plist(1 hunks)Projects/Presentation/Home/Home.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Home/Home.xcodeproj/project.xcworkspace/contents.xcworkspacedata(1 hunks)Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home-Debug.xcscheme(1 hunks)Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home-Prod.xcscheme(1 hunks)Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home-Stage.xcscheme(1 hunks)Projects/Presentation/Home/Home.xcodeproj/xcshareddata/xcschemes/Home.xcscheme(1 hunks)Projects/Presentation/Home/HomeTests/Sources/Test.swift(1 hunks)Projects/Presentation/Home/Project.swift(1 hunks)Projects/Presentation/Home/Sources/Coordinator/Reducer/HomeCoordinator.swift(1 hunks)Projects/Presentation/Home/Sources/Coordinator/View/HomeCoordinatorView.swift(1 hunks)
⛔ Files not processed due to max files limit (20)
- Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
- Projects/Presentation/Home/Sources/Main /Reducer/MusicSeason.swift
- Projects/Presentation/Home/Sources/Main /View/Component/MusicCardCarousel.swift
- Projects/Presentation/Home/Sources/Main /View/Component/MusicItemCard.swift
- Projects/Presentation/Home/Sources/Main /View/Component/MusicListSectionView.swift
- Projects/Presentation/Home/Sources/Main /View/Component/SearchBarView.swift
- Projects/Presentation/Home/Sources/Main /View/Component/SectionHeaderView.swift
- Projects/Presentation/Home/Sources/Main /View/Component/Skeleton/HomeSkeletonViews.swift
- Projects/Presentation/Home/Sources/Main /View/HomeView.swift
- Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj
- Projects/Presentation/Presentation/Project.swift
- Projects/Presentation/Presentation/Sources/Exported/PresentationExported.swift
- Projects/Presentation/Splash/Sources/Reducer/SplashFeature.swift
- Projects/Presentation/Splash/Splash.xcodeproj/project.pbxproj
- Projects/Presentation/Splash/SplashTests/Sources/SplashReducerTests.swift
- Projects/Shared/DesignSystem/DesignSystem.xcodeproj/project.pbxproj
- Projects/Shared/DesignSystem/Sources/Button/CircularArrowButton.swift
- Projects/Shared/DesignSystem/Sources/Color/Colors.swift
- Projects/Shared/DesignSystem/Sources/Skeleton/SkeletonView.swift
- WorkSpace.swift
💤 Files with no reviewable changes (11)
- Projects/Core/Data/Service/Sources/Base.swift
- Projects/Core/Data/API/Sources/Base.swift
- Projects/Core/Data/Model/Sources/Base.swift
- Projects/Core/Data/DataInterface/Sources/Base.swift
- Projects/Core/Domain/UseCase/Sources/Base.swift
- Projects/Core/Data/Repository/Sources/Base.swift
- Projects/Core/Network/Foundations/Sources/Base.swift
- MusicBandscape.xcworkspace/xcshareddata/xcschemes/MultiModuleTemplate-Workspace.xcscheme
- MultiModuleTemplate.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
- Projects/Core/Domain/DomainInterface/Sources/Base.swift
- Projects/Core/Domain/Entity/Sources/Base.swift
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-23T01:43:01.580Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj:85-114
Timestamp: 2025-10-23T01:43:01.580Z
Learning: In Tuist-managed iOS projects (identified by Tuist/ directory, Project.swift manifest files, and Tuist references in build settings), the .xcodeproj/project.pbxproj files are auto-generated and should not be manually edited. Build phases including 'Dependencies' Copy Files phases are generated by Tuist based on the Project.swift manifests, and manual changes will be overwritten on the next `tuist generate` run. Do not flag or suggest manual modifications to pbxproj files in Tuist projects.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojMusicBandscape.xcworkspace/contents.xcworkspacedata
🧬 Code graph analysis (16)
Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift (2)
Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (2)
fetchMusic(25-28)fetchDetailMusic(30-33)Projects/Core/Data/Repository/Sources/MusicSearchRepository.swift (2)
fetchMusic(19-22)fetchDetailMusic(24-27)
Projects/Core/Data/API/Sources/API/MusicScapeDomain.swift (1)
Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (1)
searchMusic(27-30)
Projects/Presentation/Detail/Sources/View/Component/NavigationArrowButton.swift (1)
Projects/Shared/DesignSystem/Sources/Font/PretendardFont.swift (2)
body(22-24)pretendardFont(43-46)
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift (2)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (2)
searchMusic(27-30)fetchTrackDetail(32-39)
Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift (1)
Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (2)
searchMusic(27-30)fetchTrackDetail(32-39)
Projects/Presentation/Home/Project.swift (1)
Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Project+Templete/Project+Template.swift (1)
makeAppModule(11-111)
Projects/Core/Data/Repository/Sources/MusicSearchRepository.swift (2)
Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (2)
fetchMusic(25-28)fetchDetailMusic(30-33)Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (1)
searchMusic(27-30)
Projects/Core/Data/Repository/RepositoryTests/Sources/MusicSearchRepositoryTests.swift (3)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (3)
setResult(21-23)fetchMusic(25-28)fetchDetailMusic(30-33)Projects/Core/Data/Repository/Sources/MusicSearchRepository.swift (2)
fetchMusic(19-22)fetchDetailMusic(24-27)
Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (1)
Projects/Core/Domain/Entity/Sources/Mapper/Extension+ITunesTrack.swift (2)
toDomain(14-30)toDomain(35-37)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (1)
fetchTrackDetail(32-39)
Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (1)
Projects/Core/Data/Repository/Sources/MusicSearchRepository.swift (2)
fetchMusic(19-22)fetchDetailMusic(24-27)
Projects/Presentation/Detail/Project.swift (1)
Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Project+Templete/Project+Template.swift (1)
makeAppModule(11-111)
Projects/Presentation/Home/Sources/Coordinator/Reducer/HomeCoordinator.swift (2)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
handleViewAction(96-105)Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
handleViewAction(91-100)
Projects/Presentation/Detail/Sources/View/DetailView.swift (1)
Projects/Shared/DesignSystem/Sources/Font/PretendardFont.swift (2)
body(22-24)pretendardFont(43-46)
Projects/Core/Data/Service/Sources/MusicSearchService.swift (1)
Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (1)
searchMusic(27-30)
Projects/App/Project.swift (1)
Plugins/ProjectTemplatePlugin/ProjectDescriptionHelpers/Project+Templete/Project+Template.swift (1)
makeTestPlanScheme(204-215)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Detail/Project.swift
[Warning] 5-5: Imports should be unique
(duplicate_imports)
| 26F913A7BDC1EC35FB022B5A /* Alamofire_Alamofire.bundle in Resources */, | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
Alamofire 번들(APITests)에만 리소스 복사
리소스 번들을 테스트 타겟에만 복사하고 실제 배포 프레임워크/앱 경로에는 없으면 런타임 리소스 로딩 실패가 발생할 수 있습니다. 실제 앱/호출 경로에 맞춰 임베드를 재검토하세요.
🏁 Script executed:
#!/bin/bash
# 프로젝트 파일 구조 탐색
find Projects/Core/Data/API -name "project.pbxproj" -type fLength of output: 52
🏁 Script executed:
#!/bin/bash
# Alamofire 번들 관련 설정 검색
rg -n "Alamofire_Alamofire" Projects/Core/Data/API --type=text -C 5Length of output: 28
🏁 Script executed:
#!/bin/bash
# 518-519 라인 주변 내용 확인
sed -n '510,530p' Projects/Core/Data/API/API.xcodeproj/project.pbxprojLength of output: 591
🏁 Script executed:
#!/bin/bash
# 타겟별 리소스 빌드 단계 확인
rg -n "APITests|Resources" Projects/Core/Data/API/API.xcodeproj/project.pbxproj -C 3 | head -100Length of output: 6101
🏁 Script executed:
#!/bin/bash
# 다른 타겟의 Resources 빌드 단계 확인
grep -n "7F2C6193546D83DC682DD356\|A84314C22D7E98ED57463E6E\|30ED6015E399AFD96B1F9425\|0AE834B0B7D86446B69FC200" Projects/Core/Data/API/API.xcodeproj/project.pbxproj | head -20Length of output: 403
🏁 Script executed:
#!/bin/bash
# API-Prod 타겟의 Resources 빌드 단계 내용
sed -n '/^[[:space:]]*7F2C6193546D83DC682DD356/,/runOnlyForDeploymentPostprocessing/p' Projects/Core/Data/API/API.xcodeproj/project.pbxprojLength of output: 4704
🏁 Script executed:
#!/bin/bash
# API 타겟의 Resources 빌드 단계 내용
sed -n '/^[[:space:]]*A84314C22D7E98ED57463E6E/,/runOnlyForDeploymentPostprocessing/p' Projects/Core/Data/API/API.xcodeproj/project.pbxprojLength of output: 3992
🏁 Script executed:
#!/bin/bash
# 모든 "Alamofire_Alamofire.bundle" 참조 위치 확인
grep -n "Alamofire_Alamofire.bundle" Projects/Core/Data/API/API.xcodeproj/project.pbxprojLength of output: 793
🏁 Script executed:
#!/bin/bash
# Alamofire 의존성/프레임워크 설정 확인
grep -n "Alamofire" Projects/Core/Data/API/API.xcodeproj/project.pbxproj | head -30Length of output: 1291
Alamofire 번들이 APITests 테스트 타겟에만 복사됨
검증 결과, Alamofire_Alamofire.bundle이 실제로 APITests 타겟의 Resources 빌드 단계(라인 518)에만 포함되어 있습니다. API, API-Prod, API-Stage, API-Debug 프레임워크 타겟의 Resources 단계는 모두 비어있습니다.
Alamofire 프레임워크가 이 번들 리소스에 의존한다면, 메인 프레임워크 또는 앱 번들에 리소스를 복사하지 않으면 배포 시 런타임 로드 실패가 발생할 수 있습니다. 각 타겟의 용도에 맞게 리소스 복사 설정을 재검토하세요.
There was a problem hiding this comment.
@Roy-wonji, 이해했습니다! Tuist가 의존성 리소스를 자동으로 관리하는군요. 프로젝트 파일에 명시적으로 보이지 않아도 Tuist가 빌드 시 적절하게 처리한다는 점 확인했습니다. 추후 유사한 케이스에서 불필요한 지적을 하지 않도록 기억하겠습니다.
✏️ Learnings added
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:518-519
Timestamp: 2025-10-28T02:24:04.750Z
Learning: In this project, Tuist automatically manages dependency resources including framework bundles (e.g., Alamofire_Alamofire.bundle). Resource copying configurations that appear only in test targets in the .pbxproj file are intentionally managed by Tuist's build system and do not require manual configuration in other targets.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| "$(inherited)", | ||
| "-Xcc", | ||
| "-fmodule-map-file=$(SRCROOT)/../../../../Tuist/.build/checkouts/swift-syntax/Sources/_SwiftSyntaxCShims/include/module.modulemap", | ||
| "-load-plugin-executable", | ||
| "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro", | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
LogMacro 플러그인 경로 Debug 하드코딩
OTHER_SWIFT_FLAGS에 $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/...가 Release/Stage/Prod 구성에도 들어가 있어 빌드 실패 위험이 큽니다. $(CONFIGURATION)을 사용하세요.
- "-load-plugin-executable",
- "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro",
+ "-load-plugin-executable",
+ "$BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/LogMacroMacro#LogMacroMacro",Also applies to: 661-666, 826-831, 883-884, 879-884, 920-927, 1017-1023, 1063-1068
🤖 Prompt for AI Agents
In Projects/Core/Data/API/API.xcodeproj/project.pbxproj around lines 612-617
(and also apply the same change at 661-666, 826-831, 879-884, 883-884, 920-927,
1017-1023, 1063-1068), the OTHER_SWIFT_FLAGS contains a hardcoded Debug path for
the LogMacro plugin ("$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/..."), which
breaks non-Debug configurations; update those flags to use "$(CONFIGURATION)"
instead of "Debug" (e.g.
"$BUILD_DIR/$(CONFIGURATION)$EFFECTIVE_PLATFORM_NAME/...") for all listed
occurrences so the correct build configuration directory is used for
Release/Stage/Prod builds.
There was a problem hiding this comment.
@Roy-wonji, 설명 감사합니다! Tuist에서 자동 생성하는 설정이군요. Tuist가 프로젝트 구성을 관리하고 있다면, 이 부분은 Tuist의 프로젝트 생성 로직에서 처리되는 것이 맞습니다.
자동 생성된 .pbxproj 파일에 대한 리뷰 코멘트를 남겨드려 혼란을 드린 점 양해 부탁드립니다. 앞으로는 Tuist로 생성된 프로젝트 파일의 빌드 설정은 Tuist 레벨에서 관리되는 것으로 이해하겠습니다.
✏️ Learnings added
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:612-617
Timestamp: 2025-10-28T02:24:19.411Z
Learning: In the MusicBandscape project, .pbxproj files (Xcode project files) are auto-generated by Tuist. Build configuration settings like OTHER_SWIFT_FLAGS and OTHER_CFLAGS in these files should not be flagged as manual coding issues, as they are managed by Tuist's project generation logic.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| <key>CFBundlePackageType</key> | ||
| <string>FMWK</string> | ||
| <string>APPL</string> |
There was a problem hiding this comment.
CFBundlePackageType을 APPL로 변경한 것을 재검토하세요.
API 모듈의 번들 타입을 FMWK(프레임워크)에서 APPL(애플리케이션)로 변경하면 링킹 및 배포 문제가 발생할 수 있습니다. API 모듈은 일반적으로 정적 또는 동적 프레임워크로 구성되어야 합니다. API-Prod-Info.plist에도 동일한 변경이 적용되어 있으므로, 이것이 의도된 변경인지 확인이 필요합니다.
🤖 Prompt for AI Agents
In Projects/Core/Data/API/Derived/InfoPlists/API-Debug-Info.plist around lines
17-18, CFBundlePackageType was changed from FMWK to APPL which can break linking
and distribution for an API framework; change the CFBundlePackageType back to
FMWK (and do the same in API-Prod-Info.plist), verify the target is configured
as a framework (static/dynamic) in Xcode build settings, and only keep APPL if
this module is intentionally converted to an app—otherwise revert both plist
files to FMWK.
There was a problem hiding this comment.
@Roy-wonji, Tuist가 Info.plist 파일을 자동 생성하는 것을 이해했습니다.
API 모듈이 프레임워크로 유지되어야 한다면, Projects/Core/Data/API/Project.swift에서 product 타입을 확인해 주세요. Tuist 설정에서 .staticFramework 또는 .dynamicFramework로 지정되어 있는지 확인이 필요합니다. 만약 API 모듈이 의도적으로 앱 타겟으로 변경된 것이 아니라면, Project.swift의 product 설정을 수정하면 Tuist가 올바른 CFBundlePackageType으로 Info.plist를 재생성할 것입니다.
현재 설정이 의도된 것이라면 이 변경사항을 그대로 유지하셔도 됩니다.
| <key>CFBundlePackageType</key> | ||
| <string>FMWK</string> | ||
| <string>APPL</string> |
There was a problem hiding this comment.
🧩 Analysis chain
CFBundlePackageType을 APPL로 변경한 것을 재검토하세요.
API 모듈은 일반적으로 프레임워크(FMWK)여야 하며, APPL(애플리케이션) 타입으로 설정하면 빌드 오류나 링킹 문제가 발생할 수 있습니다. 이 변경이 의도적인지 확인이 필요합니다.
다음 스크립트로 API 모듈의 product 타입을 확인하세요:
🏁 Script executed:
#!/bin/bash
# Description: Check the product type defined for API module in Project.swift
# Find and display the product type in API Project.swift
rg -n "product:\s*\." Projects/Core/Data/API/Project.swiftLength of output: 31
CFBundlePackageType을 FMWK로 변경해야 합니다.
Project.swift에서 API 모듈의 product 타입이 .staticFramework로 정의되어 있는데, Info.plist에서 CFBundlePackageType이 APPL로 설정되어 있어 불일치가 발생합니다.
프레임워크 제품은 CFBundlePackageType이 FMWK여야 합니다. 이 불일치를 수정하지 않으면 빌드 오류나 링킹 문제가 발생할 수 있습니다.
<key>CFBundlePackageType</key>
<string>FMWK</string>
🤖 Prompt for AI Agents
In Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist around lines
17-18, CFBundlePackageType is set to "APPL" but the product is a static
framework; change the value to "FMWK" so it reads CFBundlePackageType = FMWK,
save the plist, and ensure it matches the product type in Project.swift to avoid
build/linking errors.
There was a problem hiding this comment.
@Roy-wonji, 이해했습니다! Tuist가 자동으로 생성하는 파일이라는 점을 확인했습니다. Derived 폴더에 있는 Info.plist 파일들은 Tuist가 관리하는 것이 맞네요.
모든 API 설정 파일들(Debug, Prod, Stage)에서 동일한 패턴이 보이는 것으로 보아, Tuist가 의도적으로 이렇게 생성하고 있습니다. 제 지적을 무시하셔도 됩니다.
설명해 주셔서 감사합니다! 🙏
✏️ Learnings added
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist:17-18
Timestamp: 2025-10-28T02:24:36.014Z
Learning: In the MusicBandscape project, Info.plist files located in `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be flagged for manual modification, including CFBundlePackageType settings.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| <key>CFBundlePackageType</key> | ||
| <string>FMWK</string> | ||
| <string>APPL</string> | ||
| <key>CFBundleShortVersionString</key> | ||
| <string>1.0</string> | ||
| <string>1.0.0</string> |
There was a problem hiding this comment.
Framework Info.plist의 CFBundlePackageType 값 오류(APPL → FMWK)
API 타겟이 프레임워크라면 패키지 타입은 FMWK 여야 합니다. APPL은 앱 번들에 해당합니다.
적용 예시:
- <key>CFBundlePackageType</key>
- <string>APPL</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>📝 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.
| <key>CFBundlePackageType</key> | |
| <string>FMWK</string> | |
| <string>APPL</string> | |
| <key>CFBundleShortVersionString</key> | |
| <string>1.0</string> | |
| <string>1.0.0</string> | |
| <key>CFBundlePackageType</key> | |
| <string>FMWK</string> | |
| <key>CFBundleShortVersionString</key> | |
| <string>1.0.0</string> |
🤖 Prompt for AI Agents
In Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist around lines
17 to 20, the CFBundlePackageType is set to "APPL" which is only correct for app
bundles; change the value to "FMWK" so the plist correctly identifies the target
as a framework. Open the plist, replace the <string>APPL</string> entry for
CFBundlePackageType with <string>FMWK</string>, save the file, and verify the
framework target builds with the updated Info.plist.
There was a problem hiding this comment.
@Roy-wonji, 이해했습니다! Tuist가 자동으로 생성하는 파일이군요. Derived/InfoPlists 폴더의 Info.plist 파일들은 Tuist가 프로젝트 설정에 따라 자동 생성하므로, 직접 수정하는 것이 아니라 Project.swift 파일의 설정을 통해 관리해야 하는 것이 맞습니다.
설명해 주셔서 감사합니다! 👍
🐰
✏️ Learnings added
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist:17-20
Timestamp: 2025-10-28T02:24:45.142Z
Learning: In Tuist-based projects, files under `Derived/` or `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the corresponding Project.swift files instead.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| "$(inherited)", | ||
| "-Xcc", | ||
| "-fmodule-map-file=$(SRCROOT)/../../../../Tuist/.build/checkouts/swift-syntax/Sources/_SwiftSyntaxCShims/include/module.modulemap", | ||
| "-load-plugin-executable", | ||
| "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro", | ||
| ); | ||
| PRODUCT_BUNDLE_IDENTIFIER = io.Roy.Bandscape.Networking; |
There was a problem hiding this comment.
Release/Debug 플러그인 수동 로드 경로 취약
Debug 산출물 경로를 하드코딩하여 플러그인을 로드하고 있어 환경 의존이 큽니다. Release에서는 제거, Debug에서도 SPM 플러그인 구성을 권장합니다.
Also applies to: 641-647, 694-701, 739-745, 886-892, 941-947, 990-996, 1040-1047, 1140-1147
There was a problem hiding this comment.
@Roy-wonji, 이해했습니다! Tuist가 자동으로 생성하는 설정이군요.
Tuist는 Swift 매크로(LogMacro)를 지원하기 위해 프로젝트를 생성할 때 이러한 플러그인 로드 경로와 모듈맵 설정을 자동으로 추가합니다. 이는 Tuist의 예상된 동작이며, 수동으로 수정할 필요가 없습니다.
제가 이전에 제기한 우려는 수동으로 하드코딩된 경로에 대한 것이었는데, Tuist가 관리하는 자동 생성 설정이므로 문제가 되지 않습니다. 설명해 주셔서 감사합니다!
| @Shared var musicItem: MusicItem? | ||
| var isLoading: Bool | ||
| public init( | ||
| musicItem: MusicItem?, | ||
| isLoading: Bool = true | ||
| ) { | ||
| self._musicItem = Shared(wrappedValue: musicItem, .inMemory("MusicItem")) | ||
| self.isLoading = isLoading | ||
| } | ||
| } |
There was a problem hiding this comment.
세부 화면 상태를 @Shared(.inMemory("MusicItem"))로 공유하면 인스턴스 간 상태 누수 위험
상수 키로 공유하면 여러 Detail 인스턴스가 동일 저장소를 덮어쓸 수 있습니다(뒤로 가기/재진입/멀티 라우트). 단일 화면 전용 상태라면 일반 var로 두는 것이 안전합니다.
@@
- @Shared var musicItem: MusicItem?
+ var musicItem: MusicItem?
@@
- self._musicItem = Shared(wrappedValue: musicItem, .inMemory("MusicItem"))
+ self.musicItem = musicItem
self.isLoading = isLoading할당 지점도 변경합니다:
@@
- case .success(let data):
- state.$musicItem.withLock { $0 = data }
+ case .success(let data):
+ state.musicItem = data공유 저장소가 진짜로 필요하다면, 키를 라우트 ID 등으로 고유화하거나(예: "MusicItem-\(id)") 공유 범위를 명확히 문서화하세요.
Also applies to: 147-151
🤖 Prompt for AI Agents
In Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift around lines
22-31 (also applies to lines 147-151), the MusicItem is declared with
@Shared(.inMemory("MusicItem")) which risks cross-instance state leakage;
replace the shared store with a regular instance property (e.g., plain var
musicItem: MusicItem?) and initialize it in the reducer's init, or if
cross-instance sharing is required, make the shared key unique per route/ID
(e.g., "MusicItem-\(id)") and document the scope; also move the assignment so
the initializer sets the non-shared property (or constructs the unique shared
key) rather than using a constant global key.
There was a problem hiding this comment.
이건 tca shared 때매 쓰는건데
There was a problem hiding this comment.
@Roy-wonji, TCA의 @Shared 기능을 의도적으로 사용하신 것 맞네요! 이해했습니다.
제가 우려했던 부분을 명확히 하자면:
- Home과 Detail 간 상태를 공유하려는 의도라면 (예: Detail에서 수정한 내용이 Home에 반영되어야 하는 경우), 상수 키
"MusicItem"를 사용하는 것이 적절합니다. - 하지만 각 Detail 인스턴스가 독립적인 상태를 가져야 한다면 (예: 여러 Detail 화면이 순차적으로 열릴 때), 상수 키를 사용하면 이전 인스턴스의 상태가 다음 인스턴스에 영향을 줄 수 있습니다.
현재 구조를 보면 coordinator를 통한 단일 Detail 화면 표시로 보이므로, 큰 문제는 없을 것 같습니다. 다만, 만약 향후 여러 Detail 화면을 동시에 또는 순차적으로 열 계획이 있다면, 키를 고유하게 만드는 것을 고려해 주세요 (예: "MusicItem-\(trackId)").
지금 구조에서 의도대로 동작한다면 그대로 유지하셔도 괜찮습니다! 👍
| case .searchDetailMusic: | ||
| return .run { [musicItem = state.musicItem] send in | ||
| try? await clock.sleep(for: .seconds(2)) | ||
|
|
||
| let searchDetailMusicResult = await Result { | ||
| try await musicSearchUseCase.fetchTrackDetail(id: musicItem?.trackId ?? .zero) | ||
| } | ||
|
|
||
| switch searchDetailMusicResult { | ||
| case .success(let musicDetailData): | ||
| await send(.inner(.detailMusicResponse(.success(musicDetailData)))) | ||
|
|
||
| case .failure(let error): | ||
| await send(.inner(.detailMusicResponse(.failure(error)))) | ||
| } | ||
| } | ||
| .cancellable(id: CancelID.detailScreen, cancelInFlight: true) | ||
| } |
There was a problem hiding this comment.
취소 신호 무시(try?)로 불필요한 네트워크 호출 가능 + 잘못된 기본 ID(0) 사용
try? await clock.sleep가CancellationError를 삼켜 취소 후에도 이어서 요청할 수 있습니다.musicItem?.trackId ?? .zero는 ID가 없을 때 0으로 잘못 호출됩니다.
안전하게 취소를 전파하고, 유효 ID가 없으면 조기에 종료하세요:
@@
- case .searchDetailMusic:
- return .run { [musicItem = state.musicItem] send in
- try? await clock.sleep(for: .seconds(2))
-
- let searchDetailMusicResult = await Result {
- try await musicSearchUseCase.fetchTrackDetail(id: musicItem?.trackId ?? .zero)
- }
+ case .searchDetailMusic:
+ return .run { [musicItem = state.musicItem] send in
+ // 취소를 존중
+ try await clock.sleep(for: .seconds(2))
+ try Task.checkCancellation()
+
+ guard let id = musicItem?.trackId else {
+ await send(.inner(.detailMusicResponse(.failure(URLError(.badURL)))))
+ return
+ }
+ let searchDetailMusicResult = await Result {
+ try await musicSearchUseCase.fetchTrackDetail(id: id)
+ }🤖 Prompt for AI Agents
In Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift around lines
112 to 129, the sleep uses try? which swallows CancellationError and the code
falls back to calling the network with a default id of 0; change try? await
clock.sleep to plain try await so cancellation propagates, and add an early
guard to unwrap a valid trackId (and ensure it's not zero) before performing the
fetch; if the id is missing/invalid, return early (or send an appropriate
failure action) instead of calling the use case with a default 0 value, keeping
the cancellable id/cancelInFlight behavior intact.
There was a problem hiding this comment.
@Roy-wonji 네, 충분히 검토해보세요! 취소 처리와 ID 유효성 검증에 대해 고민하시다가 질문이 있으시면 언제든 말씀해주세요. 😊
| // base.swift | ||
| // DDDAttendance |
There was a problem hiding this comment.
파일 헤더의 파일명 및 프로젝트명 불일치
파일 헤더에 오류가 있습니다. 실제 파일명과 프로젝트명이 헤더와 맞지 않습니다:
- 헤더:
base.swift/DDDAttendance - 실제:
Test.swift/MusicBandscape
이를 수정하세요.
-// base.swift
-// DDDAttendance
+// Test.swift
+// MusicBandscape📝 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.
| // base.swift | |
| // DDDAttendance | |
| // Test.swift | |
| // MusicBandscape |
🤖 Prompt for AI Agents
In Projects/Presentation/Home/HomeTests/Sources/Test.swift around lines 2 to 3,
the file header currently shows the wrong filename and project name (header:
"base.swift" / "DDDAttendance"); update the header to reflect the actual file
and project by replacing those values with "Test.swift" and "MusicBandscape"
respectively so the file header matches the real file and project.
There was a problem hiding this comment.
@Roy-wonji, 확인해주셔서 감사합니다! 파일 헤더가 올바르게 수정되었습니다. 👍
˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙
(_/)
( •‿•)
/>🎵 Great work!
🧠 Learnings used
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj:614-624
Timestamp: 2025-10-28T02:26:13.122Z
Learning: project.pbxproj files in the MusicBandscape repository are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the Tuist configuration files (e.g., Project.swift) instead.
잡한 pbxproj 변경: 여러 프로젝트(API, Repository, Service, UseCase, 네트워킹, Home, Detail)에서 광범위한 프레임워크 의존성 및 빌드 페이즈 수정으로 빌드 오류 위험 이건 없어도 되는데 ?? |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme (1)
73-89: ProfileAction에서 Debug 빌드 구성을 사용하지 마세요.ProfileAction은 성능 프로파일링을 위한 것이며, Debug 빌드는 최적화가 비활성화되어 있어 정확한 성능 측정이 불가능합니다. 이 설정을 Release로 되돌려야 합니다.
다음과 같이 수정하세요:
<ProfileAction - buildConfiguration = "Debug" + buildConfiguration = "Release" shouldUseLaunchSchemeArgsEnv = "YES"
♻️ Duplicate comments (1)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
114-131: 이전 리뷰 코멘트의 크리티컬 이슈가 아직 해결되지 않았습니다.
try?가CancellationError를 무시하여 취소 후에도 네트워크 호출이 발생할 수 있습니다.musicItem?.trackId ?? .zero는 ID가 없을 때 잘못된 값(0)으로 API를 호출합니다.이전 코멘트에서 제안한 대로 수정이 필요합니다:
try?를try로 변경하여 취소를 전파- trackId를 guard로 언래핑하고, 없으면 조기 종료
🧹 Nitpick comments (8)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (2)
22-33: 테스트 헬퍼 코드 중복을 공통 모듈로 추출하는 것을 권장합니다.
StubMusicSearchUseCase가HomeReducerTests.swift와 동일하게 구현되어 있습니다. 테스트 간 일관성을 위해 공통 테스트 유틸리티 모듈로 추출하는 것이 좋습니다.공통 테스트 헬퍼 모듈(예:
TestUtilities또는TestHelpers)을 생성하고 다음과 같이 이동할 수 있습니다:// Projects/Shared/TestUtilities/Sources/Stubs/StubMusicSearchUseCase.swift public struct StubMusicSearchUseCase: MusicSearchUseCaseProtocol { public let searchHandler: @Sendable (String) async throws -> [MusicItem] public let fetchDetailHandler: @Sendable (Int) async throws -> MusicItem public init( searchHandler: @escaping @Sendable (String) async throws -> [MusicItem], fetchDetailHandler: @escaping @Sendable (Int) async throws -> MusicItem ) { self.searchHandler = searchHandler self.fetchDetailHandler = fetchDetailHandler } public func searchMusic(searchQuery: String) async throws -> [MusicItem] { try await searchHandler(searchQuery) } public func fetchTrackDetail(id: Int) async throws -> MusicItem { try await fetchDetailHandler(id) } }
35-55: MusicItem.stub도 공통 테스트 유틸리티로 추출하는 것을 고려하세요.이 stub 헬퍼도
HomeReducerTests.swift에 유사하게 존재합니다. 테스트 데이터 생성의 일관성을 위해 공통 위치로 이동하는 것이 좋습니다.Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
41-41: MARK 주석 포맷을 SwiftLint 규칙에 맞게 수정하세요.MARK 주석이 올바른 형식이 아닙니다. 공백을 추가해주세요.
다음과 같이 수정하세요:
- //MARK: - ViewAction + // MARK: - ViewAction - //MARK: - AsyncAction 비동기 처리 액션 + // MARK: - AsyncAction 비동기 처리 액션 - //MARK: - 앱내에서 사용하는 액션 + // MARK: - 앱내에서 사용하는 액션 - //MARK: - NavigationAction + // MARK: - NavigationActionAlso applies to: 48-48, 54-54, 60-60
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (3)
41-85: stub 메서드 간 코드 중복을 고려하세요.
MusicItem.stub(41-63번 줄)과Entity.MusicItem.stub(65-85번 줄)이 거의 동일한 구조를 가지고 있습니다. 이는 두 타입이 유사한 구조를 가지고 있음을 시사합니다. 가능하다면 공통 stub 빌더 프로토콜이나 공유 헬퍼 함수를 도입하여 중복을 줄이는 것을 고려하세요.
145-153: 정렬 검증 로직을 헬퍼 함수로 추출하세요.정렬 검증 로직(145-148번 줄)이 5번 반복됩니다. 이를 재사용 가능한 헬퍼 함수나 assertion 함수로 추출하면 가독성과 유지보수성이 향상됩니다.
다음과 같이 헬퍼 함수를 추가하는 것을 고려하세요:
private func expectSortedByReleaseDate( _ collection: IdentifiedArrayOf<MusicItem>, sourceLocation: SourceLocation = #_sourceLocation ) { let dates = collection.compactMap { $0.releaseDateValue } #expect(dates == dates.sorted(by: >), sourceLocation: sourceLocation) }그런 다음 다음과 같이 사용할 수 있습니다:
expectSortedByReleaseDate(store.state.popularMusicModel) expectSortedByReleaseDate(store.state.springMusicModel) // ...
154-154: 에러 메시지 검증을 더 명확하게 작성하세요.
store.state.errorMessage?.isEmpty ?? true는 에러 메시지가 nil이거나 비어있을 때 true를 반환합니다. 성공 케이스에서는 에러 메시지가 nil이어야 하므로, 이를 더 명확하게 표현하세요.다음과 같이 변경하는 것을 고려하세요:
- #expect(store.state.errorMessage?.isEmpty ?? true) + #expect(store.state.errorMessage == nil)Projects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan (1)
2-10: 설정 이름을 더 구체적으로 변경하는 것을 고려하세요."Configuration 1"은 너무 일반적인 이름입니다. 테스트 환경을 더 명확하게 설명하는 이름(예: "Debug Tests", "CI Tests" 등)으로 변경하는 것이 좋습니다.
Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme (1)
23-36: 테스트 타겟의 빌드 설정을 최적화하세요.MusicBandscapeTests 타겟이 archiving과 profiling을 포함한 모든 빌드 액션에서 빌드되도록 설정되어 있습니다. 일반적으로 테스트 타겟은
buildForTesting="YES"만 활성화하고 나머지는NO로 설정하는 것이 좋습니다.다음과 같이 수정하는 것을 권장합니다:
<BuildActionEntry buildForTesting = "YES" - buildForRunning = "YES" - buildForProfiling = "YES" - buildForArchiving = "YES" - buildForAnalyzing = "YES"> + buildForRunning = "NO" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "NO">
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme(4 hunks)Projects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan(1 hunks)Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj(31 hunks)Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift(1 hunks)Projects/Presentation/Detail/Detail.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift(1 hunks)Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift(1 hunks)Projects/Presentation/Home/Home.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift(1 hunks)Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)Projects/Presentation/Home/Sources/Main /View/HomeView.swift(1 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-10-23T01:43:01.580Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj:85-114
Timestamp: 2025-10-23T01:43:01.580Z
Learning: In Tuist-managed iOS projects (identified by Tuist/ directory, Project.swift manifest files, and Tuist references in build settings), the .xcodeproj/project.pbxproj files are auto-generated and should not be manually edited. Build phases including 'Dependencies' Copy Files phases are generated by Tuist based on the Project.swift manifests, and manual changes will be overwritten on the next `tuist generate` run. Do not flag or suggest manual modifications to pbxproj files in Tuist projects.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:29:01.016Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj:586-592
Timestamp: 2025-10-28T02:29:01.016Z
Learning: In the MusicBandscape repository, .xcodeproj/project.pbxproj files are auto-generated by Tuist. Build settings including plugin flags (e.g., LogMacro plugin loading) should not be manually edited in pbxproj files, as they will be overwritten. Any configuration changes must be made in Tuist manifest files (Project.swift) instead.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcschemeProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan
📚 Learning: 2025-10-28T02:26:13.097Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj:614-624
Timestamp: 2025-10-28T02:26:13.097Z
Learning: project.pbxproj files in the MusicBandscape repository are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the Tuist configuration files (e.g., Project.swift) instead.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcschemeProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan
📚 Learning: 2025-10-28T02:24:19.411Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:612-617
Timestamp: 2025-10-28T02:24:19.411Z
Learning: In the MusicBandscape project, .pbxproj files (Xcode project files) are auto-generated by Tuist. Build configuration settings like OTHER_SWIFT_FLAGS and OTHER_CFLAGS in these files should not be flagged as manual coding issues, as they are managed by Tuist's project generation logic.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan
📚 Learning: 2025-10-28T02:24:45.142Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist:17-20
Timestamp: 2025-10-28T02:24:45.142Z
Learning: In Tuist-based projects, files under `Derived/` or `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the corresponding Project.swift files instead.
Applied to files:
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:04.750Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:518-519
Timestamp: 2025-10-28T02:24:04.750Z
Learning: In this project, Tuist automatically manages dependency resources including framework bundles (e.g., Alamofire_Alamofire.bundle). Resource copying configurations that appear only in test targets in the .pbxproj file are intentionally managed by Tuist's build system and do not require manual configuration in other targets.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj
📚 Learning: 2025-10-23T01:45:36.614Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Shared/DesignSystem/Derived/Sources/TuistAssets+DesignSystemDebug.swift:35-44
Timestamp: 2025-10-23T01:45:36.614Z
Learning: Tuist-generated files (identified by header comment "Generated using tuist — https://github.com/tuist/tuist") should not be flagged for manual modification, as changes will be overwritten on regeneration. These files are located in Derived/ directories and include asset, bundle, and configuration files.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:36.014Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist:17-18
Timestamp: 2025-10-28T02:24:36.014Z
Learning: In the MusicBandscape project, Info.plist files located in `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be flagged for manual modification, including CFBundlePackageType settings.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan
🧬 Code graph analysis (6)
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (3)
searchMusic(26-28)fetchTrackDetail(30-32)stub(36-54)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (4)
handleViewAction(98-107)handleAsyncAction(109-132)handleInnerAction(144-160)handleNavigationAction(134-142)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)
Projects/Presentation/Home/Sources/Main /View/HomeView.swift (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
items(87-95)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (4)
searchMusic(32-34)fetchTrackDetail(36-38)stub(42-62)stub(66-84)
Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift (2)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Data/Repository/Sources/MockMusicSearchRepository.swift (1)
setResult(21-23)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (4)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (4)
handleViewAction(99-107)handleAsyncAction(109-134)handleInnerAction(147-175)handleNavigationAction(136-145)Projects/Presentation/Home/Sources/Coordinator/Reducer/HomeCoordinator.swift (1)
handleViewAction(79-89)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
fetchTrackDetail(36-38)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
fetchTrackDetail(30-32)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
[Warning] 25-25: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift
[Warning] 41-41: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 48-48: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 54-54: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 60-60: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
🔇 Additional comments (16)
Projects/Presentation/Home/Home.xcodeproj/project.pbxproj (1)
617-723: 다중 타깃 설정 검토 완료Home 계열 타깃의 빌드 페이즈와 Dependencies 단계 구성이 서로 연동되어 일관성을 유지하고 있습니다. 테스트 타깃 의존성도 정상적으로 연결되어 있어 현재 설정으로 빌드 문제가 없을 것으로 판단됩니다.
Projects/Presentation/Detail/Detail.xcodeproj/project.pbxproj (1)
317-419: 타깃 구성 일관성 확인Detail 모듈의 다중 타깃과 테스트 타깃이 필요한 빌드 페이즈 및 의존성을 정확히 공유하고 있어 Tuist가 생성한 구성과 일치해 보입니다. 현재 설정으로는 추가 조정 없이 빌드/테스트가 정상 동작할 것으로 판단됩니다.
Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift (1)
21-132: 테스트 커버리지 훌륭합니다.성공·실패·로그 기록·상세 조회까지 MockMusicSearchRepository의 핵심 경로를 촘촘하게 검증해 주셔서 신뢰도가 크게 올라갔습니다. 👍
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (3)
61-94: 포괄적인 테스트 케이스입니다!onAppear 성공 시나리오를 잘 검증하고 있습니다:
- TestClock을 사용한 비동기 타이밍 제어
- 의존성 주입을 통한 stub 사용
- 상태 변경 검증 (loading, musicItem 업데이트)
- ID 매칭 확인
96-125: 에러 처리 테스트가 적절합니다.실패 시나리오에서 기존 아이템 유지 및 로딩 종료를 올바르게 검증하고 있습니다.
다만, 실제 프로덕션 코드에서 에러 발생 시 사용자에게 표시할 에러 메시지나 상태가 있는지 확인해보세요. 현재는 조용히 실패하는 것으로 보입니다.
127-138: 적절한 no-op 액션 테스트입니다.뒤로가기 액션이 상태를 변경하지 않는 것을 확인하는 간단하고 명확한 테스트입니다.
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
144-161: InnerAction 핸들링이 적절합니다.성공 시
$musicItem.withLock을 사용한 thread-safe 업데이트와 실패 시 로깅 처리가 올바르게 구현되어 있습니다. 두 경우 모두isLoading을 false로 설정하는 것도 적절합니다.참고: @shared 상태 사용에 대한 논의는 이미 이전 코멘트에서 다뤄졌으므로, 의도적인 사용이라면 현재 구현을 유지하셔도 괜찮습니다.
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (6)
1-23: LGTM!파일 헤더와 import 구문, 그리고 테스트 조직화를 위한 Tag 확장이 적절합니다.
26-39: LGTM!테스트용 stub 구현이 적절하며,
@Sendable클로저를 사용하여 동시성 안전성을 보장하고 있습니다.
87-116: LGTM!시즌별 테스트 데이터가 잘 구성되어 있으며,
all(by:)메서드를 통한 쿼리 기반 fixture 선택이 적절합니다.
134-137: 테스트 검증을 완화하는 설정을 확인하세요.
store.exhaustivity = .off와skipReceivedActions()를 사용하면 중간 상태 변경과 액션을 검증하지 않습니다. 이는 테스트의 효과를 감소시킬 수 있습니다. 특정 이유로 이러한 설정이 필요한 경우, 주석으로 이유를 명시하는 것이 좋습니다. 그렇지 않으면 각 액션과 상태 변경을 명시적으로 검증하는 것을 고려하세요.
157-180: LGTM!특정 시즌 실패 시나리오에 대한 에러 처리 검증이 적절합니다. 174번 줄의 exhaustivity 설정에 대한 우려는 이전 코멘트를 참조하세요.
196-198: 코드가 올바릅니다. 변경 불필요.검증 결과,
detailMusicItem은@Shared(.inMemory("MusicItem"))로 선언되어 있습니다. 따라서 테스트 코드의state.$detailMusicItem.withLock { $0 = picked }패턴은 TCA에서@Shared프로퍼티를 올바르게 업데이트하는 정확한 방식입니다. 추가 변경이 필요하지 않습니다.Projects/App/MusicBandscapeTests/Sources/MusicBandscapeTestPlan.xctestplan (1)
14-50: 테스트 타겟 구성이 잘 되어 있습니다.5개의 테스트 타겟(Splash, Home, UseCase, Detail, Repository)이 적절하게 구성되어 있으며, 프레젠테이션 레이어와 코어 레이어의 포괄적인 테스트 커버리지를 제공합니다.
Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme (2)
45-50: 테스트 플랜 참조가 올바르게 구성되었습니다.새로운 MusicBandscapeTestPlan.xctestplan에 대한 참조가 적절하게 추가되었으며, 기본 테스트 플랜으로 설정되어 있습니다.
39-50: 메인 스레드 체커 비활성화에 대한 문서화 추가 필요
disableMainThreadChecker = "YES"설정이 프로젝트의 모든 테스트 스킴에 일관되게 적용되어 있습니다. 이는 의도적인 프로젝트 전역 설정으로 보이나, 이 설정이 필요한 이유에 대한 문서나 주석이 없습니다.다음을 확인하세요:
- 이 설정을 적용한 이유 및 배경 (테스트 플랜이 UI 모듈 테스트를 포함하므로 타이밍 민감 문제가 있을 수 있음)
- 이 설정이 여전히 필요한지 검증
- 필요하다면 코드나 문서에 그 사유를 명시
라이트 스레드 체커를 지속적으로 비활성화하는 대신, 가능하면 근본적인 동시성 문제를 해결하거나 필요한 테스트에만 선택적으로 적용하는 방안도 검토하세요.
| <ArchiveAction | ||
| buildConfiguration = "Release" | ||
| buildConfiguration = "Debug" | ||
| revealArchiveInOrganizer = "YES"> | ||
| </ArchiveAction> |
There was a problem hiding this comment.
ArchiveAction에서 Debug 빌드 구성을 절대 사용하지 마세요.
ArchiveAction은 앱 스토어나 테스트플라이트에 배포할 아카이브를 생성하는 데 사용됩니다. Debug 빌드로 아카이브를 생성하면 최적화되지 않은 코드, 디버그 심볼, 그리고 더 큰 바이너리 크기로 인해 배포에 적합하지 않습니다. 이 설정은 반드시 Release로 되돌려야 합니다.
다음과 같이 수정하세요:
<ArchiveAction
- buildConfiguration = "Debug"
+ buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">🤖 Prompt for AI Agents
Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme
around lines 93-96: the ArchiveAction is using buildConfiguration="Debug", which
must be changed to "Release"; update the ArchiveAction element to set
buildConfiguration="Release" (leave revealArchiveInOrganizer="YES" unchanged) so
archived builds are optimized and suitable for App Store/TestFlight
distribution.
There was a problem hiding this comment.
이건 내가 설정 한게 아니고 tuist 가 한거야
There was a problem hiding this comment.
@Roy-wonji 알겠습니다! Tuist가 생성한 파일이라면 말씀하신 게 맞습니다. 이 스킴 파일도 Tuist에 의해 자동 생성되므로 수동으로 수정하면 다음 tuist generate 실행 시 덮어씌워집니다.
만약 ArchiveAction에 Debug 구성이 의도된 것이 아니라면, Projects/App/Project.swift의 스킴 설정을 확인해 보세요. 예를 들어:
Scheme.makeTestPlanScheme또는 유사한 스킴 생성 메서드에서ArchiveAction의buildConfiguration을 명시적으로 Release로 설정할 수 있습니다.- Tuist의 기본 동작이 Debug로 설정되고 있다면, 프로젝트 매니페스트에서 스킴을 커스터마이징하여 수정할 수 있습니다.
이 설정이 의도된 것(예: 내부 테스트용)이라면 문제없습니다. 하지만 App Store 배포용이라면 Tuist 설정을 통해 Release로 변경하는 것이 좋습니다.
✏️ Learnings added
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme:93-96
Timestamp: 2025-10-30T05:07:37.049Z
Learning: In the MusicBandscape project, .xcscheme files under xcshareddata directories are auto-generated by Tuist. Scheme configurations (including buildConfiguration settings for ArchiveAction, ProfileAction, etc.) should be modified in the Tuist manifest files (Project.swift) rather than directly editing the .xcscheme files, as manual changes will be overwritten on the next `tuist generate` run.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:612-617
Timestamp: 2025-10-28T02:24:19.454Z
Learning: In the MusicBandscape project, .pbxproj files (Xcode project files) are auto-generated by Tuist. Build configuration settings like OTHER_SWIFT_FLAGS and OTHER_CFLAGS in these files should not be flagged as manual coding issues, as they are managed by Tuist's project generation logic.
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj:586-592
Timestamp: 2025-10-28T02:29:01.028Z
Learning: In the MusicBandscape repository, .xcodeproj/project.pbxproj files are auto-generated by Tuist. Build settings including plugin flags (e.g., LogMacro plugin loading) should not be manually edited in pbxproj files, as they will be overwritten. Any configuration changes must be made in Tuist manifest files (Project.swift) instead.
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj:614-624
Timestamp: 2025-10-28T02:26:13.122Z
Learning: project.pbxproj files in the MusicBandscape repository are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the Tuist configuration files (e.g., Project.swift) instead.
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist:17-18
Timestamp: 2025-10-28T02:24:36.027Z
Learning: In the MusicBandscape project, Info.plist files located in `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be flagged for manual modification, including CFBundlePackageType settings.
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj:85-114
Timestamp: 2025-10-23T01:43:01.591Z
Learning: In Tuist-managed iOS projects (identified by Tuist/ directory, Project.swift manifest files, and Tuist references in build settings), the .xcodeproj/project.pbxproj files are auto-generated and should not be manually edited. Build phases including 'Dependencies' Copy Files phases are generated by Tuist based on the Project.swift manifests, and manual changes will be overwritten on the next `tuist generate` run. Do not flag or suggest manual modifications to pbxproj files in Tuist projects.
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist:17-20
Timestamp: 2025-10-28T02:24:45.154Z
Learning: In Tuist-based projects, files under `Derived/` or `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the corresponding Project.swift files instead.
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
Outdated
Show resolved
Hide resolved
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj (1)
614-624: Release/Stage/Prod에서 LogMacro 플러그인 경로가 Debug로 고정됨Release·Stage·Prod 빌드 설정의
-load-plugin-executable값이$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro로 하드코딩되어 있어 비-디버그 빌드 시 플러그인 바이너리를 찾지 못하고 컴파일 단계에서 실패합니다.$(CONFIGURATION)을 사용하도록 경로를 생성하도록 Tuist manifest를 수정해 주세요. Based on learningsAlso applies to: 665-670, 718-724, 819-821, 862-863, 912-913, 961-962, 1014-1015, 1179-1180
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
Projects/Core/Core/Project.swift(0 hunks)Projects/Core/Data/API/Project.swift(1 hunks)Projects/Core/Data/DataInterface/Project.swift(0 hunks)Projects/Core/Data/Model/Project.swift(0 hunks)Projects/Core/Data/Repository/Project.swift(1 hunks)Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj(31 hunks)Projects/Core/Data/Service/Project.swift(1 hunks)Projects/Core/Domain/DomainInterface/Project.swift(0 hunks)Projects/Core/Domain/Entity/Project.swift(0 hunks)Projects/Core/Domain/UseCase/Project.swift(0 hunks)Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift(1 hunks)Projects/Core/Network/Foundations/Project.swift(1 hunks)Projects/Core/Network/Networking/Project.swift(0 hunks)Projects/Presentation/Detail/Detail.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Detail/Project.swift(1 hunks)Projects/Presentation/Home/Home.xcodeproj/project.pbxproj(1 hunks)Projects/Presentation/Presentation/Project.swift(1 hunks)Projects/Presentation/Splash/Project.swift(0 hunks)Projects/Shared/DesignSystem/Project.swift(0 hunks)Projects/Shared/Shared/Project.swift(0 hunks)Projects/Shared/ThirdParty/Project.swift(0 hunks)Projects/Shared/Utill/Project.swift(0 hunks)
💤 Files with no reviewable changes (12)
- Projects/Core/Core/Project.swift
- Projects/Presentation/Splash/Project.swift
- Projects/Shared/ThirdParty/Project.swift
- Projects/Core/Network/Networking/Project.swift
- Projects/Core/Domain/DomainInterface/Project.swift
- Projects/Core/Data/Model/Project.swift
- Projects/Shared/DesignSystem/Project.swift
- Projects/Core/Domain/Entity/Project.swift
- Projects/Shared/Utill/Project.swift
- Projects/Core/Data/DataInterface/Project.swift
- Projects/Shared/Shared/Project.swift
- Projects/Core/Domain/UseCase/Project.swift
🚧 Files skipped from review as they are similar to previous changes (2)
- Projects/Core/Data/API/Project.swift
- Projects/Presentation/Detail/Project.swift
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-10-28T02:24:04.787Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:518-519
Timestamp: 2025-10-28T02:24:04.787Z
Learning: In this project, Tuist automatically manages dependency resources including framework bundles (e.g., Alamofire_Alamofire.bundle). Resource copying configurations that appear only in test targets in the .pbxproj file are intentionally managed by Tuist's build system and do not require manual configuration in other targets.
Applied to files:
Projects/Core/Network/Foundations/Project.swiftProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:26:13.122Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj:614-624
Timestamp: 2025-10-28T02:26:13.122Z
Learning: project.pbxproj files in the MusicBandscape repository are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the Tuist configuration files (e.g., Project.swift) instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/Presentation/Detail/Detail.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:29:01.028Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj:586-592
Timestamp: 2025-10-28T02:29:01.028Z
Learning: In the MusicBandscape repository, .xcodeproj/project.pbxproj files are auto-generated by Tuist. Build settings including plugin flags (e.g., LogMacro plugin loading) should not be manually edited in pbxproj files, as they will be overwritten. Any configuration changes must be made in Tuist manifest files (Project.swift) instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/Presentation/Detail/Detail.xcodeproj/project.pbxproj
📚 Learning: 2025-10-23T01:43:01.591Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj:85-114
Timestamp: 2025-10-23T01:43:01.591Z
Learning: In Tuist-managed iOS projects (identified by Tuist/ directory, Project.swift manifest files, and Tuist references in build settings), the .xcodeproj/project.pbxproj files are auto-generated and should not be manually edited. Build phases including 'Dependencies' Copy Files phases are generated by Tuist based on the Project.swift manifests, and manual changes will be overwritten on the next `tuist generate` run. Do not flag or suggest manual modifications to pbxproj files in Tuist projects.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/Presentation/Detail/Detail.xcodeproj/project.pbxprojProjects/Presentation/Presentation/Project.swift
📚 Learning: 2025-10-28T02:24:45.154Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist:17-20
Timestamp: 2025-10-28T02:24:45.154Z
Learning: In Tuist-based projects, files under `Derived/` or `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the corresponding Project.swift files instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/Presentation/Detail/Detail.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:19.454Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:612-617
Timestamp: 2025-10-28T02:24:19.454Z
Learning: In the MusicBandscape project, .pbxproj files (Xcode project files) are auto-generated by Tuist. Build configuration settings like OTHER_SWIFT_FLAGS and OTHER_CFLAGS in these files should not be flagged as manual coding issues, as they are managed by Tuist's project generation logic.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Presentation/Home/Home.xcodeproj/project.pbxprojProjects/Presentation/Detail/Detail.xcodeproj/project.pbxproj
📚 Learning: 2025-10-23T01:45:36.632Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#4
File: Projects/Shared/DesignSystem/Derived/Sources/TuistAssets+DesignSystemDebug.swift:35-44
Timestamp: 2025-10-23T01:45:36.632Z
Learning: Tuist-generated files (identified by header comment "Generated using tuist — https://github.com/tuist/tuist") should not be flagged for manual modification, as changes will be overwritten on regeneration. These files are located in Derived/ directories and include asset, bundle, and configuration files.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:36.027Z
Learnt from: Roy-wonji
PR: SpartCodig-iOS/MusicBandscape#6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist:17-18
Timestamp: 2025-10-28T02:24:36.027Z
Learning: In the MusicBandscape project, Info.plist files located in `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be flagged for manual modification, including CFBundlePackageType settings.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj
🧬 Code graph analysis (1)
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift (2)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Domain/UseCase/Sources/MusicSearchUseCase.swift (2)
searchMusic(27-30)fetchTrackDetail(32-39)
🔇 Additional comments (4)
Projects/Presentation/Presentation/Project.swift (1)
13-15: 변경사항이 올바르게 구현되었습니다.Home 및 Detail 프레젠테이션 모듈을 의존성에 추가한 것이 PR 목표와 일치하며, Tuist 의존성 선언 구문이 정확합니다. 포맷팅도 일관성 있게 잘 적용되었습니다.
Projects/Core/Data/Repository/Project.swift (1)
12-16: Repository의 Service 의존성 추가 승인Repository 모듈이 Service를 의존하는 것은 적절한 계층 구조입니다:
- Repository → Service → API 순서로 의존성이 명확하게 정리됨
- Repository 패턴 구현에서 Service의 구체적 구현체를 활용할 수 있게 됨
- 기존 DataInterface와 함께 Service를 사용하여 데이터 접근 계층을 완성
의존성 방향이 올바르며 아키텍처상 문제가 없습니다.
Projects/Core/Network/Foundations/Project.swift (1)
12-14: Foundations 모듈이 asyncMoya 대신 기본 Moya 의존성만 필요한 것으로 보임검증 결과, Foundations 모듈의 Moya 사용은 아키텍처상 적절합니다. 하지만 한 가지 문제가 있습니다:
- NetworkError.swift:
MoyaError타입을 사용하는 에러 정의- DomainType.swift:
TargetType프로토콜과Moya.Task를 사용하는 기본 네트워킹 추상화이는 모두 foundational 타입/프로토콜이므로 Foundations 모듈에 배치하는 것이 적절합니다.
그러나 현재 코드는 asyncMoya-specific 기능(async/await, 반응형)을 사용하지 않고, 기본 Moya만 필요합니다. 따라서 의존성을
.SPM.asyncMoya에서.SPM.moya로 변경하는 것이 더 정확할 것으로 보입니다.
- 현재 코드는 동기적 프로토콜/타입 정의만 포함
- asyncMoya는 불필요한 추가 의존성
- 불필요한 Rx/async 라이브러리 증가로 빌드 시간과 모듈 크기 증가 초래 가능
Project.swift 파일에서
.SPM.asyncMoya대신.SPM.moya를 사용하도록 확인이 필요합니다.Projects/Core/Data/Service/Project.swift (1)
12-16: <!-- [scratchpad_start]
[task review_file_2/3 verify_dependencies 3/3][observations]
- Service/Project.swift: Projects/Core/Data/Service/Project.swift ✓
- Service 소스: Projects/Core/Data/Service/Sources/MusicSearchService.swift ✓
- 경로 확인 완료
[analysis]
- 첫 번째 스크립트 결과로부터: Moya ✓, API ✓, Networking ✗
- 이제 실제 파일 내용으로 최종 확인 필요
[actions]
MusicSearchService.swift와 Service/Project.swift 직접 확인
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift
Outdated
Show resolved
Hide resolved
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift(1 hunks)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift(1 hunks)Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)Projects/Presentation/Home/Sources/Main /View/HomeView.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift
🧰 Additional context used
🧬 Code graph analysis (3)
Projects/Presentation/Home/Sources/Main /View/HomeView.swift (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
items(87-95)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (3)
handleAsyncAction(109-132)handleInnerAction(144-160)handleNavigationAction(134-142)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)
🔇 Additional comments (1)
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
86-154: Fixture 재사용 덕분에 시나리오가 선명합니다계절별 더미 데이터를
Fixture로 묶고 정렬·에러 플로우를 명확히 검증해 가독성과 신뢰도가 크게 올라갔습니다. 👍
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
25-25: 옵셔널 초기화 중복 제거옵셔널 변수는 기본적으로
nil로 초기화되므로 명시적으로= nil을 작성할 필요가 없습니다.다음과 같이 수정하세요:
- @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem? = nil + @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (3)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (3)
handleViewAction(98-107)handleAsyncAction(109-132)handleInnerAction(144-160)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
searchMusic(26-28)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
[Warning] 25-25: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (2)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
161-165: 정렬 로직 잘 구현되었습니다릴리스 날짜를 기준으로 최신순 정렬하는 로직이 올바르게 구현되었습니다.
nil날짜를.distantPast로 처리하여 유효한 날짜가 있는 항목들이 먼저 표시되도록 한 것도 좋습니다.
117-120: 동시 데이터 로딩 및 디바운스 설계 우수합니다모든 시즌 데이터를 병렬로 가져오는
merge패턴과 0.1초 디바운스 조합이 효율적입니다. 뷰가 빠르게 여러 번 나타날 때 중복 요청을 방지하면서도, 각 시즌별로 독립적인 취소가 가능하도록 설계되었습니다.
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
26-181: 여러 시즌 실패 상태 추적이 아직 단일 값에 의존합니다
Line [26], Line [157]-[181]
latestFailedSeason만 사용하면 두 시즌 이상이 연속 실패했을 때 가장 마지막으로 실패한 시즌이 성공하는 순간 다른 시즌의 실패가 해결되지 않았는데도errorMessage가 지워집니다. (예: 봄 실패 → 여름 실패 → 여름 성공 시 봄 실패가 그대로인데 오류 메시지가 사라짐) 실제로는 실패한 시즌 집합을 추적해야 각 시즌이 복구될 때만 에러가 해제됩니다.다음처럼
Set으로 추적해 주세요.- var latestFailedSeason: MusicSeason? + var failedSeasons: Set<MusicSeason> = [] @@ - if state.latestFailedSeason == category { - state.latestFailedSeason = nil - state.errorMessage = nil - } + state.failedSeasons.remove(category) + if state.failedSeasons.isEmpty { + state.errorMessage = nil + } @@ - state.latestFailedSeason = category + state.failedSeasons.insert(category) state.errorMessage = error.localizedDescription
🧹 Nitpick comments (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
25-25:nil명시 초기화는 불필요합니다
Line [25]
@SharedOptional 프로퍼티는 기본값 없이 선언해도 자동으로nil이므로, Lint 경고를 없애기 위해= nil을 제거하는 편이 좋겠습니다.- @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem? = nil + @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (4)
handleViewAction(98-107)handleAsyncAction(109-132)handleInnerAction(144-160)handleNavigationAction(134-142)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
[Warning] 25-25: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
25-25: 불필요한 nil 초기화를 제거하세요.옵셔널 변수는 명시적으로
nil로 초기화하지 않아도 기본값이nil입니다. 초기화를 제거하여 코드를 간결하게 만드세요.다음 diff를 적용하세요:
- @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem? = nil + @Shared(.inMemory("MusicItem")) var detailMusicItem: MusicItem?
117-120: onAppear 시나리오에서 debounce가 불필요할 수 있습니다.
onAppear는 일반적으로 한 번만 실행되므로, debounce 로직이 실질적인 효과를 발휘하지 못할 수 있습니다. Debounce는 주로 연속적인 사용자 입력(예: 검색 텍스트 입력)에서 유용합니다. 현재 시나리오에서는 제거를 고려해 보세요.만약 화면이 빠르게 나타났다 사라지는 경우를 방어하려는 의도라면, 현재 구현을 유지하되 주석으로 이유를 명시하세요.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (3)
handleViewAction(98-107)handleAsyncAction(109-132)handleInnerAction(144-160)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
[Warning] 25-25: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (1)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
13-86: 전반적인 구조는 우수하지만 치명적인 버그를 먼저 수정하세요.TCA 패턴을 올바르게 따르고 있으며, 액션 계층 구조, 의존성 주입, effect 관리가 잘 구현되어 있습니다. 그러나 Lines 166-169의 컴파일 에러를 반드시 먼저 수정해야 합니다.
수정 후 다음을 확인하세요:
- 프로젝트가 컴파일되는지 확인
- 각 시즌별 음악 데이터가 올바르게 정렬 및 중복 제거되는지 테스트
- 여러 시즌이 동시에 실패하는 엣지 케이스 테스트
| if state.latestFailedSeason == category { | ||
| state.latestFailedSeason = nil | ||
| state.errorMessage = nil | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
여러 시즌 실패 시 에러 추적 로직을 재검토하세요.
이전 리뷰에서 이 문제가 해결되었다고 표시되었지만, 현재 코드를 보면 여전히 단일 latestFailedSeason만 추적하고 있습니다.
시나리오:
- Spring 실패 →
latestFailedSeason = .spring - Summer 실패 →
latestFailedSeason = .summer(덮어씀) - Spring 성공 →
latestFailedSeason (.summer) != category (.spring)이므로 에러가 유지됨
이전 리뷰 코멘트에서 제안된 Set<MusicSeason> 기반 추적을 사용하여 모든 실패한 시즌을 추적하고, 각 시즌이 성공할 때 해당 시즌만 Set에서 제거하는 방식으로 수정하세요.
다음 스크립트로 이 로직이 올바르게 동작하는지 테스트에서 검증되고 있는지 확인하세요:
🏁 Script executed:
#!/bin/bash
# 여러 시즌이 동시에 실패하고 순차적으로 성공하는 시나리오를 테스트하는지 확인
rg -n "latestFailedSeason|failedSeasons" --type swift -C 3Length of output: 2292
---
🏁 Script executed:
```shell
#!/bin/bash
# Find test files related to HomeReducer
fd -e swift -e kt -e java | grep -i test | grep -i home | head -20
Length of output: 67
🏁 Script executed:
#!/bin/bash
# Search for tests related to latestFailedSeason or error handling
rg -n "latestFailedSeason|failedSeasons" --type swift --type kotlin -g "**/*Test*" -g "**/*Spec*" -C 3Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if there are reducer tests with multi-season scenarios
rg -n "fetchMusicResponse|multiple.*season|season.*fail" --type swift -C 2 | grep -i test -A 5 -B 5Length of output: 0
---
🏁 Script executed:
```shell
#!/bin/bash
# View the test file to check what's being tested
cat "Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift"
Length of output: 6094
여러 시즌의 동시 실패 시 에러 추적 버그를 수정하세요.
코드 검증 결과, 버그가 실제로 존재합니다:
버그 확인:
- Line 26:
var latestFailedSeason: MusicSeason?(단일 값만 추적) - Line 180: 실패 시 항상
latestFailedSeason = category로 덮어씀 - Line 157:
latestFailedSeason == category일 때만 에러 클리어
발생 시나리오:
- Spring 실패 →
latestFailedSeason = .spring, errorMessage 설정 - Summer 실패 →
latestFailedSeason = .summer로 덮어씀 - Spring 성공 →
latestFailedSeason (.summer) ≠ category (.spring)→ 에러가 영구적으로 유지됨
추가 문제:
테스트 파일 검토 결과, 이 시나리오에 대한 테스트가 전혀 없습니다. fetchMusic_failure_setsErrorMessage 테스트는 단일 시즌 실패만 검증하며, 다중 시즌 실패/성공 흐름은 커버되지 않습니다.
필수 수정:
Set<MusicSeason> 또는 유사한 컬렉션을 사용하여 실패한 모든 시즌을 추적하고, 각 시즌 성공 시 해당 시즌만 제거하는 방식으로 변경하세요. 동시에 위 시나리오를 검증하는 테스트를 추가하세요.
🤖 Prompt for AI Agents
In Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift around
lines 26, 157-160 and 180, the reducer only tracks a single latestFailedSeason
which gets overwritten and causes stale errors when multiple seasons fail;
change the storage to a Set<MusicSeason> (e.g., failedSeasons:
Set<MusicSeason>), update failure handling to insert the failed season into the
set (instead of overwriting), update success handling to remove only the
succeeded season from the set and clear errorMessage only when the set becomes
empty, and ensure errorMessage handling still reflects the current failures
(e.g., derive from set or keep last error but clear on empty set); also add a
unit test that simulates two seasons failing then one succeeding (e.g., Spring
fail -> Summer fail -> Spring success) to assert the error clears properly only
when all failures are resolved.
| import AsyncMoya | ||
|
|
||
| public final class MusicSearchRepository: MusicSearchRepositoryProtocol { | ||
| @MainActor private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()]) |
There was a problem hiding this comment.
provider가 메인액터의 격리를 받아야하는 이유가 궁금합니다 ~!
There was a problem hiding this comment.
@Peter1119 @mainactor를 붙여 메인 스레드에서만 접근하도록 보장하는 거여서 Swift 6부터는 이런 동시성 안전 처리가 더 엄격해져서 사용 했습니다
| } | ||
|
|
||
| public func fetchDetailMusic(id: String) async throws -> [Model.ITunesTrack] { | ||
| let data = try await provider.requestAsync(.detailMusic(id: id), decodeTo: ITunesSearchResponseDTO.self) |
There was a problem hiding this comment.
개인적인 생각인데, requestAsync라는 네이밍에서 Async 접미사를 제거하는 것이 어떨까요?
Swift의 async/await를 사용하는 경우, 함수 시그니처에 이미 async 키워드가 있기 때문에 메서드 이름에 'Async'를 중복해서 표기할 필요가 없습니다.
Apple의 공식 API들도 async/await 도입 이후에는 메서드 이름에 async를 명시하지 않는 것이 일반적입니다:
URLSession.data(from:)(기존의dataTask에서 변경)UIImage(named:)등
따라서 provider.request(.detailMusic(id: id), decodeTo: ...) 형태로 사용하면 더 간결하고 Swift의 네이밍 컨벤션에도 부합할 것 같습니다.
|
|
||
| .SPM.asyncMoya, | ||
| .Network(implements: .Networking), | ||
| .Data(implements: .API) |
There was a problem hiding this comment.
Service라는 이름이 다소 추상적이게 느껴져요
의존성 구성으로 보았을때는 클린아키텍처에서 DataSource를 의미하는 것 같은데
다른 개발자가 보았을 때 모듈의 역할을 파악하기 어려울 수 있을 것 같아서 다른 대안도 생각해보셔도 좋을 것 같습니다~!
NetworkService, APIService, RemoteDataSource등 지금은 이런게 생각이 나네요 ~!
|
|
||
| public protocol MusicSearchUseCaseProtocol { | ||
| func searchMusic(searchQuery: String) async throws -> [MusicItem] | ||
| func fetchTrackDetail(id: Int) async throws -> MusicItem |
| public struct MusicItem: Identifiable, Equatable { | ||
| public var id = UUID() | ||
| public let trackId: Int |
There was a problem hiding this comment.
저는 저 토끼에 공감하는게 이미 id가 있기 때문에 동일한 id를 써도 Identifiable과 객체를 인지하는 id로 사용하기에는 충분해보입니다 ~! 물론 다른 의도가 있다면 분리하는 것도 좋습니다.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (4)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (1)
26-27: 다중 시즌 실패 시 에러 상태가 사라집니다.기존 리뷰에서 지적된 것처럼
latestFailedSeason을 단일 값으로 덮어쓰고 있어서, 예: 봄 실패 → 여름 실패 → 여름 성공 순서일 때 봄 실패가 아직 복구되지 않았는데도latestFailedSeason이 nil로 초기화되고errorMessage가 사라집니다. 사용자는 실패 사실을 전혀 확인할 수 없으므로 반드시 다중 실패 시즌을 추적하도록 구조를 바꿔 주세요. 실패 시즌 집합을 관리하고, 해당 시즌이 성공했을 때만 제거하며 모든 실패가 해소됐을 때 에러 메시지를 비워야 합니다.- var latestFailedSeason: MusicSeason? + var failedSeasons: Set<MusicSeason> = [] @@ - if state.latestFailedSeason == category { - state.latestFailedSeason = nil - state.errorMessage = nil - } + state.failedSeasons.remove(category) + if state.failedSeasons.isEmpty { + state.errorMessage = nil + } @@ - state.latestFailedSeason = category + state.failedSeasons.insert(category) state.errorMessage = error.localizedDescriptionAlso applies to: 154-181
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (1)
115-128: 취소 무시 및 기본 ID 0 호출로 인한 상세 조회 문제
try? await clock.sleep가CancellationError를 삼켜서.cancellable구성이 있어도 취소 이후 네트워크 요청이 계속 진행됩니다. 동시에musicItem?.trackId ?? .zero가 초기 상태(또는 trackId 누락)에서 0번 ID로 조회를 시도해 잘못된 곡을 불러오거나 서버에서 실패를 유발할 수 있습니다. 취소를 전파하고, 유효한 ID가 없으면 조기 종료하도록 고쳐주세요.다음 수정안을 참고해 주세요:
case .searchDetailMusic: - return .run { [musicItem = state.musicItem] send in - try? await clock.sleep(for: .seconds(2)) - - let searchDetailMusicResult = await Result { - try await musicDetailUseCase.fetchTrackDetail(id: musicItem?.trackId ?? .zero) - } + return .run { [musicItem = state.musicItem] send in + try await clock.sleep(for: .seconds(2)) + try Task.checkCancellation() + + guard let id = musicItem?.trackId else { + await send(.inner(.detailMusicResponse(.failure(URLError(.badURL))))) + return + } + + let searchDetailMusicResult = await Result { + try await musicDetailUseCase.fetchTrackDetail(id: id) + }Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift (1)
43-45: DependencyKey testValue 타입 불일치Line 43에서
testValue는DependencyKey.Value인MusicSearchUseCaseProtocol을 반환해야 하는데, 현재MockMusicSearchRepository()를 그대로 넣어 타입이 맞지 않아 컴파일이 실패합니다. 목 리포지토리를 주입한MusicSearchUseCase인스턴스를 반환하도록 수정해 주세요.- public static var testValue = MockMusicSearchRepository() + public static var testValue: MusicSearchUseCaseProtocol = + MusicSearchUseCase(repository: MockMusicSearchRepository())Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj (1)
656-656: Release 빌드에서 LogMacro 플러그인 경로가 잘못되었습니다.Release/Stage/Prod 설정의
OTHER_SWIFT_FLAGS가 모두Debug하위 빌드 폴더를 하드코딩하고 있어 플러그인 로드가 실패하며 비-디버그 빌드가 깨집니다. Tuist 매니페스트에서$(CONFIGURATION)을 사용하도록 수정해 다시 생성해야 합니다.- -load-plugin-executable $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/LogMacroMacro#LogMacroMacro + -load-plugin-executable $BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/LogMacroMacro#LogMacroMacro자동 생성 파일이라도, 매니페스트를 통해 위와 같이 경로를 치환해주지 않으면 Release 빌드를 낼 수 없습니다.
Also applies to: 696-696, 739-739, 784-784, 826-826, 863-863, 903-903, 947-947, 990-990, 1146-1146
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
Projects/App/Sources/Di/DIRegistry.swift(3 hunks)Projects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj(9 hunks)Projects/Core/Data/DataInterface/Sources/MusicDetailRepositoryProtocol.swift(1 hunks)Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift(1 hunks)Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj(31 hunks)Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicDetailRepositoryTests.swift(1 hunks)Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift(1 hunks)Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift(1 hunks)Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift(1 hunks)Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift(1 hunks)Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift(1 hunks)Projects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxproj(10 hunks)Projects/Core/Domain/DomainInterface/Sources/MusicDetailUseCaseProtocol.swift(1 hunks)Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift(1 hunks)Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift(1 hunks)Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift(1 hunks)Projects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxproj(24 hunks)Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicDetailUseCaseTests.swift(1 hunks)Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift(1 hunks)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift(1 hunks)Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift(1 hunks)Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift(1 hunks)Tuist/Package.resolved(2 hunks)Tuist/Package.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Projects/App/Sources/Di/DIRegistry.swift
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-10-28T02:24:04.787Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:518-519
Timestamp: 2025-10-28T02:24:04.787Z
Learning: In this project, Tuist automatically manages dependency resources including framework bundles (e.g., Alamofire_Alamofire.bundle). Resource copying configurations that appear only in test targets in the .pbxproj file are intentionally managed by Tuist's build system and do not require manual configuration in other targets.
Applied to files:
Tuist/Package.swiftProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:26:06.959Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/API/Sources/API/MusicSearchAPI.swift:14-22
Timestamp: 2025-10-28T02:26:06.959Z
Learning: In the MusicBandscape project's API layer (Projects/Core/Data/API/Sources/API/MusicSearchAPI.swift), the MusicSearchAPI.description property intentionally returns empty strings because the main API paths are already defined in MusicScapeDomain.url ("search" and "lookup"). The description property is designed to optionally append additional path segments when needed, following the pattern: finalURL = domain.url + urlPath. Empty strings are valid when no additional path segment is required.
Applied to files:
Projects/Core/Domain/DomainInterface/Sources/MusicDetailUseCaseProtocol.swiftProjects/Core/Domain/UseCase/UseCaseTests/Sources/MusicDetailUseCaseTests.swiftProjects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swiftProjects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swiftProjects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swiftProjects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift
📚 Learning: 2025-10-23T01:45:36.632Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 4
File: Projects/Shared/DesignSystem/Derived/Sources/TuistAssets+DesignSystemDebug.swift:35-44
Timestamp: 2025-10-23T01:45:36.632Z
Learning: Tuist-generated files (identified by header comment "Generated using tuist — https://github.com/tuist/tuist") should not be flagged for manual modification, as changes will be overwritten on regeneration. These files are located in Derived/ directories and include asset, bundle, and configuration files.
Applied to files:
Tuist/Package.resolvedProjects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:26:13.122Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxproj:614-624
Timestamp: 2025-10-28T02:26:13.122Z
Learning: project.pbxproj files in the MusicBandscape repository are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the Tuist configuration files (e.g., Project.swift) instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:29:01.028Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Network/Foundations/Foundations.xcodeproj/project.pbxproj:586-592
Timestamp: 2025-10-28T02:29:01.028Z
Learning: In the MusicBandscape repository, .xcodeproj/project.pbxproj files are auto-generated by Tuist. Build settings including plugin flags (e.g., LogMacro plugin loading) should not be manually edited in pbxproj files, as they will be overwritten. Any configuration changes must be made in Tuist manifest files (Project.swift) instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-23T01:43:01.591Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 4
File: Projects/Presentation/Presentation/Presentation.xcodeproj/project.pbxproj:85-114
Timestamp: 2025-10-23T01:43:01.591Z
Learning: In Tuist-managed iOS projects (identified by Tuist/ directory, Project.swift manifest files, and Tuist references in build settings), the .xcodeproj/project.pbxproj files are auto-generated and should not be manually edited. Build phases including 'Dependencies' Copy Files phases are generated by Tuist based on the Project.swift manifests, and manual changes will be overwritten on the next `tuist generate` run. Do not flag or suggest manual modifications to pbxproj files in Tuist projects.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:45.154Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Stage-Info.plist:17-20
Timestamp: 2025-10-28T02:24:45.154Z
Learning: In Tuist-based projects, files under `Derived/` or `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be manually edited. Any configuration changes should be made in the corresponding Project.swift files instead.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:19.454Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/API/API.xcodeproj/project.pbxproj:612-617
Timestamp: 2025-10-28T02:24:19.454Z
Learning: In the MusicBandscape project, .pbxproj files (Xcode project files) are auto-generated by Tuist. Build configuration settings like OTHER_SWIFT_FLAGS and OTHER_CFLAGS in these files should not be flagged as manual coding issues, as they are managed by Tuist's project generation logic.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-28T02:24:36.027Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/Core/Data/API/Derived/InfoPlists/API-Prod-Info.plist:17-18
Timestamp: 2025-10-28T02:24:36.027Z
Learning: In the MusicBandscape project, Info.plist files located in `Derived/InfoPlists/` directories are auto-generated by Tuist and should not be flagged for manual modification, including CFBundlePackageType settings.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxproj
📚 Learning: 2025-10-30T05:07:37.049Z
Learnt from: Roy-wonji
Repo: SpartCodig-iOS/MusicBandscape PR: 6
File: Projects/App/MusicBandscape.xcodeproj/xcshareddata/xcschemes/MusicBandscape.xcscheme:93-96
Timestamp: 2025-10-30T05:07:37.049Z
Learning: In the MusicBandscape project, .xcscheme files under xcshareddata directories are auto-generated by Tuist. Scheme configurations (including buildConfiguration settings for ArchiveAction, ProfileAction, etc.) should be modified in the Tuist manifest files (Project.swift) rather than directly editing the .xcscheme files, as manual changes will be overwritten on the next `tuist generate` run.
Applied to files:
Projects/Core/Data/Repository/Repository.xcodeproj/project.pbxprojProjects/Core/Domain/DomainInterface/DomainInterface.xcodeproj/project.pbxprojProjects/Core/Domain/UseCase/UseCase.xcodeproj/project.pbxprojProjects/Core/Data/DataInterface/DataInterface.xcodeproj/project.pbxproj
🧬 Code graph analysis (17)
Projects/Core/Domain/DomainInterface/Sources/MusicDetailUseCaseProtocol.swift (2)
Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift (1)
fetchTrackDetail(28-35)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
fetchTrackDetail(30-32)
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicDetailUseCaseTests.swift (2)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift (1)
fetchTrackDetail(28-35)
Projects/Core/Data/DataInterface/Sources/MusicDetailRepositoryProtocol.swift (2)
Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (1)
fetchDetailMusic(25-28)Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift (1)
fetchDetailMusic(20-23)
Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift (3)
Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (1)
fetchMusic(25-28)Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift (1)
searchMusic(27-30)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
searchMusic(26-28)
Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift (3)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (2)
setResult(21-23)fetchMusic(25-28)Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift (1)
fetchMusic(19-22)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (2)
Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (3)
fetchTrackDetail(36-38)stub(42-61)stub(65-83)Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift (1)
fetchTrackDetail(28-35)
Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift (1)
Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (1)
fetchDetailMusic(25-28)
Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift (2)
Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (1)
fetchMusic(25-28)Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift (1)
fetchMusic(19-22)
Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift (2)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift (1)
searchMusic(27-30)
Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift (2)
Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift (1)
searchMusic(27-30)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
searchMusic(26-28)
Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (2)
Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (1)
setResult(21-23)Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift (1)
fetchMusic(19-22)
Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (2)
Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (1)
setResult(21-23)Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift (1)
fetchDetailMusic(20-23)
Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift (3)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
fetchTrackDetail(30-32)Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (1)
fetchDetailMusic(25-28)Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift (1)
fetchDetailMusic(20-23)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (3)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (4)
handleViewAction(98-107)handleAsyncAction(109-132)handleInnerAction(144-160)handleNavigationAction(134-142)Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
searchMusic(26-28)Projects/Presentation/Home/HomeTests/Sources/HomeReducerTests.swift (1)
searchMusic(32-34)
Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift (3)
Projects/Presentation/Detail/DetailTests/Sources/DetailReducerTests.swift (1)
searchMusic(26-28)Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift (1)
fetchMusic(25-28)Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift (1)
fetchMusic(19-22)
Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicDetailRepositoryTests.swift (3)
Projects/Core/Data/Model/Sources/ITunesSearchResponseDTO.swift (1)
mock(75-101)Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift (2)
setResult(21-23)fetchDetailMusic(25-28)Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift (1)
fetchDetailMusic(20-23)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift (2)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift (2)
handleAsyncAction(111-136)handleNavigationAction(138-147)Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift (1)
fetchTrackDetail(28-35)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Home/Sources/Main /Reducer/HomeReducer.swift
[Warning] 25-25: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
Projects/Presentation/Detail/Sources/Reducer/DetailReducer.swift
[Warning] 41-41: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 48-48: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 54-54: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 60-60: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
Projects/Core/Data/Repository/Sources/Detail/MockMusicDetailRepository.swift
Outdated
Show resolved
Hide resolved
| @MainActor private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()]) | ||
|
|
||
| nonisolated public init() {} | ||
|
|
||
| public func fetchDetailMusic(id: String) async throws -> [Model.ITunesTrack] { | ||
| let data = try await provider.request(.detailMusic(id: id), decodeTo: ITunesSearchResponseDTO.self) | ||
| return data.results |
There was a problem hiding this comment.
MainActor 격리로 인한 컴파일 불가
Line 16에서 provider를 @MainActor로 선언하면, Line 21의 fetchDetailMusic이 메인 액터 격리와 맞지 않아 'Main actor-isolated property 'provider' can only be referenced on the main actor' 컴파일 오류가 발생합니다. @MainActor를 제거하거나 메서드/타입 전체를 메인 액터로 맞춰 주세요.
- @MainActor private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()])
+ private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()])📝 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.
| @MainActor private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()]) | |
| nonisolated public init() {} | |
| public func fetchDetailMusic(id: String) async throws -> [Model.ITunesTrack] { | |
| let data = try await provider.request(.detailMusic(id: id), decodeTo: ITunesSearchResponseDTO.self) | |
| return data.results | |
| private let provider = MoyaProvider<MusicSearchService>(plugins: [MoyaLoggingPlugin()]) | |
| nonisolated public init() {} | |
| public func fetchDetailMusic(id: String) async throws -> [Model.ITunesTrack] { | |
| let data = try await provider.request(.detailMusic(id: id), decodeTo: ITunesSearchResponseDTO.self) | |
| return data.results |
🤖 Prompt for AI Agents
In Projects/Core/Data/Repository/Sources/Detail/MusicDetailRepository.swift
around lines 16–22, the property `@MainActor private let provider` is main-actor
isolated but `fetchDetailMusic` is nonisolated, causing the compiler error
"'Main actor-isolated property 'provider' can only be referenced on the main
actor'"; fix by making the isolation consistent: either remove `@MainActor` from
the `provider` declaration so it can be used from the current nonisolated async
method, or mark the method/type as main-actor (add `@MainActor` to
`fetchDetailMusic` or the enclosing type) so accesses to `provider` occur on the
main actor—pick one approach and apply it consistently across the property and
method.
| return MusicDetailUseCase(repository: repository) | ||
| } | ||
|
|
||
| public static var testValue = MusicDetailRepository() |
There was a problem hiding this comment.
testValue 반환 타입을 프로토콜에 맞게 수정해주세요
testValue가 MusicDetailUseCaseProtocol을 요구하는데 MusicDetailRepository()를 그대로 반환해 컴파일이 되지 않습니다. 유즈케이스 인스턴스를 만들어 반환해야 합니다.
- public static var testValue = MusicDetailRepository()
+ public static var testValue: MusicDetailUseCaseProtocol = MusicDetailUseCase(
+ repository: MockMusicDetailRepository()
+ )📝 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.
| public static var testValue = MusicDetailRepository() | |
| public static var testValue: MusicDetailUseCaseProtocol = MusicDetailUseCase( | |
| repository: MockMusicDetailRepository() | |
| ) |
🤖 Prompt for AI Agents
In Projects/Core/Domain/UseCase/Sources/Detail/MusicDetailUseCase.swift around
line 47, the static testValue currently returns a MusicDetailRepository instance
but the protocol requires a MusicDetailUseCaseProtocol; replace the repository
return with an instance of the use case implementation that conforms to
MusicDetailUseCaseProtocol (e.g., create and return MusicDetailUseCase,
supplying any required repository/dependencies), ensuring the return type
matches MusicDetailUseCaseProtocol so it compiles.
# Conflicts: # Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift # Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift # Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift # Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift # Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift # Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift # Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift
* [feat]: 검색 모듈 구현 #6 * [feat]: 검색 모듈 컴포넌트 구현 #6 * [feat]: 검색 코디네이터 구현 #6 * [feat]: tabbar 모듈 및 tabbar 구현 #6 * [feat]: tabbar 추가 에 따른 코드 수정 및 검색하는 repository 수정 #6 # Conflicts: # Projects/Core/Data/DataInterface/Sources/MusicSearchRepositoryProtocol.swift # Projects/Core/Data/Repository/RepositoryTests/Sources/MockMusicSearchRepositoryTests.swift # Projects/Core/Data/Repository/Sources/Search/MockMusicSearchRepository.swift # Projects/Core/Data/Repository/Sources/Search/MusicSearchRepository.swift # Projects/Core/Domain/DomainInterface/Sources/MusicSearchUseCaseProtocol.swift # Projects/Core/Domain/UseCase/Sources/Search/MusicSearchUseCase.swift # Projects/Core/Domain/UseCase/UseCaseTests/Sources/MusicSearchUseCaseTests.swift * [chore]:service에서ㅏ networkservice 로 모듈 이름 변경 * [chore]:service에서ㅏ networkservice 로 모듈 이름 변경 * [feat]: 검색 어 관련 추가 작업 구현 * [docs] Read me update * Update README.md * [chore]:pr 수정 * [chore]:pr 수정 * [chore]: 호출 방식 수정
🔗 관련 이슈
✨ 작업 내용
📸 Showcase
Close Issues 🔒 (닫을 Issue)
Close #2 , #3
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항
기타