-
Notifications
You must be signed in to change notification settings - Fork 0
[feat] 프로필 UI 구현 #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
[feat] 프로필 UI 구현 #58
Conversation
📝 WalkthroughWalkthrough프로필 화면 및 후기 UI를 대규모로 추가합니다. 여러 카드 컴포넌트(Profile, Tier, WinRate, Review), 관련 컬렉션뷰 셀들, 티어 설명 화면(TierExplanation)과 컨트롤러들이 새로 도입되었고, 일부 네비게이터 루트 변경 및 소규모 UI/리소스 업데이트가 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant App as AppCoordinator
participant VC as TierExplanationViewController
participant View as TierExplanationView
participant Model as OreTier
participant Chips as TierChipCell
User->>App: 앱 진입
App->>VC: TierExplanationViewController로 루트 설정
User->>VC: 화면 로드 및 configure(oreTier, sports)
VC->>Model: OreTier.allCases 요청
VC->>View: configure 뷰 업데이트(퍼센트, 실제티어, 타이틀)
View->>Chips: 칩 컬렉션 표시(모든 티어)
User->>Chips: 특정 칩 선택
Chips->>VC: 선택 이벤트 전달
VC->>Model: skills(sports:) 호출
Model->>VC: SkillExplanation 목록 반환
VC->>View: Skill 목록 갱신 -> 사용자에게 표시
sequenceDiagram
participant User as 사용자
participant VC as MyProfileViewController
participant View as MyProfileView
participant Profile as ProfileCard
participant ReviewCell as ReviewCollectionViewCell
User->>VC: viewDidLoad()
VC->>View: mainView로 설정
View->>Profile: profileCard, tierCard, winRateCard, reviewCard 초기화 및 레이아웃
VC->>View: 컬렉션뷰 delegate/dataSource 설정
User->>View: 후기 컬렉션뷰 스크롤/조회
VC->>ReviewCell: numberOfItemsInSection / cellForItemAt 호출
ReviewCell->>User: 후기 셀 렌더링(프로필·내용)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
🤖 Fix all issues with AI agents
In `@SMASHING/Global/Components/BlueCTAButton.swift`:
- Around line 1-6: Update the file header comment to match the actual filename:
change the top comment that currently says "CTAButton.swift" to
"BlueCTAButton.swift" (or otherwise mirror the real filename) so the header
aligns with BlueCTAButton.swift; locate the header block at the top of the file
and edit the filename string and, if present, any associated comment metadata
(author/date) to remain consistent.
In `@SMASHING/Global/Enums/OreTier.swift`:
- Around line 103-106: The "백핸드 드라이브" skill explanation in the returned array
repeats the same sentence three times; update the SkillExplanation entry for
name "백핸드 드라이브" so its explanation is a single concise sentence (or replace
duplicates with varied, meaningful text) within the array returned (the
SkillExplanation instances) to remove redundancy and improve text quality.
In `@SMASHING/Presentation/Home/Cell/MatchingCell.swift`:
- Line 62: The comment "emphasis 컬러 추가" is inconsistent with the code using
.Text.muted; update either the comment or the color to match intent: if emphasis
color was intended, change the call to setTitleColor(.Text.emphasis, for:
.normal) (look for the setTitleColor(...) invocation and .Text.muted), otherwise
update the inline comment to accurately state "muted 컬러 사용" next to the
setTitleColor(...) call; ensure the symbol .Text.muted or .Text.emphasis is used
consistently with the comment.
In `@SMASHING/Presentation/Profile/Card/ProfileCard.swift`:
- Around line 17-76: The challengeAction closure is private and never set from
outside, so taps on challengeButton become a no-op; make the action injectable
by exposing a setter or public config method (e.g., add a public func
setChallengeAction(_ action: `@escaping` () -> Void) or change private var
challengeAction to an internal/public var) and ensure the existing
challengeButtonTapped selector still calls challengeAction?() safely; reference
the challengeAction property and the challengeButton / challengeButtonTapped
method when applying the change.
In `@SMASHING/Presentation/Profile/Card/ReviewCard.swift`:
- Around line 70-108: The layout currently places noReviewLabel and
reviewCollectionView in overlapping positions without switching visibility; add
a state-toggle method (e.g., updateReviewsAppearance(reviewCount:), or
updateReviewState()) that checks the data source count and sets
noReviewLabel.isHidden and reviewCollectionView.isHidden appropriately (or
assigns noReviewLabel to reviewCollectionView.backgroundView) and call this
method after data is loaded/updated (references: noReviewLabel,
reviewCollectionView, setLayout); ensure only one view is visible to prevent
overlap and update constraints if you choose backgroundView instead of hiding.
In `@SMASHING/Presentation/Profile/Card/TierCard.swift`:
- Around line 17-84: The tierDetailAction closure is private and never set
externally, so detailTapped (called by tierDetailButton) does nothing; expose a
public API to inject the callback by adding a setter method or changing the
property visibility (e.g., provide a public func setTierDetailAction(_ action:
`@escaping` () -> Void) or make tierDetailAction public/internal(set:) so external
code can assign it), then ensure detailTapped invokes tierDetailAction safely
(optional call). Update references to tierDetailAction, detailTapped, and
tierDetailButton accordingly.
In `@SMASHING/Presentation/Profile/Cell/SportsButtonChip.swift`:
- Around line 39-80: The visual selection state isn't tied to
UIButton.isSelected; override the isSelected property in SportsButtonChip (add
"override var isSelected: Bool { didSet { updateSelectionAppearance() } }"),
implement a small helper updateSelectionAppearance() that contains the styling
currently in setSelected(_: ) (backgroundColor and label.textColor), and in
init(sports:selected:) assign self.isSelected = selected instead of calling
setSelected(_:). Keep setSelected(_:) either as a convenience that sets
isSelected or remove its external usage so all visual updates go through the
isSelected didSet.
In `@SMASHING/Presentation/Profile/MyProfileViewController.swift`:
- Around line 21-25: The override of viewDidLoad in MyProfileViewController is
missing a call to super.viewDidLoad(); update the viewDidLoad() implementation
to call super.viewDidLoad() as the first statement, then assign view = mainView
and set mainView.reviewCard.reviewCollectionView.delegate and .dataSource to
self so the UIViewController lifecycle initialization runs correctly.
- Around line 33-45: In collectionView(_:cellForItemAt:), remove the per-reuse
call to cell.contentView.snp.makeConstraints() because makeConstraints
accumulates constraints; instead move the width constraint into the cell's
internal layout (e.g., add a setupConstraints() or init method on
ReviewCollectionViewCell that calls contentView.snp.makeConstraints once) or if
you must update on reuse use contentView.snp.remakeConstraints() to replace
existing constraints; update ReviewCollectionViewCell to own its constraints and
ensure cellForItemAt only calls cell.configure(_:) and does not create
constraints.
In `@SMASHING/Presentation/Profile/UserProfileViewController.swift`:
- Around line 22-28: Override of viewDidLoad in UserProfileViewController is
missing a call to super.viewDidLoad(); add a call to super.viewDidLoad() as the
first statement inside viewDidLoad() so the superclass initialization runs
before configuring mainView and assigning
mainView.reviewCard.reviewCollectionView.delegate/dataSource.
- Around line 46-48: cellForItemAt에서 cell.contentView.snp.makeConstraints를 호출해
재사용 시 제약이 중복 추가되는 문제가 있습니다; cellForItemAt 내부의 해당 makeConstraints 호출을 제거하고 대신
UICollectionViewDelegateFlowLayout의 collectionView(_:layout:sizeForItemAt:)로 셀
너비를 반환하거나 셀 클래스의 init/awakeFromNib에서 한 번만 제약을 설정하도록 옮기세요 (참조: cellForItemAt,
cell.contentView.snp.makeConstraints, collectionView(_:layout:sizeForItemAt:),
init/awakeFromNib).
In `@SMASHING/Presentation/Review/MyReviewsView.swift`:
- Around line 68-76: The satisfaction chips are being populated with random
numbers inside setUI(), causing inconsistent UI; instead, accept real data and
remove Int.random from the SatisfictionChip creation: change setUI() to iterate
over a provided data source (e.g., a [ReviewScoreCount] or similar model) and
create SatisfictionChip(review:review, num:count) using that injected data,
wiring the data source into the view/controller that owns MyReviewsView (or
expose a public configure(with:) method) and update satisfactionStackView
population to use that model rather than Int.random.
In `@SMASHING/Presentation/Review/MyReviewsViewController.swift`:
- Around line 13-25: The TempReview.mockReviews array is hardcoded test data
that must not ship; replace or gate it so production builds don't use it. Move
mockReviews out of the production path by either making the reviews data source
injectable into MyReviewsViewController (e.g., accept a ReviewProvider or array
in the initializer) and only provide TempReview.mockReviews from preview/debug
code, or wrap the mock definition with a compile-time guard (`#if` DEBUG ...
`#endif`) so it's available during development but excluded from release builds;
ensure MyReviewsViewController consumes the real data source in production.
- Around line 55-67: The cellForItemAt implementation is repeatedly calling
contentView.snp.makeConstraints which accumulates width constraints on reused
ReviewCollectionViewCell instances; remove the makeConstraints call from
collectionView(_:cellForItemAt:) and instead set the contentView width once
inside ReviewCollectionViewCell (e.g. in a setupConstraints() method) or replace
makeConstraints with remakeConstraints if you must set it there, ensuring you
update only in cell initialization (awakeFromNib/init) or by having the layout
delegate compute item size in collectionView(_:layout:sizeForItemAt:); update
references to ReviewCollectionViewCell, configure(_:) and
contentView.snp.makeConstraints accordingly.
In `@SMASHING/Presentation/Review/TierExplanationView.swift`:
- Around line 15-19: Update the MARK comment formatting to match SwiftLint rules
by adding a space after the slashes and using the exact "// MARK: - ..."
pattern; specifically replace the existing "//MARK: - Properties" and "//MARK: -
UI Components" with the corrected form (affects the MARK near the dismissAction
property and the duplicate occurrence around line 102), ensuring all MARK
comments in TierExplanationView (e.g., the ones surrounding dismissAction)
follow "// MARK: - <SectionName>".
- Around line 263-268: The numberOfItems implementation is hardcoded for the
skills collection and can desync from the selected tier/sport; update
collectionView(_:numberOfItemsInSection:) so that if collectionView ==
mainView.tierCollectionView you still return OreTier.allCases.count, otherwise
return the count based on the current selected OreTier and sport (e.g. use
selectedOreTier.skills(sports: selectedSports).count or the viewModel's oreTier
and sports properties) instead of OreTier.diamond.skills(sports:).count so the
items always match the active selection.
- Around line 17-24: The dismissAction is captured as nil when navigationBar
(lazy var) is initialized, so update the implementation to propagate changes:
make dismissAction a var with a didSet observer that calls
navigationBar.setRightButton(image: UIImage.icCloseLg, action: dismissAction ??
{}) to update the button action whenever dismissAction changes, and in your
ViewController (e.g., in loadView or viewDidLoad) assign dismissAction = { [weak
self] in self?.dismiss(animated: true) } (or the actual dismiss closure) so the
navigationBar's setRightButton receives the real handler; alternatively, after
setting dismissAction from the controller call navigationBar.setRightButton(...)
directly to ensure the button uses the new closure.
- Around line 271-287: In collectionView(_:cellForItemAt:) you're repeatedly
adding width constraints to cell.contentView via
cell.contentView.snp.makeConstraints for SkillExplanationCell which causes
duplicate constraints; remove that makeConstraints call (or replace with a
one-time setup in SkillExplanationCell's init/layoutSubviews if you need it) and
rely on the existing sizeForItemAt implementation to size cells, referencing
collectionView(_:cellForItemAt:), SkillExplanationCell,
cell.contentView.snp.makeConstraints and sizeForItemAt to locate the change.
🧹 Nitpick comments (11)
SMASHING/Presentation/Review/Cell/ReviewCollectionViewCell.swift (2)
17-21: 프로필 이미지가 설정되지 않습니다.
configure(_:)메서드에서profileImageView의 이미지를 설정하지 않고 있어 항상 회색 배경만 표시됩니다.TempReview모델에 이미지 정보가 있다면 추가하거나, 없다면 기본 프로필 이미지를 설정하는 것이 좋겠습니다.♻️ 제안
func configure(_ review: TempReview) { nicknameLabel.text = review.nickname dateLabel.text = review.date contentLabel.text = review.content + // profileImageView.image = review.profileImage ?? UIImage(named: "defaultProfile") }Also applies to: 99-105
101-101: 코딩 컨벤션: 파라미터 콜론 앞 공백 제거Swift 스타일 가이드에 따르면 콜론 앞에는 공백이 없어야 합니다.
💅 수정 제안
- func configure(_ review :TempReview) { + func configure(_ review: TempReview) {SMASHING/Presentation/Review/Cell/SatisfictionChip.swift (2)
1-6: 파일명 오타: "Satisfiction" → "Satisfaction"파일명에 오타가 있습니다. "Satisfiction"이 아니라 "Satisfaction"이 맞습니다. 클래스명도 함께 수정하는 것이 좋겠습니다.
17-18: 불필요한 프로퍼티 저장
review프로퍼티는setupAttributes()에서imageSmall접근에만 사용됩니다. 초기화 시점에 이미지를 직접 설정하면 프로퍼티 저장이 필요 없습니다.♻️ 제안
final class SatisfictionChip: BaseUIView { - - // MARK: - Properties - - private var review: ReviewScore // MARK: - UI Components ... init(review: ReviewScore, num: Int) { - self.review = review if num > 99 { self.label.text = "99+" } else { self.label.text = "\(num)" } super.init(frame: .zero) backgroundColor = .Background.overlay - setupAttributes() + setupAttributes(review: review) } - private func setupAttributes() { + private func setupAttributes(review: ReviewScore) { self.layer.cornerRadius = 20 self.imageView.image = review.imageSmall }Also applies to: 39-40, 55-58
SMASHING/Global/Components/BlueCTAButton.swift (1)
39-39: 시맨틱 컬러 사용 검토
.Tier.diamondBackground는 티어 관련 UI를 위한 색상으로 보입니다. CTA 버튼 전용 색상(예:.Button.primaryBackground)이 있다면 해당 색상을 사용하는 것이 의미상 더 적절할 수 있습니다.SMASHING/Presentation/Review/MyReviewsViewController.swift (1)
36-40: 커스텀 뷰 할당은loadView()로 옮기는 편이 안전합니다.
BaseViewController.viewDidLoad에서setUI/setLayout이 실행되므로viewDidLoad에서 교체하면 베이스 설정이 누락될 수 있습니다.💡 제안 수정
+override func loadView() { + view = myReviewsView +} + override func viewDidLoad() { super.viewDidLoad() - view = myReviewsView setupDelegate() }SMASHING/Global/Enums/OreTier.swift (1)
58-74: index 수동 매핑은 유지보수 리스크가 있습니다.
케이스 순서가 바뀌면 누락/오류가 생길 수 있어allCases기반 계산을 권장합니다.💡 제안 수정
var index: Int { - switch self { - case .iron: - return 0 - case .bronze: - return 1 - case .silver: - return 2 - case .gold: - return 3 - case .platinum: - return 4 - case .diamond: - return 5 - case .challenger: - return 6 - } + OreTier.allCases.firstIndex(of: self) ?? 0 }SMASHING/Global/Coordinators/AppCoordinator.swift (1)
20-24: 루트 설정은 push 대신 setViewControllers 고려
start()가 재호출되면 스택이 누적될 수 있어, 루트를 명시적으로 교체하는 편이 안전합니다. 네비게이션 바도 API로 일관되게 숨기는 편을 권장합니다.수정 제안 (루트 교체 방식)
- navigationController.pushViewController(root, animated: false) - navigationController.isNavigationBarHidden = true + navigationController.setViewControllers([root], animated: false) + navigationController.setNavigationBarHidden(true, animated: false)SMASHING/Presentation/Profile/Card/ReviewCard.swift (1)
72-75: 랜덤 카운트는 프로덕션에서 비결정적만족도 칩 카운트가 매번 랜덤으로 바뀌어 UX가 불안정해집니다. 실제 데이터로 주입할 수 있는
configure메서드(또는 바인딩)로 전환을 권장합니다.SMASHING/Presentation/Profile/UserProfileViewController.swift (2)
2-2: 파일 헤더 주석의 파일명 불일치헤더 주석의 파일명이
UserProfileViewController 2.swift로 되어 있으나, 실제 파일명은UserProfileViewController.swift입니다. 정리가 필요합니다.✏️ 수정 제안
-// UserProfileViewController 2.swift +// UserProfileViewController.swift
32-34:min()함수 사용으로 가독성 개선삼항 연산자 대신
min()함수를 사용하면 의도가 더 명확해집니다.♻️ 리팩터링 제안
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return TempReview.mockReviews.count > 3 ? 3 : TempReview.mockReviews.count + return min(TempReview.mockReviews.count, 3) }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (9)
SMASHING/Global/Resource/Assets.xcassets/AppIcon.appiconset/logo-black 1.pngis excluded by!**/*.pngSMASHING/Global/Resource/Assets.xcassets/AppIcon.appiconset/logo-black 2.pngis excluded by!**/*.pngSMASHING/Global/Resource/Assets.xcassets/AppIcon.appiconset/logo-black 3.pngis excluded by!**/*.pngSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down.imageset/ic_thumbs_down.pdfis excluded by!**/*.pdfSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down_lg.imageset/ic_thumbs_down_lg.pdfis excluded by!**/*.pdfSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up.imageset/ic_thumbs_up.pdfis excluded by!**/*.pdfSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double.imageset/ic_thumbs_up_double.pdfis excluded by!**/*.pdfSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double_lg.imageset/ic_thumbs_up_double_lg.pdfis excluded by!**/*.pdfSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_lg.imageset/ic_thumbs_up_lg.pdfis excluded by!**/*.pdf
📒 Files selected for processing (30)
SMASHING/Global/Components/BlueCTAButton.swiftSMASHING/Global/Coordinators/AppCoordinator.swiftSMASHING/Global/Coordinators/Coordinator.swiftSMASHING/Global/Enums/OreTier.swiftSMASHING/Global/Enums/ReviewScore.swiftSMASHING/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down.imageset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down_lg.imageset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up.imageset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double.imageset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double_lg.imageset/Contents.jsonSMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_lg.imageset/Contents.jsonSMASHING/Presentation/Home/Cell/MatchingCell.swiftSMASHING/Presentation/Onboarding/View/AreaSelectionView.swiftSMASHING/Presentation/Profile/Card/ProfileCard.swiftSMASHING/Presentation/Profile/Card/ReviewCard.swiftSMASHING/Presentation/Profile/Card/TierCard.swiftSMASHING/Presentation/Profile/Card/WinRateCard.swiftSMASHING/Presentation/Profile/Cell/SportsButtonChip.swiftSMASHING/Presentation/Profile/MyProfileView.swiftSMASHING/Presentation/Profile/MyProfileViewController.swiftSMASHING/Presentation/Profile/ProfileViewController.swiftSMASHING/Presentation/Profile/UserProfileView.swiftSMASHING/Presentation/Profile/UserProfileViewController.swiftSMASHING/Presentation/Review/Cell/ReviewCollectionViewCell.swiftSMASHING/Presentation/Review/Cell/SatisfictionChip.swiftSMASHING/Presentation/Review/MyReviewsView.swiftSMASHING/Presentation/Review/MyReviewsViewController.swiftSMASHING/Presentation/Review/TierExplanationView.swift
🧰 Additional context used
🧬 Code graph analysis (9)
SMASHING/Presentation/Home/Cell/MatchingCell.swift (1)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)
SMASHING/Presentation/Profile/Cell/SportsButtonChip.swift (1)
SMASHING/Presentation/Profile/Card/TierCard.swift (1)
setUI(86-97)
SMASHING/Global/Components/BlueCTAButton.swift (1)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)
SMASHING/Presentation/Profile/MyProfileViewController.swift (1)
SMASHING/Global/Extensions/UICollectionView+.swift (1)
dequeueReusableCell(32-43)
SMASHING/Presentation/Profile/Card/WinRateCard.swift (3)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)SMASHING/Global/Extensions/UIStackView+.swift (1)
addArrangedSubviews(12-16)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)
SMASHING/Presentation/Profile/Card/TierCard.swift (4)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)SMASHING/Global/Extensions/UIStackView+.swift (1)
addArrangedSubviews(12-16)SMASHING/Presentation/Review/TierExplanationView.swift (1)
selected(207-211)
SMASHING/Presentation/Review/TierExplanationView.swift (5)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)SMASHING/Global/Extensions/UIStackView+.swift (1)
addArrangedSubviews(12-16)SMASHING/Global/Enums/OreTier.swift (2)
actualTier(28-37)skills(17-26)SMASHING/Global/Extensions/UICollectionView+.swift (1)
dequeueReusableCell(32-43)
SMASHING/Presentation/Review/Cell/SatisfictionChip.swift (1)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)
SMASHING/Presentation/Profile/Card/ProfileCard.swift (2)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)
🪛 SwiftLint (0.57.0)
SMASHING/Presentation/Review/TierExplanationView.swift
[Warning] 15-15: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 19-19: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
[Warning] 102-102: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
🔇 Additional comments (36)
SMASHING/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json (1)
4-28: LGTM — 아이콘 파일 매핑이 명확합니다.AppIcon 세 가지 변형(기본/다크/틴티드)에 대한
filename지정이 명확하고 구조도 올바릅니다.SMASHING/Presentation/Onboarding/View/AreaSelectionView.swift (1)
28-28: LGTM!셀렉터와 함수 이름을
didTapAddressButton에서addressButtonDidTap으로 변경한 것은 iOS 네이밍 컨벤션([주체][액션]패턴)에 더 부합합니다. 셀렉터 참조와 함수 이름이 정확히 일치하며,@objc어트리뷰트도 올바르게 적용되어 있습니다.Also applies to: 61-63
SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_lg.imageset/Contents.json (1)
1-12: LGTM!PDF 벡터 포맷을 사용한 표준 Xcode 에셋 카탈로그 설정 파일입니다. 유니버설 idiom 사용으로 모든 기기에서 적절히 스케일링됩니다.
SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down_lg.imageset/Contents.json (1)
1-12: LGTM!ic_thumbs_up_lg와 일관된 구조의 에셋 카탈로그 설정입니다.
SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double.imageset/Contents.json (1)
1-12: LGTM!다른 리뷰 아이콘 에셋들과 일관된 구조입니다.
SMASHING/Global/Resource/Colors.xcassets/Review/Contents.json (1)
1-6: LGTM!Review 에셋 그룹의 표준 폴더 메타데이터 파일입니다. 리뷰 관련 아이콘들을 논리적으로 그룹화합니다.
SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_down.imageset/Contents.json (1)
1-12: LGTM!다른 리뷰 아이콘 에셋들과 일관된 표준 구조입니다.
SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up.imageset/Contents.json (1)
1-12: 문제 없음(LGTM).
Xcode 에셋 카탈로그의 표준Contents.json형식이며 단일 universal PDF 지정이 적절합니다.SMASHING/Global/Resource/Colors.xcassets/Review/ic_thumbs_up_double_lg.imageset/Contents.json (1)
1-12: 문제 없음(LGTM).
에셋 카탈로그 메타데이터가 정상이며 이미지 리소스 정의가 일관됩니다.SMASHING/Presentation/Home/Cell/MatchingCell.swift (1)
33-33: LGTM!닉네임 라벨들의 색상이
.Text.muted로 일관되게 변경되었습니다.Also applies to: 55-55
SMASHING/Global/Enums/ReviewScore.swift (1)
8-37: LGTM!
ReviewScoreenum에best케이스와 이미지 접근자가 깔끔하게 추가되었습니다. 기존 패턴과 일관성 있게 구현되어 있습니다.SMASHING/Presentation/Review/Cell/SatisfictionChip.swift (1)
65-74: 레이아웃 제약 조건 충돌 가능성
contentStackView에height.equalTo(24)와top.bottom.equalToSuperview().inset(10)이 동시에 적용되어 있습니다. 이 경우 뷰의 총 높이가 44pt(10 + 24 + 10)로 고정되는데, intrinsicContentSize와 충돌할 수 있습니다. 의도한 동작인지 확인해 주세요.SMASHING/Global/Components/BlueCTAButton.swift (2)
17-17: 클로저 순환 참조 가능성
action클로저가 강한 참조로 저장되어 있어, 호출 측에서self를 캡처하면 순환 참조가 발생할 수 있습니다. 사용 시[weak self]를 사용하도록 문서화하거나, 버튼 내부에서 약한 참조 처리를 고려해 주세요.Also applies to: 21-29, 54-56
35-50: LGTM!버튼의 기본 스타일링과 레이아웃 구현이 깔끔합니다. 재사용 가능한 CTA 버튼 컴포넌트로 잘 구성되어 있습니다.
SMASHING/Presentation/Review/MyReviewsView.swift (2)
54-63: 컬렉션 뷰 기본 구성은 깔끔합니다.
automaticSize와 셀 등록 등 필수 설정이 잘 갖춰져 있습니다.
79-115: 레이아웃 제약이 명확합니다.
안전 영역 기준으로 상하 스택이 자연스럽게 이어집니다.SMASHING/Presentation/Profile/Cell/SportsButtonChip.swift (1)
65-75: 스택뷰/아이콘 제약은 명확합니다.
간격과 크기 설정이 일관적입니다.SMASHING/Global/Enums/OreTier.swift (7)
17-25: 종목별 스킬 매핑 분기가 명확합니다.
28-36: 종목별 실제 티어 매핑이 일관됩니다.
39-55: 퍼센트 문구 정의가 명확합니다.
129-155: 테니스 스킬 정의는 일관적입니다.
158-184: 배드민턴 스킬 정의는 일관적입니다.
190-223: 종목별 실제 티어 문구 매핑이 명확합니다.
227-230: SkillExplanation 구조가 단순하고 명확합니다.SMASHING/Presentation/Review/TierExplanationView.swift (7)
104-149: 뷰 구성/레이아웃 흐름이 일관적입니다.
구성 요소 배치가 명확합니다.
151-155: configure 갱신 로직이 명확합니다.
158-170: PaddingLabel 구현은 명확합니다.
173-221: TierChipCell 상태 스타일링이 깔끔합니다.
244-258: 칩 크기 계산 로직이 명확합니다.
291-297: 선택 변경 시 갱신 흐름이 명확합니다.
301-351: SkillExplanationCell 레이아웃이 명확합니다.SMASHING/Global/Coordinators/Coordinator.swift (1)
31-35: 코멘트 생략 (공백만 추가됨).SMASHING/Presentation/Profile/ProfileViewController.swift (1)
14-20: BaseViewController 공통 UI/레이아웃 호출 누락 여부 확인
BaseViewController에 공통 설정(예: 공통 스타일/레이아웃)이 있다면 현재 override에서 건너뛰게 됩니다. 의도된 변경인지 확인 부탁드립니다.SMASHING/Presentation/Profile/Card/WinRateCard.swift (1)
88-123: 스택/디바이더 구성 깔끔합니다.
구성 요소 분리와 제약 설정이 명확합니다.SMASHING/Presentation/Profile/MyProfileView.swift (1)
36-80: 스크롤 뷰 + 콘텐츠 레이아웃 흐름이 명확합니다.
카드 스택과 제약 설정이 일관되어 유지보수하기 좋습니다.SMASHING/Presentation/Profile/UserProfileView.swift (1)
121-143: [Rewritten review comment text]
[Classification tag]
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| containerView.addSubviews(titleLabel, seeAllButton, satisfactionStackView, noReviewLabel, reviewCollectionView) | ||
|
|
||
| satisfactionStackView.addArrangedSubviews( | ||
| SatisfictionChip(review: .best, num: Int.random(in: 0...150)), | ||
| SatisfictionChip(review: .good,num: Int.random(in: 0...150)), | ||
| SatisfictionChip(review: .bad, num: Int.random(in: 0...150))) | ||
| } | ||
|
|
||
| override func setLayout() { | ||
| containerView.snp.makeConstraints { | ||
| $0.edges.equalToSuperview() | ||
| } | ||
|
|
||
| titleLabel.snp.makeConstraints { | ||
| $0.top.leading.equalToSuperview().inset(16) | ||
| } | ||
|
|
||
| seeAllButton.snp.makeConstraints { | ||
| $0.centerY.equalTo(titleLabel) | ||
| $0.trailing.equalToSuperview().inset(16) | ||
| } | ||
|
|
||
| satisfactionStackView.snp.makeConstraints { | ||
| $0.top.equalTo(titleLabel.snp.bottom).offset(12) | ||
| $0.leading.equalToSuperview().offset(16) | ||
| $0.height.equalTo(40) | ||
| } | ||
|
|
||
| noReviewLabel.snp.makeConstraints { | ||
| $0.top.equalTo(satisfactionStackView.snp.bottom).offset(12) | ||
| $0.centerX.equalToSuperview() | ||
| // $0.bottom.equalToSuperview().inset(16) | ||
| } | ||
|
|
||
| reviewCollectionView.snp.makeConstraints { | ||
| $0.top.equalTo(satisfactionStackView.snp.bottom) | ||
| $0.horizontalEdges.equalToSuperview().inset(16) | ||
| $0.bottom.equalToSuperview().inset(16) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
후기 없음/있음 상태 전환 로직 부재로 겹침 가능
noReviewLabel과 reviewCollectionView가 동시에 레이아웃되어 화면이 겹칠 수 있습니다. 데이터 유무에 따라 명시적으로 숨김/표시를 전환하거나 backgroundView 방식으로 분리하는 것을 권장합니다.
수정 제안 (상태 토글 메서드 추가)
+ func setHasReviews(_ hasReviews: Bool) {
+ noReviewLabel.isHidden = hasReviews
+ reviewCollectionView.isHidden = !hasReviews
+ seeAllButton.isHidden = !hasReviews
+ }🤖 Prompt for AI Agents
In `@SMASHING/Presentation/Profile/Card/ReviewCard.swift` around lines 70 - 108,
The layout currently places noReviewLabel and reviewCollectionView in
overlapping positions without switching visibility; add a state-toggle method
(e.g., updateReviewsAppearance(reviewCount:), or updateReviewState()) that
checks the data source count and sets noReviewLabel.isHidden and
reviewCollectionView.isHidden appropriately (or assigns noReviewLabel to
reviewCollectionView.backgroundView) and call this method after data is
loaded/updated (references: noReviewLabel, reviewCollectionView, setLayout);
ensure only one view is visible to prevent overlap and update constraints if you
choose backgroundView instead of hiding.
| var dismissAction: (() -> Void)? | ||
|
|
||
| //MARK: - UI Components | ||
|
|
||
| private lazy var navigationBar = CustomNavigationBar(title: "티어 설명").then { | ||
| $0.setRightButton(image: UIImage.icCloseLg, action: dismissAction ?? {}) | ||
| $0.setLeftButtonHidden(true) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
fd -type f -name "TierExplanationView.swift"Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 240
🏁 Script executed:
fd -type f -name "*TierExplanation*"Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 240
🏁 Script executed:
find . -type f -name "*.swift" | grep -i tier | head -20Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 458
🏁 Script executed:
cat -n SMASHING/Presentation/Review/TierExplanationView.swift | head -80Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 2991
🏁 Script executed:
wc -l SMASHING/Presentation/Review/TierExplanationView.swiftRepository: TEAM-SMASHING/SMASHING-iOS
Length of output: 128
🏁 Script executed:
cat -n SMASHING/Presentation/Review/TierExplanationView.swift | sed -n '200,250p'Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 2150
🏁 Script executed:
find . -type f -name "*.swift" | xargs grep -l "TierExplanationView" | grep -i "controller\|viewcontroller"Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 52
🏁 Script executed:
rg "TierExplanationView" -A 3 -B 3 --type swift | head -100Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 3106
🏁 Script executed:
rg "dismissAction" SMASHING/Presentation/Review/TierExplanationView.swift -A 2 -B 2Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 378
🏁 Script executed:
rg "TierExplanationViewController" -A 5 -B 5 --type swiftRepository: TEAM-SMASHING/SMASHING-iOS
Length of output: 3130
🏁 Script executed:
cat -n SMASHING/Presentation/Review/TierExplanationView.swift | sed -n '1,120p'Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 4789
🏁 Script executed:
cat -n SMASHING/Presentation/Review/TierExplanationView.swift | sed -n '130,160p'Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 1315
🏁 Script executed:
rg "override func loadView" SMASHING/Presentation/Review/TierExplanationView.swift -A 15Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 758
🏁 Script executed:
rg "func configure" SMASHING/Presentation/Review/TierExplanationView.swift -B 2 -A 10Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 891
🏁 Script executed:
cat -n SMASHING/Presentation/Review/TierExplanationView.swift | sed -n '224,238p'Repository: TEAM-SMASHING/SMASHING-iOS
Length of output: 751
닫기 버튼 액션이 nil로 고정되는 실제 문제입니다.
navigationBar는 lazy var로, dismissAction이 nil일 때 초기화되면서 dismissAction ?? {}의 빈 클로저가 영구적으로 고정됩니다. ViewController의 loadView()에서 dismissAction을 설정하지 않아 닫기 버튼이 동작하지 않습니다. dismissAction을 property observer로 관리하여 버튼 액션을 갱신하고, ViewController에서 실제 dismiss 클로저를 주입하세요.
💡 제안 수정
-var dismissAction: (() -> Void)?
+var dismissAction: (() -> Void)? {
+ didSet {
+ navigationBar.setRightButton(image: UIImage.icCloseLg, action: dismissAction ?? {})
+ }
+}
-private lazy var navigationBar = CustomNavigationBar(title: "티어 설명").then {
- $0.setRightButton(image: UIImage.icCloseLg, action: dismissAction ?? {})
- $0.setLeftButtonHidden(true)
-}
+private lazy var navigationBar = CustomNavigationBar(title: "티어 설명").then {
+ $0.setLeftButtonHidden(true)
+}
...
override func loadView() {
view = mainView
+ mainView.dismissAction = { [weak self] in
+ self?.dismiss(animated: true)
+ }
mainView.configure(oreTier: oreTier, sports: sports)
mainView.tierCollectionView.delegate = self
mainView.tierCollectionView.dataSource = self
mainView.tierExplanationCollectionView.delegate = self
mainView.tierExplanationCollectionView.dataSource = self
}🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 19-19: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
🤖 Prompt for AI Agents
In `@SMASHING/Presentation/Review/TierExplanationView.swift` around lines 17 - 24,
The dismissAction is captured as nil when navigationBar (lazy var) is
initialized, so update the implementation to propagate changes: make
dismissAction a var with a didSet observer that calls
navigationBar.setRightButton(image: UIImage.icCloseLg, action: dismissAction ??
{}) to update the button action whenever dismissAction changes, and in your
ViewController (e.g., in loadView or viewDidLoad) assign dismissAction = { [weak
self] in self?.dismiss(animated: true) } (or the actual dismiss closure) so the
navigationBar's setRightButton receives the real handler; alternatively, after
setting dismissAction from the controller call navigationBar.setRightButton(...)
directly to ensure the button uses the new closure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@SMASHING/Global/Components/BlueCTAButton.swift`:
- Around line 17-23: init의 클로저 매개변수는 프로퍼티에 저장되어 나중에 호출하므로 현재 시그니처가 컴파일 오류를
일으킵니다; fix: 변경 대상은 init(label: String, action: (() -> Void)? = nil)에서 action
매개변수에 `@escaping을` 추가하여 init(label: String, action: `@escaping` (() -> Void)? =
nil)로 만들고, 파일 내에서 같은 역할을 하는 다른 초기화자나 할당 위치가 있다면 동일하게 `@escaping을` 적용해 주세요; 참조 심볼:
action(프로퍼티) 및 init(label:action:).
In `@SMASHING/Presentation/Review/TierExplanationView.swift`:
- Around line 81-85: Rename the misspelled property recomandationLabel to
recommendationLabel and update every usage of that identifier in this file
(including references where it's added to the view, its constraints, and any
property accesses — e.g., the places currently referencing recomandationLabel
around the view setup/constraint code and lines you noted). Ensure the
declaration in the class (private let recommendationLabel = UILabel().then { ...
}) matches all call sites (e.g., addSubview, constraint activation,
text/configuration calls) and rebuild to fix compile errors caused by the name
change.
♻️ Duplicate comments (4)
SMASHING/Presentation/Profile/MyProfileViewController.swift (1)
21-25:viewDidLoad()에서super.viewDidLoad()호출 누락UIViewController의 기본 초기화 과정이 생략되면 예상치 못한 동작이 발생할 수 있습니다.
🔧 수정 제안
override func viewDidLoad() { + super.viewDidLoad() view = mainView mainView.reviewCard.reviewCollectionView.delegate = self mainView.reviewCard.reviewCollectionView.dataSource = self }SMASHING/Presentation/Review/TierExplanationView.swift (3)
13-24: 닫기 버튼 액션이 nil로 고정되는 문제가 있습니다.
navigationBar는 lazy var로 선언되어 있어, 초기화 시점에dismissAction이 nil이면 빈 클로저가 영구적으로 캡처됩니다. 이후dismissAction을 설정해도 버튼 액션에 반영되지 않습니다.💡 제안 수정
- var dismissAction: (() -> Void)? + var dismissAction: (() -> Void)? { + didSet { + navigationBar.setRightButton(image: UIImage.icCloseLg, action: dismissAction ?? {}) + } + } - private lazy var navigationBar = CustomNavigationBar(title: "티어 설명").then { - $0.setRightButton(image: UIImage.icCloseLg, action: dismissAction ?? {}) - $0.setLeftButtonHidden(true) - } + private lazy var navigationBar = CustomNavigationBar(title: "티어 설명").then { + $0.setLeftButtonHidden(true) + }그리고 ViewController에서 dismiss 액션을 설정하세요:
override func loadView() { view = mainView mainView.dismissAction = { [weak self] in self?.dismiss(animated: true) } // ... }
281-288: 셀 재사용 시마다 width 제약이 반복 추가됩니다.
cellForItemAt에서 매번snp.makeConstraints를 호출하면 기존 제약이 제거되지 않고 새 제약이 계속 추가되어 레이아웃 충돌이 발생합니다.💡 제안 수정
} else { let cell = collectionView.dequeueReusableCell(SkillExplanationCell.self, for: indexPath) - cell.contentView.snp.makeConstraints { - $0.width.equalTo(collectionView.frame.width) - } cell.configure(with: oreTier.skills(sports: sports)[indexPath.row]) return cell }
224-239: ViewController에서 외부 설정 및 dismiss 액션이 누락되었습니다.
sports와oreTier가 private으로 고정되어 외부에서 주입할 수 없습니다.mainView.dismissAction이 설정되지 않아 닫기 버튼이 동작하지 않습니다.💡 제안 수정
final class TierExplanationViewController: BaseViewController { private let mainView = TierExplanationView() - private var sports: Sports = .tableTennis - private var oreTier: OreTier = .bronze + private var sports: Sports + private var oreTier: OreTier + + init(sports: Sports, oreTier: OreTier) { + self.sports = sports + self.oreTier = oreTier + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func loadView() { view = mainView + mainView.dismissAction = { [weak self] in + self?.dismiss(animated: true) + } mainView.configure(oreTier: oreTier, sports: sports)
🧹 Nitpick comments (5)
SMASHING/Presentation/Profile/MyProfileViewController.swift (3)
29-31:min()함수 사용으로 가독성 개선삼항 연산자 대신
min()함수를 사용하면 더 간결하고 명확합니다.♻️ 리팩터링 제안
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return TempReview.mockReviews.count > 3 ? 3 : TempReview.mockReviews.count + return min(TempReview.mockReviews.count, 3) }
43-45: 셀 내부에서 width 제약 설정 권장
remakeConstraints사용으로 제약 누적 문제는 해결되었지만, 매 재사용 시 제약을 재설정하는 것은 비효율적입니다.ReviewCollectionViewCell내부의setupConstraints()에서 width 제약을 설정하거나,UICollectionViewDelegateFlowLayout의sizeForItemAt을 활용하는 것이 더 적절합니다.
34-37: 디버그 빌드에서 셀 등록 오류 감지 개선셀 dequeue 실패 시 빈
UICollectionViewCell()을 반환하면 문제가 조용히 묻힐 수 있습니다. 개발 중 셀 등록 누락 등의 문제를 빠르게 발견하려면 디버그 빌드에서 명시적 오류 처리를 고려해보세요.♻️ 리팩터링 제안
guard let cell = collectionView.dequeueReusableCell( withReuseIdentifier: ReviewCollectionViewCell.reuseIdentifier, for: indexPath -) as? ReviewCollectionViewCell else { return UICollectionViewCell() } +) as? ReviewCollectionViewCell else { + assertionFailure("ReviewCollectionViewCell dequeue 실패 - 셀 등록 확인 필요") + return UICollectionViewCell() +}SMASHING/Presentation/Review/TierExplanationView.swift (2)
158-171:PaddingLabel클래스 개선 제안
- 서브클래싱 의도가 없다면
final키워드를 추가하세요.drawText(in:)에서UIEdgeInsets재생성이 불필요합니다.♻️ 제안 수정
-class PaddingLabel: UILabel { +final class PaddingLabel: UILabel { var edgeInset: UIEdgeInsets = .zero override func drawText(in rect: CGRect) { - let insets = UIEdgeInsets(top: edgeInset.top, left: edgeInset.left, bottom: edgeInset.bottom, right: edgeInset.right) - super.drawText(in: rect.inset(by: insets)) + super.drawText(in: rect.inset(by: edgeInset)) }
255-259: 스킬 설명 셀의 높이가 고정되어 있습니다.
height: 110으로 고정하면 긴 설명 텍스트가 잘릴 수 있습니다.tierExplanationCollectionView의estimatedItemSize가automaticSize로 설정되어 있으므로, 고정 높이 대신 self-sizing을 활용하는 것이 좋습니다.♻️ 제안 수정
sizeForItemAt에서 SkillExplanationCell의 크기 계산을 제거하고 self-sizing에 의존하세요:
} else { - let width = collectionView.frame.width - 32 - return CGSize(width: width, height: 110) + // Self-sizing cells - estimatedItemSize가 automaticSize로 설정됨 + let width = collectionView.frame.width + return CGSize(width: width, height: 1) // 높이는 자동 계산 }또는
sizeForItemAt을 아예 구현하지 않고 레이아웃의estimatedItemSize에 맡기세요.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
SMASHING/Global/Components/BlueCTAButton.swiftSMASHING/Global/Coordinators/AppCoordinator.swiftSMASHING/Presentation/Profile/Card/ProfileCard.swiftSMASHING/Presentation/Profile/MyProfileViewController.swiftSMASHING/Presentation/Review/Cell/SkillExplanationCell.swiftSMASHING/Presentation/Review/TierExplanationView.swift
🚧 Files skipped from review as they are similar to previous changes (2)
- SMASHING/Global/Coordinators/AppCoordinator.swift
- SMASHING/Presentation/Profile/Card/ProfileCard.swift
🧰 Additional context used
🧬 Code graph analysis (3)
SMASHING/Presentation/Review/Cell/SkillExplanationCell.swift (2)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)
SMASHING/Global/Components/BlueCTAButton.swift (1)
SMASHING/Global/Extensions/UIFont+.swift (1)
pretendard(21-24)
SMASHING/Presentation/Review/TierExplanationView.swift (6)
SMASHING/Global/Components/NavigationBar.swift (2)
setRightButton(101-105)setLeftButtonHidden(97-99)SMASHING/Presentation/Review/Cell/SkillExplanationCell.swift (3)
setUI(38-41)setLayout(43-58)configure(60-63)SMASHING/Global/Extensions/UIView+.swift (1)
addSubviews(12-16)SMASHING/Global/Extensions/UIStackView+.swift (1)
addArrangedSubviews(12-16)SMASHING/Global/Enums/OreTier.swift (2)
actualTier(28-37)skills(17-26)SMASHING/Global/Extensions/UICollectionView+.swift (1)
dequeueReusableCell(32-43)
🔇 Additional comments (10)
SMASHING/Global/Components/BlueCTAButton.swift (1)
35-55: 속성 구성과 탭 처리 흐름이 명확합니다버튼 스타일 설정과
buttonTapped()호출 구조가 깔끔합니다.SMASHING/Presentation/Review/Cell/SkillExplanationCell.swift (5)
1-12: LGTM!파일 헤더와 import 구문이 프로젝트 컨벤션을 따르고 있습니다.
13-34: LGTM!UI 컴포넌트가 잘 구성되어 있습니다.
containerView의 스타일링과 라벨 설정이 적절합니다.
36-41: LGTM!
contentView에 뷰를 추가하는 올바른 패턴을 따르고 있습니다.
43-58: LGTM!레이아웃 제약조건이 잘 설정되어 있습니다.
subtitleLabel에 bottom 제약을 설정하여 self-sizing이 정상 동작합니다.
60-64: LGTM!
configure메서드가 간결하고 명확합니다.SMASHING/Presentation/Review/TierExplanationView.swift (4)
291-298: LGTM!티어 선택 시 상태 업데이트와 UI 갱신 로직이 올바르게 구현되어 있습니다.
67-100: LGTM!컬렉션 뷰 설정이 적절합니다. 레이아웃 설정과 셀 등록이 올바르게 구현되어 있습니다.
104-155: LGTM!
setUI,setLayout,configure메서드가 프로젝트 패턴을 잘 따르고 있습니다. 레이아웃 제약조건이 명확하게 정의되어 있습니다.
190-196:layer.borderColor는 현재 상태 변경 시 적절히 업데이트됩니다.
selected()와deselected()메서드가 이미layer.borderColor를 올바르게 업데이트하고 있습니다. 다만 프로젝트가 향후 다크 모드를 지원할 계획이라면,CGColor는 특성 변경(trait change) 시 자동으로 업데이트되지 않는다는 점을 고려하세요. 다크 모드 지원을 추가할 때traitCollectionDidChange를 통해 색상을 갱신하거나 상태 업데이트 메서드를 활용하면 됩니다.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| private var action: (() -> Void)? | ||
|
|
||
| // MARK: - Setup Methods | ||
|
|
||
| init(label: String, action: (() -> Void)? = nil) { | ||
| self.action = action | ||
| super.init(frame: .zero) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name "BlueCTAButton.swift" -type fRepository: TEAM-SMASHING/SMASHING-iOS
Length of output: 118
🏁 Script executed:
cat -n ./SMASHING/Global/Components/BlueCTAButton.swiftRepository: TEAM-SMASHING/SMASHING-iOS
Length of output: 1702
클로저 파라미터에 @escaping 추가 필수
action을 프로퍼티에 저장하고 나중에 실행하므로 현재 시그니처는 컴파일 오류가 발생합니다. @escaping을 추가해야 합니다.
🛠️ 수정 제안
- init(label: String, action: (() -> Void)? = nil) {
+ init(label: String, action: `@escaping` (() -> Void)? = nil) {
self.action = action
super.init(frame: .zero)🤖 Prompt for AI Agents
In `@SMASHING/Global/Components/BlueCTAButton.swift` around lines 17 - 23, init의
클로저 매개변수는 프로퍼티에 저장되어 나중에 호출하므로 현재 시그니처가 컴파일 오류를 일으킵니다; fix: 변경 대상은 init(label:
String, action: (() -> Void)? = nil)에서 action 매개변수에 `@escaping을` 추가하여 init(label:
String, action: `@escaping` (() -> Void)? = nil)로 만들고, 파일 내에서 같은 역할을 하는 다른 초기화자나
할당 위치가 있다면 동일하게 `@escaping을` 적용해 주세요; 참조 심볼: action(프로퍼티) 및 init(label:action:).
| private let recomandationLabel = UILabel().then { | ||
| $0.text = "승급을 위해 아래의 기술들을 연마해보세요" | ||
| $0.font = .pretendard(.textMdSb) | ||
| $0.textColor = .Text.primary | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변수명 오타: recomandationLabel → recommendationLabel
💡 제안 수정
- private let recomandationLabel = UILabel().then {
+ private let recommendationLabel = UILabel().then {그리고 이 변수를 참조하는 다른 부분도 함께 수정하세요 (Line 106, 139, 141, 145).
🤖 Prompt for AI Agents
In `@SMASHING/Presentation/Review/TierExplanationView.swift` around lines 81 - 85,
Rename the misspelled property recomandationLabel to recommendationLabel and
update every usage of that identifier in this file (including references where
it's added to the view, its constraints, and any property accesses — e.g., the
places currently referencing recomandationLabel around the view setup/constraint
code and lines you noted). Ensure the declaration in the class (private let
recommendationLabel = UILabel().then { ... }) matches all call sites (e.g.,
addSubview, constraint activation, text/configuration calls) and rebuild to fix
compile errors caused by the name change.
LJIN24
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GOOD
📌 Related Issue
📷 Screenshots
💬 To Reviewers
Summary by CodeRabbit
릴리스 노트
New Features
Style
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.