Skip to content

feat: 검색 화면 구현#94

Merged
clxxrlove merged 28 commits into
developfrom
BOOK-148-feature/#93
Jul 21, 2025
Merged

feat: 검색 화면 구현#94
clxxrlove merged 28 commits into
developfrom
BOOK-148-feature/#93

Conversation

@clxxrlove
Copy link
Copy Markdown
Member

@clxxrlove clxxrlove commented Jul 17, 2025

🔗 관련 이슈

📘 작업 유형

  • ✨ Feature (기능 추가)
  • 🐞 Bugfix (버그 수정)
  • 🔧 Refactor (코드 리팩토링)
  • ⚙️ Chore (환경 설정)
  • 📝 Docs (문서 작성 및 수정)
  • ✅ Test (기능 테스트)
  • 🎨 style (코드 스타일 수정)

📙 작업 내역

  • 최근 검색어를 가져오기 위한 기능 추가
  • 검색 화면 구현

구현 예정 내역

  • 검색 결과를 가져오기 위한 기능 추가
  • 최근 검색어 기능 구현 (Create / Read / Delete)
  • 검색 결과 화면 구현 / 무한 스크롤 적용

🧪 테스트 내역

  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

🎨 스크린샷 또는 시연 영상 (선택)

기능 미리보기
검색 화면
ScreenRecording_07-20-2025.01-59-31_1.MP4

Summary by CodeRabbit

Summary by CodeRabbit

  • 신규 기능

    • 최근 검색어 저장 및 관리 기능이 추가되었습니다. 최근 검색어를 최대 10개까지 저장, 불러오기, 삭제할 수 있습니다.
    • 검색 화면이 새롭게 도입되어, 최근 검색어와 검색 결과를 구분하여 표시합니다.
    • 검색 결과 셀, 최근 검색어 셀, 섹션 헤더 등 검색 관련 UI 컴포넌트가 추가되었습니다.
    • 검색 버튼이 홈 화면에 추가되어, 검색 화면으로 이동할 수 있습니다.
    • 책 검색 기능이 도입되어, 다양한 검색 옵션과 페이징을 지원합니다.
    • UserDefaults 기반의 KeyValueStorage가 추가되어 데이터 저장 방식이 다양해졌습니다.
  • UI 개선

    • 텍스트 필드와 라벨 컴포넌트에 새로운 속성 및 메서드가 추가되어 스타일 조정이 용이해졌습니다.
    • 인셋 및 컬러 등 디자인 시스템 요소가 확장되었습니다.
  • 기타

    • 의존성 주입 컨테이너에 검색 관련 저장소 및 유스케이스가 등록되었습니다.
    • 네트워크 및 인증 관련 프로바이더 구성이 개선되고, 토큰 리프레시 로직이 안정화되었습니다.
    • 외부 라이브러리 Kingfisher가 추가되어 이미지 로딩 기능이 강화되었습니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 17, 2025

"""

Walkthrough

도서 검색(SearchFlow) 화면과 관련된 주요 UI 컴포넌트, 뷰모델, 저장소, 유스케이스, DI 등록, Coordinator 등 전체적인 검색 화면의 구조와 흐름이 새롭게 추가되었습니다. 검색 기록 관리, 검색 결과 표시, DI 및 Coordinator 연결, 그리고 관련 셀/헤더 뷰 등이 구현되었습니다.

Changes

파일/그룹 변경 요약
src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift
src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift
검색 기록 저장소 및 최근 검색어 조회 유스케이스 프로토콜 및 구현체 추가
src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift UserDefaults 기반 최근 검색어 저장소 구현 추가
src/Projects/BKData/Sources/DataAssembly.swift
src/Projects/BKDomain/Sources/DomainAssembly.swift
src/Projects/BKStorage/Sources/StorageAssembly.swift
DI 컨테이너에 최근 검색어 저장소, 유스케이스, UserDefaultsStorage 등록 및 명명된 의존성 처리
src/Projects/BKStorage/Sources/Storage/UserDefaultsStorage.swift UserDefaults 기반 KeyValueStorage 구현체 추가
src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift
src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift
텍스트필드 타입 변경, 색상 적용 로직 개선, 검색 버튼 색상 동적 처리 등 리팩토링 및 기능 확장
src/Projects/BKDesign/Sources/Components/BKLabel.swift attributedText 및 fontStyle 설정 메서드 추가
src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BKInset.swift inset2(8) 상수 추가
src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift
src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift
홈 화면에 검색 버튼 추가 및 Coordinator에 검색 플로우 진입 메서드 추가
src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift SearchCoordinator 클래스 신규 추가 및 검색 플로우 관리
src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift
최근 검색어 셀, 검색 결과 셀, 섹션 헤더 뷰 등 검색 관련 UI 컴포넌트 추가
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift 검색 화면 전체 View 및 DiffableDataSource, 섹션 및 셀 배치, 상태별 UI 처리 등 구현
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift 검색 뷰컨트롤러 및 이벤트/상태 바인딩, Combine 기반 처리 추가
src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift 검색 뷰모델, 상태/액션/SideEffect 및 Combine 기반 비즈니스 로직 추가
src/.swiftlint.yml function_body_length 룰에서 error 임계값(50줄) 제거, warning(30줄)만 유지
src/Projects/BKData/Sources/API/BookAPI.swift 신규 BookAPI enum 추가 및 RequestTarget 프로토콜 구현
src/Projects/BKData/Sources/DTO/Request/SearchBookRequestDTO.swift 도서 검색 요청 DTO 추가 및 DictionaryRepresentable 프로토콜 적용
src/Projects/BKData/Sources/DTO/Response/SearchBookResponseDTO.swift 도서 검색 응답 DTO 추가
src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift DictionaryRepresentable 프로토콜 및 snake_case 변환 기능 추가
src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift BookRepository 구현체 추가, 도서 검색 API 호출 및 결과 매핑
src/Projects/BKDomain/Sources/Entity/Book.swift Book 엔티티 추가
src/Projects/BKDomain/Sources/Interface/Repository/BookRepository.swift BookRepository 프로토콜 추가 (search 메서드 포함)
src/Projects/BKDomain/Sources/VO/SearchBookParameters.swift 도서 검색 파라미터 VO 추가
src/Projects/BKDomain/Sources/VO/SearchBookResult.swift SearchBookResult DTO 및 Book 변환 확장 추가
src/Projects/BKDomain/Sources/VO/SearchCoverType.swift
src/Projects/BKDomain/Sources/VO/SearchQueryType.swift
src/Projects/BKDomain/Sources/VO/SearchSortType.swift
src/Projects/BKDomain/Sources/VO/SearchTarget.swift
도서 검색 관련 VO enum들 추가 (CoverType, QueryType, SortType, Target)
src/Projects/BKDomain/Sources/Interface/Usecase/DeleteRecentSearchUseCase.swift
src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift
src/Projects/BKDomain/Sources/Interface/Usecase/SearchBookUseCase.swift
src/Projects/BKDomain/Sources/Interface/Usecase/StoreRecentSearchUseCase.swift
도서 검색 및 최근 검색어 관련 UseCase 프로토콜 추가
src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift
src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift
src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift
UseCase 기본 구현체 추가
src/Projects/BKData/Sources/Repository/DefaultAuthRepository.swift NetworkProvider 분리 (defaultProvider, oauthProvider) 및 DI 반영
src/Projects/BKData/Sources/DataAssembly.swift DI에 RecentSearchRepository, BookRepository, OAuth 명명된 NetworkProvider 등록 추가
src/Projects/BKStorage/Sources/Storage/UserDefaultsStorage.swift UserDefaultsStorage 구현 추가 (KeyValueStorage 준수)
src/Projects/BKStorage/Sources/StorageAssembly.swift KeyValueStorage 명명 등록 추가 (Keychain, UserDefaults) 및 TokenProvider, TokenStore DI 수정
src/Projects/BKCore/Sources/DiContainer/DIContainer.swift DIContainer 기본 이름 "default" → "Default" 변경
src/Projects/BKNetwork/Sources/Extension/URLRequest+.swift URLRequest 쿼리 아이템 생성 시 인코딩 제거 및 map 사용으로 변경
src/Projects/BKNetwork/Sources/NetworkAssembly.swift NetworkProvider 명명 변경 ("default" → "Default", "oauth" → "OAuth") 및 refresh 토큰 처리 로직 변경 (직접 네트워크 요청 및 토큰 저장)
src/Projects/BKPresentation/Project.swift
src/Tuist/Package.swift
src/Tuist/Package.resolved
Kingfisher 라이브러리 추가 및 의존성 등록
src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift
검색 관련 UI 컴포넌트 (최근 검색어 셀, 검색 결과 셀, 섹션 헤더) 추가
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift 검색 화면 View 및 상태별 UI 처리, 이벤트 발행 구현
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift 검색 뷰컨트롤러, 이벤트/상태 바인딩, Combine 기반 처리 구현
src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift 검색 뷰모델, 상태/액션/SideEffect 및 Combine 기반 비즈니스 로직 구현
src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift 홈 화면에 검색 버튼 추가 및 액션 연결
src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift 검색 버튼 탭 처리 메서드 추가 (SearchCoordinator 시작)
src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift SearchCoordinator 클래스 추가, 검색 플로우 관리
src/Projects/BKPresentation/Sources/AuthFlow/Coordinator/LoginCoordinator.swift LoginCoordinator start() 내 coordinator 할당 추가
src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift LoginCoordinator 약한 참조 추가 및 로그인 상태 변경 시 popAndFinish 호출
src/.swiftlint.yml function_body_length 룰에서 error 임계값(50줄) 제거, warning(30줄)만 유지

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HomeViewController
    participant MainFlowCoordinator
    participant SearchCoordinator
    participant SearchViewController
    participant SearchViewModel
    participant FetchRecentSearchUseCase
    participant RecentSearchRepository
    participant UserDefaultsStorage

    User->>HomeViewController: 검색 버튼 탭
    HomeViewController->>MainFlowCoordinator: didTapSearchButton()
    MainFlowCoordinator->>SearchCoordinator: SearchCoordinator 생성 및 start()
    SearchCoordinator->>SearchViewController: SearchViewController 생성 및 push
    SearchViewController->>SearchViewModel: .onAppear 액션 전달
    SearchViewModel->>FetchRecentSearchUseCase: execute()
    FetchRecentSearchUseCase->>RecentSearchRepository: load()
    RecentSearchRepository->>UserDefaultsStorage: load(for: key)
    UserDefaultsStorage-->>RecentSearchRepository: [String] 반환
    RecentSearchRepository-->>FetchRecentSearchUseCase: [String] 반환
    FetchRecentSearchUseCase-->>SearchViewModel: [String] 반환(Publisher)
    SearchViewModel->>SearchViewController: State 업데이트
    SearchViewController->>SearchView: applySnapshot(with: state)
Loading

Assessment against linked issues

Objective Addressed Explanation
도서 검색 화면 구현 (#93)

Suggested labels

✨ feat

Suggested reviewers

  • doyeonk429
    """
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-148-feature/#93

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or @coderabbitai 요약 to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@clxxrlove clxxrlove marked this pull request as draft July 17, 2025 10:31
@github-actions github-actions Bot requested a review from doyeonk429 July 17, 2025 10:31
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (8)
src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift (1)

4-6: 에러 처리 전략을 명확히 하는 것을 권장합니다.

현재 메서드들이 에러를 던지지 않는 것으로 정의되어 있는데, 구현체에서 try?로 에러를 무시하는 것을 확인했습니다. 도메인 레이어에서 에러 처리 방식을 명시적으로 정의하는 것이 좋을 것 같습니다.

다음과 같은 방식을 고려해보세요:

public protocol RecentSearchRepository {
    func load() -> [String]
-    func save(query: String)
-    func clear()
+    func save(query: String) throws
+    func clear() throws
}

또는 Result 타입을 사용한 방식:

public protocol RecentSearchRepository {
    func load() -> [String]
-    func save(query: String)
-    func clear()
+    func save(query: String) -> Result<Void, Error>
+    func clear() -> Result<Void, Error>
}
src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift (1)

18-23: SearchViewModel의 의존성 주입을 고려해보세요.

현재 SearchViewModel을 직접 생성하고 있는데, 다른 ViewModel들과 일관성을 위해 의존성 주입을 사용하는 것이 좋겠습니다.

- let searchViewController = SearchViewController(viewModel: SearchViewModel())
+ let searchViewController = SearchViewController(viewModel: container.resolve(SearchViewModel.self))

또한 자식 coordinator 관리를 위한 정리 로직도 고려해볼 수 있습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (1)

15-20: Clear 버튼의 액션 핸들러 구현이 누락되었습니다.

버튼이 생성되었지만 실제 동작을 위한 target-action이나 closure 기반 핸들러가 구현되지 않았습니다.

다음과 같은 방식으로 구현할 수 있습니다:

// 1. Closure 기반 접근
var onClearButtonTapped: ((String) -> Void)?

// 2. Delegate 패턴 접근
protocol RecentKeywordCellDelegate: AnyObject {
    func recentKeywordCell(_ cell: RecentKeywordCell, didTapClearFor keyword: String)
}

구현 방식에 대한 가이드를 제공해드릴까요?

src/Projects/BKStorage/Sources/Storage/UserDefaultsStorage.swift (1)

6-28: 일관성과 성능 개선을 위한 제안

현재 구현은 모든 데이터 타입에 대해 JSONEncoder를 사용하고 있습니다. 하지만 KeychainKeyValueStorage에서는 String과 Data 타입에 대해 직접 변환을 수행합니다. 일관성과 성능 향상을 위해 동일한 최적화를 적용하는 것을 고려해보세요.

다음과 같이 encode 메서드를 추가하여 성능을 최적화할 수 있습니다:

+    private func encode<T: Encodable>(_ data: T) throws -> Data {
+        if let string = data as? String {
+            return Data(string.utf8)
+        } else if let rawData = data as? Data {
+            return rawData
+        } else {
+            return try JSONEncoder().encode(data)
+        }
+    }

     public func save<T: Encodable>(_ data: T, for account: String) throws {
-        let encoded = try JSONEncoder().encode(data)
+        let encoded = try encode(data)
         userDefaults.set(encoded, forKey: account)
     }
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift (1)

38-49: 중복된 조건 확인 로직 개선

count == 0 체크가 두 번 수행되고 있으며, SwiftLint 경고를 비활성화하는 대신 코드를 개선하는 것이 좋겠습니다.

다음과 같이 개선할 수 있습니다:

         case .result(let count):
-            // swiftlint:disable:next empty_count
             isHidden = count == 0
-            // swiftlint:disable:next empty_count
-            if count > 0 {
+            if !isHidden {
                 titleLabel.setAttributedText(
                     with: makeResultString(
                         count: count
                     )
                 )
             }
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (1)

29-43: TODO 구현 지원 및 unused parameter 수정

검색 기능 구현이 남아있는 것을 확인했습니다. 또한 클로저에서 사용되지 않는 매개변수가 있습니다.

검색 기능 구현을 도와드릴 수 있습니다. 새로운 이슈를 생성하시겠습니까?

unused parameter 경고를 수정하려면:

-            .sink { [weak self] event in
+            .sink { [weak self] _ in
                 // TODO: - 검색 기능 구현
 //                self?.viewModel.send()
             }
src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (2)

13-17: SearchResult 구조체 설계 개선을 고려해보세요.

현재 구현은 작동하지만, UIImage를 직접 사용하는 것보다는 이미지 URL이나 식별자를 사용하고 이미지 로딩은 별도로 처리하는 것을 고려해보세요. 이렇게 하면 테스트하기 더 쉬워지고 모델과 UI 계층의 분리가 더 명확해집니다.

 struct SearchResult: Hashable {
-    let thumbnail: UIImage
+    let thumbnailURL: String
     let title: String
     let description: String
 }

19-89: SearchViewModel 구현이 잘 구조화되어 있습니다.

Redux 패턴을 적절히 적용하여 단방향 데이터 플로우를 구현했고, Combine을 사용한 반응형 프로그래밍 패턴이 잘 적용되었습니다. 메모리 관리와 의존성 주입도 올바르게 처리되었습니다.

향후 개선사항으로 사이드 이펙트 처리 시 에러 핸들링 추가를 고려해보세요.

 func handle(_ effect: SideEffect) -> AnyPublisher<Action, Never> {
     switch effect {
     case .recentQueries:
         fetchRecentSearchUseCase.execute()
             .map(Action.fetchRecentQueriesSuccessed)
+            .catch { _ in Just(Action.fetchRecentQueriesSuccessed([])) }
             .eraseToAnyPublisher()
     }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e4f519 and fe6863d.

📒 Files selected for processing (22)
  • src/.swiftlint.yml (0 hunks)
  • src/Projects/BKData/Sources/DataAssembly.swift (1 hunks)
  • src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift (1 hunks)
  • src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift (6 hunks)
  • src/Projects/BKDesign/Sources/Components/BKLabel.swift (1 hunks)
  • src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift (2 hunks)
  • src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BKInset.swift (1 hunks)
  • src/Projects/BKDomain/Sources/DomainAssembly.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift (4 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (1 hunks)
  • src/Projects/BKStorage/Sources/Storage/UserDefaultsStorage.swift (1 hunks)
  • src/Projects/BKStorage/Sources/StorageAssembly.swift (2 hunks)
💤 Files with no reviewable changes (1)
  • src/.swiftlint.yml
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#69
File: src/Projects/BKDesign/PreviewApp/Sources/View/CatalogViewController.swift:57-70
Timestamp: 2025-07-10T14:01:04.933Z
Learning: clxxrlove는 Reed-iOS 프로젝트에서 테스트/데모용 코드와 프로덕션 코드에 대해 다른 코드 품질 기준을 적용한다. 테스트용 뷰에서는 안전성 검증 등의 엄격한 코드 품질 요구사항을 적용하지 않는 것을 선호한다.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/Constant/PresentationKeys.swift:3-5
Timestamp: 2025-07-07T13:53:13.006Z
Learning: clxxrlove prefers using enum over struct for constants namespace due to better runtime performance, memory layout, and compiler optimizations. Enum prevents instantiation at compile time without needing private init().
src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BKInset.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#80
File: src/Projects/BKDesign/Sources/Components/BottomSheet/BKBottomSheetViewController.swift:148-148
Timestamp: 2025-07-14T05:23:04.937Z
Learning: Reed-iOS 프로젝트의 BKBottomSheetViewController에서 버튼 영역 높이(84)는 디자인 시스템에 기반한 의도적인 고정값입니다. clxxrlove에 따르면 작은 기기에서도 동일한 크기가 유지되어야 하며, 동적 계산보다는 상수화만 필요합니다.
src/Projects/BKData/Sources/DataAssembly.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#66
File: src/Projects/BKData/Sources/DataAssembly.swift:49-60
Timestamp: 2025-07-09T02:32:03.801Z
Learning: clxxrlove prefers separating protocol registrations for RefreshHandler and AuthRepository even when using the same implementation (DefaultAuthRepository) to maintain Interface Segregation Principle, where RefreshHandler focuses on token refresh and AuthRepository handles broader authentication operations.
src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKDomain/Sources/DomainAssembly.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift (1)
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#68
File: src/Projects/BKDesign/Sources/Components/Button/BKButtonGroup.swift:86-92
Timestamp: 2025-07-10T08:21:16.126Z
Learning: BKButtonGroup의 horizontal 레이아웃에서 stackView.distribution을 .fillProportionally로 설정한 것은 의도된 디자인입니다. doyeonk429는 버튼들이 콘텐츠 길이에 따라 다른 너비를 가지는 것을 선호합니다.
src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift (1)
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#63
File: src/Projects/BKDesign/Sources/Extensions/UIColor+.swift:50-92
Timestamp: 2025-07-08T17:17:37.703Z
Learning: BKDesign 프로젝트에서 UIColor+ 확장의 시맨틱 컬러 메서드들(.bkContentColor, .bkBorderColor, .bkDividerColor, .bkBaseColor)이 현재 다크 모드에서도 라이트 모드 색상을 반환하는 것은 의도적인 구현입니다. 아직 다크 모드가 구현되지 않았지만, 미래의 다크 모드 지원을 위한 코드 구조로 준비되어 있습니다.
src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
🧬 Code Graph Analysis (13)
src/Projects/BKData/Sources/DataAssembly.swift (1)
src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (1)
  • register (25-46)
src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift (1)
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1)
  • execute (13-15)
src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift (1)
src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift (3)
  • load (15-17)
  • save (19-24)
  • clear (26-28)
src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift (1)
src/Projects/BKPresentation/Sources/MainFlow/Setting/Coordinator/SettingCoordinator.swift (1)
  • start (18-22)
src/Projects/BKDomain/Sources/DomainAssembly.swift (1)
src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (1)
  • register (25-46)
src/Projects/BKStorage/Sources/StorageAssembly.swift (1)
src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (1)
  • register (25-46)
src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift (1)
src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift (1)
  • didTapSearchButton (36-43)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (3)
src/Projects/BKDesign/Sources/Extensions/UIColor+.swift (1)
  • bkContentColor (52-63)
src/Projects/BKDesign/Sources/Extensions/UIView+.swift (1)
  • addSubviews (7-9)
src/Projects/BKDesign/Sources/Components/BKLabel.swift (1)
  • setText (80-83)
src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift (2)
src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift (1)
  • typeDidChanged (60-63)
src/Projects/BKDesign/Sources/Extensions/UIColor+.swift (1)
  • bkContentColor (52-63)
src/Projects/BKStorage/Sources/Storage/UserDefaultsStorage.swift (1)
src/Projects/BKStorage/Sources/Storage/KeychainKeyValueStorage.swift (1)
  • encode (65-73)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift (2)
src/Projects/BKDesign/Sources/Components/BKLabel.swift (3)
  • setFontStyle (75-78)
  • setText (80-83)
  • setAttributedText (71-73)
src/Projects/BKDesign/Sources/Extensions/UIColor+.swift (1)
  • bkContentColor (52-63)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (1)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift (1)
  • applySnapshot (63-89)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (3)
src/Projects/BKDesign/Sources/Extensions/UIColor+.swift (1)
  • bkContentColor (52-63)
src/Projects/BKDesign/Sources/Extensions/UIView+.swift (1)
  • addSubviews (7-9)
src/Projects/BKDesign/Sources/Components/BKLabel.swift (1)
  • setText (80-83)
🪛 SwiftLint (0.57.0)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift

[Warning] 39-39: TODOs should be resolved (- 검색 기능 구현)

(todo)


[Warning] 38-38: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (51)
src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BKInset.swift (1)

7-7: 새로운 inset 상수 추가 승인

기존 디자인 시스템의 네이밍 패턴과 일관되게 inset2 상수가 추가되었습니다. 8의 배수 시스템을 따르는 값으로 적절합니다.

src/Projects/BKDomain/Sources/Interface/Usecase/FetchRecentSearchUseCase.swift (1)

5-7: 깔끔한 프로토콜 정의 승인

Never 타입을 사용하여 에러가 발생하지 않음을 명확히 표현했고, 최근 검색어 조회라는 목적에 적합한 간단한 인터페이스입니다.

src/Projects/BKDomain/Sources/DomainAssembly.swift (1)

63-68: DI 등록 패턴 승인 및 구현체 확인 완료

  • src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift에서
    DefaultFetchRecentSearchUseCaseFetchRecentSearchUseCase 프로토콜을 구현하는 구조체로 정의되어 있습니다.

위 확인을 바탕으로 변경 사항을 승인합니다.

src/Projects/BKPresentation/Sources/MainFlow/MainFlowCoordinator.swift (1)

36-43: SearchCoordinator 구현 확인 및 일관된 패턴 승인

  • SearchCoordinator 클래스가
    src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift
    파일 5행에 final class SearchCoordinator: Coordinator { … } 로 정의되어 있음을 확인했습니다.
  • didTapSearchButton() 메서드는 기존 didTapSettingButton() 패턴과 일치하며, childCoordinators 관리도 올바르게 구현되어 있습니다.

따라서 추가 수정 없이 변경사항을 승인합니다.

src/Projects/BKData/Sources/DataAssembly.swift (1)

57-62: RecentSearchRepository DI 설정 검증 및 승인

DefaultRecentSearchRepository 구현체와 “UserDefaults” named storage 등록이 모두 정상적으로 확인되었습니다. 추가 조치 필요 없습니다.

  • DefaultRecentSearchRepository 정의:
    src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift:6
  • UserDefaults named storage 등록:
    src/Projects/BKStorage/Sources/StorageAssembly.swift
src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift (1)

3-7: 프로토콜 설계가 깔끔하고 명확합니다.

최근 검색어 관리를 위한 기본적인 CRUD 연산이 잘 정의되어 있습니다. 동기적 메서드 호출로 설계된 것이 적절해 보입니다.

src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift (3)

44-44: 초기화 과정에서 configure() 호출 추가가 적절합니다.

setup() 후에 configure()를 호출하여 초기 상태를 올바르게 설정하는 것이 좋은 접근입니다.


60-63: 타입 변경 시 UI 업데이트가 일관성 있게 구현되었습니다.

typeDidChanged() 오버라이드를 통해 border color와 search button color를 동시에 업데이트하는 것이 적절합니다. 베이스 클래스의 패턴을 잘 따르고 있습니다.


71-73: configure() 메서드 구현이 명확합니다.

초기화 시점에서 search button의 색상을 설정하는 역할이 분명하고, typeDidChanged()와 일관성을 유지하고 있습니다.

src/Projects/BKPresentation/Sources/MainFlow/Home/HomeViewController.swift (4)

24-28: 검색 버튼 구현이 기존 패턴과 일관성을 유지합니다.

기존 settingButton과 동일한 방식으로 구현되어 있어 코드의 일관성이 좋습니다.


44-46: 스택 뷰 구성이 적절합니다.

settingButtonsearchButton을 vertical stack view에 추가하여 UI 구성이 깔끔합니다.


61-61: 액션 바인딩이 올바르게 구현되었습니다.

기존 settingButton과 동일한 패턴으로 target-action을 설정하여 일관성을 유지하고 있습니다.


72-74: Coordinator 패턴 통합이 적절합니다.

openSearch() 메서드가 coordinator의 didTapSearchButton() 메서드를 호출하는 것이 MVP 패턴과 Coordinator 패턴을 잘 따르고 있습니다.

src/Projects/BKDesign/Sources/Components/BKLabel.swift (2)

71-73: setAttributedText(with:) 메서드 구현이 명확합니다.

직접적으로 super.attributedText를 설정하는 방식이 적절하고, 기존의 @available(*, unavailable) 제약사항을 우회하는 명시적인 인터페이스를 제공합니다.


75-78: setFontStyle(style:) 메서드가 상태 관리를 올바르게 수행합니다.

fontStyle 프로퍼티를 업데이트하고 apply() 메서드를 호출하여 변경사항을 반영하는 것이 기존 패턴과 일관성을 유지합니다.

src/Projects/BKStorage/Sources/StorageAssembly.swift (4)

11-16: KeyValueStorage의 네임드 등록이 적절합니다.

기존 KeychainKeyValueStorage를 "Keychain" 이름으로 등록하여 명시적인 구분을 제공합니다.


18-23: UserDefaultsStorage 추가가 검색 기능 요구사항을 잘 지원합니다.

최근 검색어 저장을 위한 UserDefaults 기반 저장소 구현이 적절합니다. 네임드 등록을 통해 Keychain 저장소와 명확히 구분됩니다.


29-29: @Autowired의 네임드 주입이 올바르게 구현되었습니다.

기존 TokenProvider가 여전히 Keychain 저장소를 사용하도록 명시적으로 지정한 것이 적절합니다.


38-38: TokenStore의 네임드 주입도 일관성을 유지합니다.

TokenProvider와 동일하게 "Keychain" 이름을 사용하여 기존 동작을 유지합니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/Coordinator/SearchCoordinator.swift (2)

5-8: 표준 Coordinator 패턴 구현이 적절합니다.

Coordinator 프로토콜을 준수하고 필수 프로퍼티들을 올바르게 선언했습니다.


10-16: 초기화 로직이 올바르게 구현되었습니다.

부모 coordinator와 navigation controller를 적절히 주입받고 있습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (5)

7-8: 셀 식별자 구현이 적절합니다.

static identifier를 사용하여 셀 등록에 필요한 식별자를 제공하고 있습니다.


10-20: UI 컴포넌트 선언이 일관성 있게 구현되었습니다.

BKDesign 시스템을 활용하여 일관된 디자인을 적용하고 있습니다.


22-43: Auto Layout 제약 조건이 적절히 구현되었습니다.

SnapKit을 사용하여 명확하고 읽기 쉬운 제약 조건을 설정했습니다.


49-51: Configure 메서드 구현이 올바릅니다.

BKLabel의 setText 메서드를 사용하여 텍스트를 설정하고 있습니다.


54-60: 레이아웃 상수를 enum으로 캡슐화한 것이 좋습니다.

이전 학습 내용에 따라 enum을 사용하여 상수를 정의하는 것이 성능상 유리합니다.

src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (3)

6-7: UseCase 구현이 적절합니다.

프로토콜 준수와 의존성 주입 패턴을 올바르게 사용하고 있습니다.


9-11: 의존성 주입 구현이 올바릅니다.

Repository 의존성을 생성자를 통해 주입받는 표준 패턴을 따르고 있습니다.


13-15: Combine Publisher 구현이 적절합니다.

이전 학습 내용에 따르면 Failure 타입이 Never인 경우 에러 처리가 불필요하므로, Just를 사용하여 동기 작업을 Publisher로 래핑하는 것이 적절합니다.

src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift (5)

6-9: Repository 구현이 적절합니다.

KeyValueStorage를 사용한 의존성 주입과 상수 정의가 올바르게 구현되었습니다.


11-13: 의존성 주입 패턴이 일관되게 적용되었습니다.

Storage 의존성을 생성자를 통해 주입받는 표준 패턴을 따르고 있습니다.


15-17: Load 메서드 구현이 적절합니다.

Optional try를 사용하여 에러 발생 시 빈 배열을 반환하는 안전한 구현입니다.


19-24: Save 메서드 로직이 효율적으로 구현되었습니다.

중복 제거, 최신 항목 우선 정렬, 최대 개수 제한 등의 로직이 잘 구현되어 있습니다.


26-28: Clear 메서드 구현이 적절합니다.

Optional try를 사용하여 에러를 조용히 처리하는 것이 검색 기록 관리에 적합합니다.

src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift (7)

26-30: 프로퍼티 리팩토링이 적절히 구현되었습니다.

typetextFieldType으로 명명하여 명확성을 높이고, typeDidChanged() 메서드를 통해 타입 변경 로직을 분리한 것이 좋습니다.


41-41: 프로퍼티 배치가 개선되었습니다.

onTextChanged 프로퍼티를 더 명확한 위치로 이동하여 가독성을 향상시켰습니다.


48-48: 초기화 로직이 일관되게 업데이트되었습니다.

변경된 프로퍼티명에 맞춰 초기화 로직을 올바르게 수정했습니다.


101-106: 타입 변경 로직의 분리가 우수합니다.

typeDidChanged() 메서드를 별도로 추출하여 상속받는 클래스에서 오버라이드할 수 있도록 한 것이 좋은 설계입니다.


119-119: Configure 메서드 업데이트가 적절합니다.

변경된 프로퍼티명에 맞춰 일관되게 수정되었습니다.


170-170: Clear 버튼 UX 개선이 우수합니다.

텍스트를 지운 후 즉시 버튼을 숨기는 것이 사용자 경험을 향상시킵니다.


203-212: 검색 버튼 색상 프로퍼티가 적절히 추가되었습니다.

새로운 검색 기능을 지원하기 위한 searchButtonColor 프로퍼티가 타입별로 적절한 색상을 제공합니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift (2)

15-26: 초기화 구현이 적절합니다

기본 초기화에서 .recent 타입으로 설정하는 것이 합리적이며, SnapKit을 사용한 제약 조건 설정도 올바르게 구현되었습니다.


54-77: AttributedString 생성 로직이 잘 구현되었습니다

폰트 fallback 로직과 범위 검색을 통한 색상 적용이 적절하게 구현되었습니다. 범위 검색 실패 시에도 안전하게 처리됩니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)

51-59: configure 메서드가 적절하게 구현되었습니다

셀의 데이터 설정 로직이 명확하고 간단하게 구현되었습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift (3)

32-61: 뷰 설정 및 레이아웃이 적절하게 구현되었습니다

BaseView의 오버라이드 메서드들이 올바르게 구현되었고, SnapKit을 사용한 제약 조건 설정도 적절합니다.


63-89: 상태 기반 UI 업데이트 로직이 잘 구현되었습니다

검색 상태에 따른 UI 업데이트와 빈 상태 처리가 적절하게 구현되었습니다. 애니메이션 적용도 좋습니다.


92-146: 헬퍼 메서드들이 체계적으로 구현되었습니다

컬렉션뷰 설정, 데이터 소스 설정, 레이아웃 생성 등이 모두 적절하게 분리되어 구현되었습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (2)

10-28: 뷰 컨트롤러 초기화가 적절하게 구현되었습니다

BaseViewController 상속과 뷰모델 래핑, 내비게이션 설정이 올바르게 구현되었습니다.


45-53: 상태 바인딩이 적절하게 구현되었습니다

Combine을 사용한 반응형 상태 바인딩이 올바르게 구현되었습니다. 메인 스레드 스케줄링과 중복 제거도 적절합니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (2)

1-7: 가져오기 구문이 적절하게 구성되어 있습니다.

필요한 모듈들이 명확하게 가져와졌고, UIKit.UIImage처럼 구체적인 타입만 가져오는 것도 좋은 관습입니다.


8-11: SearchItem 열거형 설계가 적절합니다.

키워드와 검색 결과를 명확하게 구분하는 열거형 설계가 좋고, Hashable 프로토콜 준수로 컬렉션에서 사용하기에 적합합니다.

@clxxrlove clxxrlove marked this pull request as ready for review July 20, 2025 07:49
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (4)
src/Projects/BKDomain/Sources/VO/SearchBookParameters.swift (1)

5-11: 불필요한 nil 초기화 제거 필요

SwiftLint 경고에 따라 옵셔널 프로퍼티의 명시적 nil 초기화를 제거하세요. 의미 있는 기본값은 유지하면서 불필요한 nil 초기화만 제거하시면 됩니다.

-    public var queryType: SearchQueryType? = nil
+    public var queryType: SearchQueryType?
-    public var searchTarget: SearchTarget? = nil
+    public var searchTarget: SearchTarget?
     public var maxResults: Int? = 10
     public var start: Int? = 1
-    public var sort: SearchSortType? = nil
+    public var sort: SearchSortType?
-    public var cover: SearchCoverType? = nil
+    public var cover: SearchCoverType?
-    public var categoryId: Int? = nil
+    public var categoryId: Int?
src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift (1)

12-24: 빈 검색어 처리 고려사항

현재 구현은 빈 검색어나 공백만 있는 검색어도 그대로 전달합니다. 불필요한 네트워크 요청을 방지하기 위해 입력 검증을 추가하는 것이 좋을 것 같습니다.

다음과 같이 개선할 수 있습니다:

 public func execute(
     query: String,
     startIndex: Int
 )-> AnyPublisher<(books: [Book], totalResults: Int), Never> {
+    let trimmedQuery = query.trimmingCharacters(in: .whitespacesAndNewlines)
+    guard !trimmedQuery.isEmpty else {
+        return Just((books: [], totalResults: 0))
+            .eraseToAnyPublisher()
+    }
+    
     repository.search(
         SearchBookParameters(
-            query: query,
+            query: trimmedQuery,
             start: startIndex
         )
     )
     .map { ($0.0, $0.totalResults) }
     .eraseToAnyPublisher()
 }
src/Projects/BKData/Sources/API/BookAPI.swift (1)

40-49: 헤더 로직 단순화 가능

현재 각 케이스마다 헤더를 반복적으로 정의하고 있습니다. 더 간결하게 표현할 수 있습니다.

 var headers: [String: String] {
-    switch self {
-    case .upsert:
-        return [
-            "Content-Type": "application/json"
-        ]
-    case .detail, .myLibrary, .search:
-        return [:]
-    }
+    return method == .put ? ["Content-Type": "application/json"] : [:]
 }
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)

88-107: 이미지 로딩 개선 제안

Kingfisher를 사용한 이미지 로딩 구현이 좋습니다. 다만 하드코딩된 플레이스홀더 URL 대신 앱 번들의 플레이스홀더 이미지를 사용하는 것을 고려해보세요.

if let image {
    thumbnail.kf.setImage(with: image)
} else {
-   thumbnail.kf.setImage(with: URL(string: "https://placehold.co/68x100"))
+   thumbnail.image = UIImage(named: "placeholder_book") // 또는 적절한 번들 이미지
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe6863d and 44e69fc.

📒 Files selected for processing (44)
  • src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (2 hunks)
  • src/Projects/BKData/Sources/API/AuthAPI.swift (5 hunks)
  • src/Projects/BKData/Sources/API/BookAPI.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Request/AuthLoginRequestDTO.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Request/RefreshTokenRequestDTO.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Request/SearchBookRequestDTO.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Response/SearchBookResponseDTO.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift (1 hunks)
  • src/Projects/BKData/Sources/DataAssembly.swift (3 hunks)
  • src/Projects/BKData/Sources/Repository/DefaultAuthRepository.swift (5 hunks)
  • src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift (1 hunks)
  • src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift (1 hunks)
  • src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift (5 hunks)
  • src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift (3 hunks)
  • src/Projects/BKDomain/Sources/DomainAssembly.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Entity/Book.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Repository/BookRepository.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Usecase/DeleteRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Usecase/SearchBookUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/Interface/Usecase/StoreRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchBookParameters.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchBookResult.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchCoverType.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchQueryType.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchSortType.swift (1 hunks)
  • src/Projects/BKDomain/Sources/VO/SearchTarget.swift (1 hunks)
  • src/Projects/BKNetwork/Sources/Extension/URLRequest+.swift (1 hunks)
  • src/Projects/BKNetwork/Sources/NetworkAssembly.swift (2 hunks)
  • src/Projects/BKPresentation/Project.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/AuthFlow/Coordinator/LoginCoordinator.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift (2 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (1 hunks)
  • src/Tuist/Package.resolved (2 hunks)
  • src/Tuist/Package.swift (2 hunks)
✅ Files skipped from review due to trivial changes (15)
  • src/Projects/BKPresentation/Project.swift
  • src/Projects/BKDomain/Sources/Interface/Usecase/DeleteRecentSearchUseCase.swift
  • src/Tuist/Package.resolved
  • src/Projects/BKDomain/Sources/VO/SearchTarget.swift
  • src/Projects/BKDomain/Sources/Entity/Book.swift
  • src/Tuist/Package.swift
  • src/Projects/BKDomain/Sources/VO/SearchQueryType.swift
  • src/Projects/BKDomain/Sources/VO/SearchSortType.swift
  • src/Projects/BKData/Sources/DTO/Request/SearchBookRequestDTO.swift
  • src/Projects/BKData/Sources/API/AuthAPI.swift
  • src/Projects/BKDomain/Sources/VO/SearchCoverType.swift
  • src/Projects/BKDomain/Sources/Interface/Repository/BookRepository.swift
  • src/Projects/BKDomain/Sources/VO/SearchBookResult.swift
  • src/Projects/BKData/Sources/DTO/Response/SearchBookResponseDTO.swift
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchSectionHeaderView.swift
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/Projects/BKDomain/Sources/DomainAssembly.swift
  • src/Projects/BKDomain/Sources/Interface/Repository/RecentSearchRepository.swift
  • src/Projects/BKData/Sources/DataAssembly.swift
  • src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift
  • src/Projects/BKDesign/Sources/Components/BKSearchTextField.swift
  • src/Projects/BKData/Sources/Repository/DefaultRecentSearchRepository.swift
  • src/Projects/BKDesign/Sources/Components/BKBaseTextField.swift
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#69
File: src/Projects/BKDesign/PreviewApp/Sources/View/CatalogViewController.swift:57-70
Timestamp: 2025-07-10T14:01:04.933Z
Learning: clxxrlove는 Reed-iOS 프로젝트에서 테스트/데모용 코드와 프로덕션 코드에 대해 다른 코드 품질 기준을 적용한다. 테스트용 뷰에서는 안전성 검증 등의 엄격한 코드 품질 요구사항을 적용하지 않는 것을 선호한다.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/Constant/PresentationKeys.swift:3-5
Timestamp: 2025-07-07T13:53:13.006Z
Learning: clxxrlove prefers using enum over struct for constants namespace due to better runtime performance, memory layout, and compiler optimizations. Enum prevents instantiation at compile time without needing private init().
src/Projects/BKPresentation/Sources/AuthFlow/Coordinator/LoginCoordinator.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
src/Projects/BKData/Sources/DTO/Request/AuthLoginRequestDTO.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
src/Projects/BKDomain/Sources/Interface/Usecase/SearchBookUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
src/Projects/BKData/Sources/DTO/Request/RefreshTokenRequestDTO.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
src/Projects/BKDomain/Sources/Interface/Usecase/StoreRecentSearchUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#79
File: src/Projects/BKPresentation/Sources/MainFlow/Setting/ViewModel/SettingViewModel.swift:78-85
Timestamp: 2025-07-11T12:12:48.911Z
Learning: AppVersionUseCase.execute() returns AnyPublisher<String, Never> where the Failure type is Never, making error handling unnecessary. The app version is retrieved from the main bundle which is always available, and the implementation already handles missing version by defaulting to "-".
src/Projects/BKData/Sources/Repository/DefaultAuthRepository.swift (2)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#66
File: src/Projects/BKData/Sources/DataAssembly.swift:49-60
Timestamp: 2025-07-09T02:32:03.801Z
Learning: clxxrlove prefers separating protocol registrations for RefreshHandler and AuthRepository even when using the same implementation (DefaultAuthRepository) to maintain Interface Segregation Principle, where RefreshHandler focuses on token refresh and AuthRepository handles broader authentication operations.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift:109-111
Timestamp: 2025-07-07T13:47:32.784Z
Learning: The LoginViewModel's authenticateWithToken case in src/Projects/BKPresentation/Sources/AuthFlow/ViewModel/LoginViewModel.swift is test code and not important for implementation according to the user.
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)
Learnt from: doyeonk429
PR: YAPP-Github/Reed-iOS#63
File: src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BottomSheetShadow.swift:5-10
Timestamp: 2025-07-08T17:16:15.942Z
Learning: doyeonk429는 BottomSheetShadow struct에서 apply(to:)와 asCALayerShadow() 메서드를 처음에 만들었지만 활용 측면에서 애매할 것 같아서 제외했다고 함. 정적 속성만으로 구현하는 것을 선호함.
src/Projects/BKNetwork/Sources/NetworkAssembly.swift (1)
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#66
File: src/Projects/BKData/Sources/DataAssembly.swift:49-60
Timestamp: 2025-07-09T02:32:03.801Z
Learning: clxxrlove prefers separating protocol registrations for RefreshHandler and AuthRepository even when using the same implementation (DefaultAuthRepository) to maintain Interface Segregation Principle, where RefreshHandler focuses on token refresh and AuthRepository handles broader authentication operations.
🧬 Code Graph Analysis (10)
src/Projects/BKDomain/Sources/Interface/Usecase/SearchBookUseCase.swift (1)
src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift (1)
  • execute (12-24)
src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift (2)
src/Projects/BKData/Sources/Repository/DefaultAuthStateRepository.swift (1)
  • isLoggedIn (19-22)
src/Projects/BKPresentation/Sources/Common/Coordinator/Coordinator.swift (1)
  • popAndFinish (28-37)
src/Projects/BKDomain/Sources/Interface/Usecase/StoreRecentSearchUseCase.swift (1)
src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (1)
  • execute (12-14)
src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (2)
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1)
  • execute (12-14)
src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift (1)
  • execute (12-14)
src/Projects/BKDomain/Sources/UseCase/DefaultSearchBookUseCase.swift (1)
src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift (1)
  • search (15-40)
src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift (2)
src/Projects/BKDomain/Sources/UseCase/DefaultFetchRecentSearchUseCase.swift (1)
  • execute (12-14)
src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (1)
  • execute (12-14)
src/Projects/BKData/Sources/API/BookAPI.swift (1)
src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift (1)
  • search (15-40)
src/Projects/BKData/Sources/Repository/DefaultAuthRepository.swift (1)
src/Projects/BKNetwork/Sources/Provider/OAuthNetworkProvider.swift (1)
  • request (23-37)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (2)
src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift (1)
  • search (15-40)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchView.swift (1)
  • applySnapshot (77-106)
src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (1)
src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift (1)
  • search (15-40)
🪛 SwiftLint (0.57.0)
src/Projects/BKDomain/Sources/VO/SearchBookParameters.swift

[Warning] 5-5: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


[Warning] 6-6: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


[Warning] 9-9: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


[Warning] 10-10: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


[Warning] 11-11: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

🔇 Additional comments (21)
src/Projects/BKPresentation/Sources/AuthFlow/Coordinator/LoginCoordinator.swift (1)

21-22: Coordinator 패턴 구현이 올바르게 되었습니다.

변수명을 더 명확하게 변경하고 coordinator 참조를 할당하는 것은 표준적인 coordinator 패턴 구현입니다. 하지만 이 변경사항이 "검색 화면 구현" PR과 어떤 관련이 있는지 명확하지 않습니다.

#!/bin/bash
# Description: 검색 관련 기능에서 LoginCoordinator가 사용되는지 확인
# Expected: 검색 흐름에서 로그인 coordinator 사용 여부 확인

rg -A 5 -B 5 "LoginCoordinator" --type swift

Likely an incorrect or invalid review comment.

src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift (1)

9-9: Coordinator 참조 추가가 올바르게 구현되었습니다.

weak 참조로 선언하여 순환 참조를 방지하는 것이 좋습니다.

src/Projects/BKDomain/Sources/UseCase/DefaultDeleteRecentSearchUseCase.swift (1)

5-15: 구현이 일관성 있고 올바릅니다.

다른 RecentSearch 관련 UseCase들과 동일한 패턴을 따르며, 저장소의 동기 작업을 Combine Publisher로 래핑하는 방식이 적절합니다. Never 실패 타입 사용도 로컬 저장소 삭제 작업의 특성에 맞습니다.

src/Projects/BKCore/Sources/DiContainer/DIContainer.swift (1)

31-31: 일관성을 위한 올바른 변경입니다.

다른 Assembly 파일들에서의 명명 규칙 변경과 일치하도록 기본 키를 "Default"로 통일한 것은 적절합니다. DI 컨테이너의 일관성을 보장합니다.

Also applies to: 52-52

src/Projects/BKData/Sources/DTO/Request/AuthLoginRequestDTO.swift (1)

6-6: 접근성 수준 조정이 적절합니다.

DTO를 internal로 변경하여 모듈 내부 캡슐화를 개선한 것은 좋은 변경입니다. 외부 노출이 필요하지 않은 DTO의 적절한 스코핑입니다.

src/Projects/BKData/Sources/DTO/Request/RefreshTokenRequestDTO.swift (1)

5-6: 일관된 캡슐화 개선입니다.

AuthLoginRequestDTO와 마찬가지로 internal 접근성으로 변경하여 적절한 모듈 경계를 설정한 좋은 변경입니다.

src/Projects/BKNetwork/Sources/Extension/URLRequest+.swift (1)

9-11: nil 처리 및 퍼센트 인코딩 자동 처리되어 별도 검증 불필요

이 이니셜라이저는 [String: Any] 타입으로 nil 값이 들어올 수 없고, URLComponentsqueryItems를 통해 자동으로 퍼센트 인코딩을 수행합니다.
따라서 compactMapmap 변경이나 수동 인코딩 로직 제거는 문제를 일으키지 않습니다.

Likely an incorrect or invalid review comment.

src/Projects/BKDomain/Sources/Interface/Usecase/StoreRecentSearchUseCase.swift (1)

5-7: 프로토콜 설계가 잘 구성되었습니다

Combine을 활용한 반응형 프로그래밍 패턴을 올바르게 적용했으며, Never 실패 타입 사용이 최근 검색어 저장 로직에 적합합니다.

src/Projects/BKDomain/Sources/Interface/Usecase/SearchBookUseCase.swift (1)

5-10: 검색 유스케이스 프로토콜이 잘 설계되었습니다

페이지네이션을 지원하는 startIndex 파라미터와 검색 결과 및 총 개수를 함께 반환하는 튜플 구조가 검색 기능에 적합합니다.

src/Projects/BKDomain/Sources/UseCase/DefaultStoreRecentSearchUseCase.swift (1)

5-15: 구현이 일관된 패턴을 잘 따르고 있습니다

다른 최근 검색어 유스케이스들(DefaultFetchRecentSearchUseCase, DefaultDeleteRecentSearchUseCase)과 동일한 패턴으로 구현되어 있어 코드베이스의 일관성을 유지합니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (1)

73-78: 레이아웃 상수 구조화 잘됨

레이아웃 상수를 enum으로 정리한 것은 좋은 패턴입니다. 유지보수가 쉽고 일관성 있는 레이아웃을 보장합니다.

src/Projects/BKData/Sources/Repository/DefaultAuthRepository.swift (2)

10-25: 네트워크 제공자 분리 리팩토링이 잘 구현되었습니다

인증 관련 작업을 defaultProvideroauthProvider로 분리한 구조가 좋습니다. 이는 관심사의 분리 원칙을 잘 따르고 있으며, 각각의 네트워크 제공자가 명확한 역할을 가지도록 설계되었습니다.


31-49: 메서드별 적절한 네트워크 제공자 사용

각 메서드가 적절한 네트워크 제공자를 사용하고 있습니다:

  • 로그인/토큰 갱신: defaultProvider 사용
  • 로그아웃/계정 삭제: oauthProvider 사용

기존 메서드 시그니처와 반환 타입이 그대로 유지되어 호환성도 보장됩니다.

Also applies to: 52-66, 73-85, 90-107

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)

50-82: 레이아웃 및 제약 조건 구현이 우수합니다

썸네일이 올바르게 뷰 계층에 추가되었고(52번째 줄), 제약 조건과 콘텐츠 우선순위 설정이 적절합니다. 이전 리뷰 코멘트에서 지적된 썸네일 뷰 계층 누락 문제가 해결되었습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchViewController.swift (3)

6-34: 이벤트 기반 아키텍처 설계가 우수합니다

SearchViewEvent 열거형을 통한 명확한 이벤트 정의와 Snapshot 구조체를 사용한 효율적인 상태 비교 로직이 잘 구현되었습니다. 뷰모델을 AnyViewBindableViewModel로 래핑하여 타입 안전성도 확보했습니다.


36-67: Combine을 활용한 반응형 프로그래밍 구현이 우수합니다

이벤트 필터링, 중복 제거(removeDuplicates()), 그리고 적절한 메모리 관리(weak self)가 잘 적용되었습니다. 각 이벤트 타입별로 분리된 스트림 처리로 코드 가독성도 좋습니다.


69-86: UI 업데이트 로직이 적절합니다

메인 스레드에서의 UI 업데이트와 중복 상태 변경 방지를 통한 성능 최적화가 잘 구현되었습니다.

src/Projects/BKPresentation/Sources/MainFlow/Search/ViewModel/SearchViewModel.swift (4)

13-42: 뷰모델 아키텍처 설계가 탁월합니다

액션/사이드이펙트 분리, 명확한 상태 정의, 그리고 검색 아이템을 위한 적절한 열거형 설계가 우수합니다. Reducer 패턴을 활용한 단방향 데이터 플로우 구현이 인상적입니다.


70-116: 상태 관리 로직이 체계적으로 구현되었습니다

검색, 페이지네이션, 최근 검색어 관리 등의 복잡한 상태 변화를 명확하게 처리하고 있습니다. 특히 중복 도서 제거 로직(102-104번째 줄)에서 ISBN을 사용한 비교가 적절합니다.


118-157: 사이드이펙트 처리가 우수합니다

비동기 작업을 위한 사이드이펙트 핸들링이 잘 구현되었습니다. Publishers.Zip을 사용한 검색 결과 가져오기와 최근 검색어 저장의 병렬 처리(134-144번째 줄)가 효율적입니다.


159-167: 반응형 프로그래밍 바인딩이 적절합니다

사이드이펙트 스트림과 액션 처리의 바인딩이 메모리 안전성을 고려하여 잘 구현되었습니다.

Comment thread src/Projects/BKData/Sources/API/BookAPI.swift
Comment thread src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift Outdated
Comment thread src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift Outdated
Comment thread src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift Outdated
Comment thread src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift
Comment thread src/Projects/BKNetwork/Sources/NetworkAssembly.swift
Comment thread src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)

96-115: configure 메서드 최적화를 제안합니다

현재 구현은 동작하지만 몇 가지 개선점이 있습니다:

  1. numberOfLineslineBreakMode 설정을 init으로 이동
  2. 하드코딩된 placeholder URL 개선

다음과 같이 최적화할 수 있습니다:

override init(frame: CGRect) {
    super.init(frame: frame)
    addSubviews(thumbnail, labelStack)
    [titleLabel, descriptionStack].forEach(labelStack.addArrangedSubview(_:))
    [authorLabel, separatorLabel, publisherLabel].forEach(descriptionStack.addArrangedSubview(_:))
    
+   // 정적 설정을 init으로 이동
+   titleLabel.numberOfLines = 1
+   titleLabel.lineBreakMode = .byTruncatingTail
+   authorLabel.numberOfLines = 1
+   authorLabel.lineBreakMode = .byTruncatingTail
    
    // ... 기존 제약 조건 코드
}

func configure(
    title: String,
    description: BookDescription,
    image: URL?
) {
-   titleLabel.numberOfLines = 1
-   authorLabel.numberOfLines = 1
    titleLabel.setText(text: title)
    authorLabel.setText(text: description.author)
    publisherLabel.setText(text: description.publisher)
-   titleLabel.lineBreakMode = .byTruncatingTail
-   authorLabel.lineBreakMode = .byTruncatingTail
    
    if let image {
        thumbnail.kf.setImage(with: image)
    } else {
-       thumbnail.kf.setImage(with: URL(string: "https://placehold.co/68x100"))
+       // 기본 이미지나 앱 번들의 placeholder 이미지 사용 고려
+       thumbnail.image = UIImage(named: "placeholder_book") // 또는 적절한 기본 이미지
    }
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44e69fc and afe4740.

📒 Files selected for processing (5)
  • src/Projects/BKData/Sources/DTO/Request/SearchBookRequestDTO.swift (1 hunks)
  • src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift (2 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift (1 hunks)
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Projects/BKPresentation/Sources/AuthFlow/View/LoginViewController.swift
  • src/Projects/BKData/Sources/DTO/Util/DictionaryRepresentable.swift
  • src/Projects/BKData/Sources/DTO/Request/SearchBookRequestDTO.swift
  • src/Projects/BKPresentation/Sources/MainFlow/Search/View/RecentKeywordCell.swift
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#94
File: src/Projects/BKData/Sources/Repository/DefaultBookRepository.swift:15-40
Timestamp: 2025-07-21T05:16:18.455Z
Learning: clxxrlove는 Reed-iOS 프로젝트에서 기능 구현 시 우선순위를 명확하게 설정한다. 기본 기능 구현을 먼저 완료하고, 에러 처리나 안정성 개선은 이후 단계에서 진행하는 것을 선호한다.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#69
File: src/Projects/BKDesign/PreviewApp/Sources/View/CatalogViewController.swift:57-70
Timestamp: 2025-07-10T14:01:04.933Z
Learning: clxxrlove는 Reed-iOS 프로젝트에서 테스트/데모용 코드와 프로덕션 코드에 대해 다른 코드 품질 기준을 적용한다. 테스트용 뷰에서는 안전성 검증 등의 엄격한 코드 품질 요구사항을 적용하지 않는 것을 선호한다.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#94
File: src/Projects/BKData/Sources/API/BookAPI.swift:51-58
Timestamp: 2025-07-20T08:11:52.822Z
Learning: Reed-iOS 프로젝트에서 clxxrlove는 미구현 기능의 경우 API 구조를 먼저 정의하고 실제 구현은 나중에 하는 단계적 개발 방식을 사용한다. upsert 같은 기능에서 body가 nil로 설정되어 있어도 이는 의도적인 것일 수 있다.
Learnt from: clxxrlove
PR: YAPP-Github/Reed-iOS#56
File: src/Projects/BKPresentation/Sources/Constant/PresentationKeys.swift:3-5
Timestamp: 2025-07-07T13:53:13.006Z
Learning: clxxrlove prefers using enum over struct for constants namespace due to better runtime performance, memory layout, and compiler optimizations. Enum prevents instantiation at compile time without needing private init().
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (1)

Learnt from: doyeonk429
PR: #63
File: src/Projects/BKDesign/Sources/Foundation/GraphicSystem/BottomSheetShadow.swift:5-10
Timestamp: 2025-07-08T17:16:15.942Z
Learning: doyeonk429는 BottomSheetShadow struct에서 apply(to:)와 asCALayerShadow() 메서드를 처음에 만들었지만 활용 측면에서 애매할 것 같아서 제외했다고 함. 정적 속성만으로 구현하는 것을 선호함.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
src/Projects/BKPresentation/Sources/MainFlow/Search/View/SearchResultCell.swift (5)

1-14: LGTM! 구조가 잘 정의되어 있습니다

import 구문이 적절하고, BookDescription 구조체가 관련 데이터를 잘 캡슐화하고 있습니다.


16-48: UI 컴포넌트 구성이 잘 되어 있습니다

BKLabel을 적절히 활용하고 있고, 레이아웃 구조가 명확합니다. 구분자(" | ")가 하드코딩되어 있지만 도서 메타데이터 표시 맥락에서는 적절해 보입니다.


50-82: 초기화 및 제약 조건 설정이 올바릅니다

썸네일이 뷰 계층에 제대로 추가되었고, content hugging과 compression resistance 우선순위가 텍스트 줄임 처리를 위해 적절히 구성되어 있습니다.


88-94: prepareForReuse 구현이 적절합니다

셀 재사용 시 모든 콘텐츠를 적절히 초기화하여 데이터 오염을 방지하고 있습니다.


118-129: 상수 정의가 잘 구성되어 있습니다

enum을 사용한 상수 네임스페이스와 BKDesign 시스템의 일관된 활용이 좋습니다. 썸네일 크기와 비율 설정이 적절해 보입니다.

Copy link
Copy Markdown
Member

@doyeonk429 doyeonk429 left a comment

Choose a reason for hiding this comment

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

구체적인 리뷰는 저녁에 더 달게용...

@clxxrlove clxxrlove merged commit a11976b into develop Jul 21, 2025
3 checks passed
@clxxrlove clxxrlove deleted the BOOK-148-feature/#93 branch July 21, 2025 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-148/feat] SearchFlow 화면 구현

2 participants