Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d8f4256
chore/#150: git ignore 수정
0Hooni May 15, 2025
7cd9a17
chore/#150: .gitignore 수정
0Hooni May 15, 2025
ef4d22c
Merge branch 'refactor/#150-layout-abstract' of https://github.com/Po…
0Hooni May 15, 2025
c13e8d4
feat/#150: safe index 가능한 Collection extention 추가
0Hooni May 15, 2025
b6a1c04
refactor/#150: 검색 결과 없음 네이밍 수정
0Hooni May 15, 2025
e18f572
refactor/#150: View와 Reactor 공통으로 사용할 Section type 정의
0Hooni May 15, 2025
123ccf7
refactor/#150: 빈 결과일때의 Section과 레이아웃 분리
0Hooni May 15, 2025
7ad04b9
refactor/#150: 리액터 코드 단순화
0Hooni May 16, 2025
269afce
rafactor/#150: state 단순화
0Hooni May 16, 2025
8bf7e35
fix/#150: clear 버튼이 반대로 동작하던 부분 원상복구
0Hooni May 16, 2025
eada1e1
fix/#150: RxCocoa 의존성 문제 해결
0Hooni May 16, 2025
c68ef47
refactor/#150: Fatory를 value semantic으로 수정
0Hooni May 16, 2025
30c6167
refactor/#150: 반복되는 레이아웃 추상화 및 프로바이더로 분리
0Hooni May 16, 2025
5505d8e
refactor/#150: 반복되는 레이아웃 추상화 데이터소스 결합도 줄임
0Hooni May 16, 2025
8a210b7
Merge branch 'refactor/#150-layout-abstract' of https://github.com/Po…
0Hooni May 16, 2025
4fde99d
refactor/#150: TagCollection layout inset 설정을 외부로 분리
0Hooni May 17, 2025
98ae966
chore/#150: gitignore 수정
0Hooni May 17, 2025
08e9972
feat/#150: CollectionLayoutBuilder 도입
0Hooni May 18, 2025
47f5066
refactor/#150: LayoutFactory에 빌더 패턴을 사용하도록 수정
0Hooni May 18, 2025
2580238
refactor/#150: PopupSearch의 레이아웃 팩토리에 빌더 도입
0Hooni May 18, 2025
65d74c8
style/#150: Apply SwiftLint autocorrect
github-actions[bot] May 18, 2025
db403e3
chore/#150: 코드래빗 설정 추가
0Hooni May 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .coderabbit.yaml
Copy link
Member

Choose a reason for hiding this comment

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

공유주신 코드리뷰의 역할을 하는 기능인가 보네요!!

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
language: ko-KR # 언어 설정

early_access: true # 미리보기 기능 활성화
enable_free_tier: true # 프리 티어 활성화
auto_resolve_threads: false # 자동 해결 비활성화

reviews:
profile: chill
request_changes_workflow: true
high_level_summary: true # 리뷰에 대해 요약(high-level summary)를 자동 작성
high_level_summary_placeholder: '@coderabbitai 요약'
auto_title_placeholder: '@coderabbitai'
poem: true
review_status: true # PR 리뷰 상태를 리뷰 요약란에 표시
collapse_walkthrough: false # 리뷰 단계 설명을 기본적으로 접지 않음

abort_on_close: true # PR이 닫히면 리뷰 수행을 중단(abort)


auto_review:
enabled: true # 자동 리뷰 기능을 활성화
auto_incremental_review: true # 커밋이 추가될 때마다 변경 사항에 대해서만 자동 수행
ignore_title_keywords: [] # PR 제목에 포함되면 리뷰를 건너뛰는 키워드 목록
labels: [] # 특정 라벨이 붙은 PR만 자동 리뷰 대상
drafts: false # Draft 상태인 PR은 자동 리뷰 대상에서 제외(false면 제외)
base_branches: [] # 특정 브랜치만 리뷰하도록

tools:
shellcheck: # 셸 스크립트 문법 및 보안 검사
enabled: true
ruff: # Python 코드 스타일 검사기
enabled: true
markdownlint: # 마크다운 문법 검사
enabled: true
github-checks: # GitHub 체크 연동 + 타임아웃(ms 단위)
enabled: true
timeout_ms: 90000
languagetool: # 맞춤법, 문법 검사
enabled: true
disabled_rules:
- EN_UNPAIRED_BRACKETS
- EN_UNPAIRED_QUOTES
disabled_categories:
- TYPOS
- TYPOGRAPHY
- CASING
enabled_only: false
level: default
enabled_rules: []
enabled_categories: []
biome: # JavaScript/TypeScript 정적 분석
enabled: true
hadolint: # Dockerfile 코드 스타일 검사
enabled: true
swiftlint: # Swift 코드 스타일 검사
enabled: true
phpstan: # PHP 정적 분석
enabled: true
level: default
golangci-lint: # Go 코드 스타일 검사
enabled: true
yamllint: # YAML 형식 검사
enabled: true
gitleaks: # Git 시크릿 노출 탐지
enabled: true
checkov: # 인프라 보안 검사
enabled: true
ast-grep: # AST 기반 코드 패턴 검사
packages: []
rule_dirs: []
util_dirs: []
essential_rules: true

# CodeRabbit AI 챗 기능을 사용 가능하게 하고,
# 한 번에 처리 가능한 토큰 수를 최대 4096으로 제한
chat:
enabled: true
max_token_length: 4096


# 지식 기반에 사용할 학습 범위를 지정하십시오.
# 'Local' - Repository
# 'Global'- Organization
# 'Auto' - Repository(users public) + Organization(private)
knowledge_base:
web_search: # AI 웹 검색 허용
enabled: true
learnings: # 학습 범위 설정 (local, global, auto)
scope: local
issues: # 이슈 자동 참조 범위 설정 (local, global, auto)
scope: auto
jira:
project_keys: []
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Cursor
**/buildServer.json
.vscode/*
Copy link
Member

Choose a reason for hiding this comment

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

안전한 서브스크립트 접근..!

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

public extension Collection {
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import UIKit

public final class CollectionLayoutBuilder {
private var itemSize: NSCollectionLayoutSize?
private var groupSize: NSCollectionLayoutSize?
private var numberOfItemsPerGroup: Int = 1
private var interItemSpacing: NSCollectionLayoutSpacing?
private var section: NSCollectionLayoutSection?
private var headerItem: NSCollectionLayoutBoundarySupplementaryItem?

public init() { }

public init(section existingSection: NSCollectionLayoutSection) {
self.section = existingSection
}

@discardableResult
Copy link
Member

Choose a reason for hiding this comment

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

컴파일러에서 경고 메시지를 이렇게 제한을 할 수도 있군요..!

public func item(
width: NSCollectionLayoutDimension,
height: NSCollectionLayoutDimension
) -> Self {
itemSize = NSCollectionLayoutSize(
widthDimension: width,
heightDimension: height
)

return self
}

@discardableResult
public func group(
width: NSCollectionLayoutDimension,
height: NSCollectionLayoutDimension
) -> Self {
groupSize = NSCollectionLayoutSize(
widthDimension: width,
heightDimension: height
)

return self
}

@discardableResult
public func numberOfItemsPerGroup(_ count: Int) -> Self {
numberOfItemsPerGroup = count

return self
}

@discardableResult
public func itemSpacing(_ spacing: CGFloat) -> Self {
interItemSpacing = .fixed(spacing)

return self
}

@discardableResult
public func withContentInsets(
top: CGFloat = 0,
leading: CGFloat = 0,
bottom: CGFloat = 0,
trailing: CGFloat = 0
) -> Self {
section?.contentInsets = NSDirectionalEdgeInsets(
top: top,
leading: leading,
bottom: bottom,
trailing: trailing
)

return self
}

@discardableResult
public func composeSection(_ axis: UIAxis) -> Self {
guard let itemSize, let groupSize else {
fatalError("Item and Group must be set before creating section")
}

let item = NSCollectionLayoutItem(layoutSize: itemSize)

var group: NSCollectionLayoutGroup!

switch axis {
case .vertical:
group = NSCollectionLayoutGroup.vertical(
layoutSize: groupSize,
subitems: Array(repeating: item, count: numberOfItemsPerGroup)
)

case .horizontal:
group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: Array(repeating: item, count: numberOfItemsPerGroup)
)

default: fatalError("Can't compose section to selected axis")
}

if let interItemSpacing {
group.interItemSpacing = interItemSpacing
}

section = NSCollectionLayoutSection(group: group)

return self
}

@discardableResult
public func header(
elementKind: String,
width: NSCollectionLayoutDimension = .fractionalWidth(1.0),
height: NSCollectionLayoutDimension = .fractionalHeight(1.0),
alignment: NSRectAlignment = .top
) -> Self {
let headerSize = NSCollectionLayoutSize(
widthDimension: width,
heightDimension: height
)

headerItem = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: elementKind,
alignment: alignment
)

if let headerItem {
section?.boundarySupplementaryItems = [headerItem]
}

return self
}

@discardableResult
public func withScrollingBehavior(_ behavior: UICollectionLayoutSectionOrthogonalScrollingBehavior) -> Self {
section?.orthogonalScrollingBehavior = behavior

return self
}

@discardableResult
public func groupSpacing(_ spacing: CGFloat) -> Self {
section?.interGroupSpacing = spacing

return self
}

@discardableResult
public func modifySection(_ modifier: (NSCollectionLayoutSection) -> Void) -> Self {
if let section = self.section {
modifier(section)
}
return self
}

@discardableResult
public func withExistingHeader(_ headerItem: NSCollectionLayoutBoundarySupplementaryItem) -> Self {
self.headerItem = headerItem

if let section = self.section {
section.boundarySupplementaryItems = [headerItem]
}

return self
}

@discardableResult
public func header(_ headerItems: [NSCollectionLayoutBoundarySupplementaryItem]) -> Self {
if let section = self.section {
section.boundarySupplementaryItems = headerItems
}

return self
}

public func build() -> NSCollectionLayoutSection {
guard let section else { fatalError("Section must be created before building") }
return section
}

public func buildHeader() -> NSCollectionLayoutBoundarySupplementaryItem {
guard let headerItem else { fatalError("Header must be created before building") }
return headerItem
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UIKit

public protocol CollectionLayoutProvidable {
func makeLayout() -> NSCollectionLayoutSection
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import UIKit

public struct GridCollectionLayoutProvider: CollectionLayoutProvidable {
public init() { }

public func makeLayout() -> NSCollectionLayoutSection {
return CollectionLayoutBuilder()
.item(width: .fractionalWidth(0.5), height: .absolute(249))
.group(width: .fractionalWidth(1.0), height: .absolute(249))
.numberOfItemsPerGroup(2)
.itemSpacing(16)
.composeSection(.horizontal)
.withContentInsets(top: 16, leading: 20, bottom: 0, trailing: 20)
.groupSpacing(24)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UIKit

public protocol HeaderLayoutProvidable {
func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem
}
Copy link
Member

Choose a reason for hiding this comment

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

protocol 을 분리하여 header를 정의하는 부분도 따로 채택할 수 있는 구조군요..!

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import UIKit

public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLayoutProvidable {
public init() { }

public func makeLayout() -> NSCollectionLayoutSection {
return CollectionLayoutBuilder()
.item(width: .estimated(100), height: .absolute(31))
.group(width: .estimated(100), height: .estimated(31))
.composeSection(.vertical)
.withScrollingBehavior(.continuous)
.groupSpacing(6)
.build()
}

public func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem {
return CollectionLayoutBuilder()
.header(elementKind: elementKind, height: .absolute(24))
.buildHeader()
}
}
Loading