Skip to content

[Refactor-T3-182] 마이페이지, 설정 V2디자인 반영. 강제 업데이트 구현#57

Merged
taipaise merged 4 commits intodevelopfrom
fix/setting
Sep 1, 2025
Merged

[Refactor-T3-182] 마이페이지, 설정 V2디자인 반영. 강제 업데이트 구현#57
taipaise merged 4 commits intodevelopfrom
fix/setting

Conversation

@taipaise
Copy link
Copy Markdown
Collaborator

@taipaise taipaise commented Aug 29, 2025

🌁 Background

  • v2 디자인 반영과, 강제 업데이트 로직 구현

📱 Screenshot

| iPhone SE3 | iPhone 13 mini | iPhone 16 Pro |

마이페이지

iPhone SE3 iPhone 13 mini iPhone 16 Pro
iPhone SE3 iPhone 13 mini iPhone 16 Pro

설정

iPhone SE3 iPhone 13 mini iPhone 16 Pro
iPhone SE3 설정 iPhone 13 mini 설정 iPhone 16 Pro 설정

👩‍💻 Contents

  • 마이페이지의 설정 버튼 디자인이 변경되었습니다.
  • 설정 페이지에서 업데이트 버튼 디자인이 변경되었습니다.
  • 저희는 Major.Minor.Patch 형식의 버전을 사용하고 있는데요, AppStore의 Major 버전과, 앱의 Major 버전이 다르면 업데이트 Alert를 띄우도록 했습니다.

✅ Testing

  • 테스트 목적과 상황

  • 시나리오 진행에 필요한 값

  • 시나리오 진행에 필요한 조건

  • 시나리오 완료 시 보장하는 결과

📝 Review Note

  • 알고 계시겠지만, 앱을 강제 종료한다는 것은 정상적인 흐름도 아니고, 사용자의 경험을 저하하는 행위입니다! 따라서 리젝의 가능성이 굉장히 크지 않을까,, 하지만 v1을 최대한 빨리 버려야하는 저희 입장에서는 앱의 업데이트를 강제해야하니.!.! 좋은 방법이 하늘에서 뚝 떨어지면 좋겠군요 🥹🥹
  • 현재 최신 버전임에도 업데이트 버튼이 보이는 이슈가 있습니다.. 요것은 배포할 때 1.0.0이 아니라 1.0으로 올려버렸어요.!.! 배포할 때 확인했었어야했는데 죄송합니다 ㅜㅜ 다음에 재 배포하면 없어질 거에요,,

📣 Related Issue

  • close #

📬 Reference

Summary by CodeRabbit

  • 신규 기능

    • 홈 화면에서 앱 버전 확인 및 업데이트 안내 팝업을 추가했습니다. 업데이트 선택 시 스토어로 이동합니다.
  • 스타일

    • 버튼 활성화 테마를 파란색에서 주황색 계열로 변경했습니다.
    • 체브론 아이콘을 새 이미지로 교체하고 간격/크기를 조정해 시각 일관성을 개선했습니다.
    • 마이페이지에서 네비게이션 바를 표시하고 설정 아이콘을 원본 색상으로 사용합니다.
    • 설정 화면의 상단 여백을 확대하고 헤더 UI를 개선했으며 알림 섹션을 제거했습니다.

@taipaise taipaise requested a review from choijungp August 29, 2025 13:04
@taipaise taipaise self-assigned this Aug 29, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 29, 2025

Walkthrough

자산 카탈로그에 신규 체브론 아이콘을 추가하고 DesignSystem에 회전 지원 API를 도입했습니다. 버튼 셀의 활성 테마를 주황색으로 변경했습니다. Home 화면에 버전 점검/업데이트 흐름과 DI의 AppConfigRepository 주입을 추가했습니다. 마이페이지·설정의 네비게이션·레이아웃·바인딩 스레딩을 조정했습니다.

Changes

Cohort / File(s) Change Summary
Asset: Chevron Icon
Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/Contents.json
신규 이미지셋 추가: bitnagil_chevron_icon@1x/2x/3x.png(유니버설), metadata author "xcode", version 1.
DesignSystem: Icon API
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift
static let bitnagilChevronIcon: UIImage?static func bitnagilChevronIcon(direction:) -> UIImage? 추가(기본 이미지 로드, 방향 회전, .alwaysTemplate).
UI Component: Button Cell Theme
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift
활성 버튼 색상 변경: 배경 lightBlue100orange50, 타이틀 navy500orange500. 비활성 상태는 동일.
UI Component: Chevron Cell Usage
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift
아이콘 호출 변경: BitnagilIcon.chevronIcon(...)BitnagilIcon.bitnagilChevronIcon(...); 트레일링 간격 16 → 20, 이미지 크기 상수 분리(폭 7, 높이 11).
DI: Presentation Assembler
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
HomeViewModel 생성 시 AppConfigRepositoryProtocol을 resolve하여 주입하도록 추가(미등록 시 fatalError).
Home: Version Check Flow
Projects/Presentation/Sources/Home/View/HomeViewController.swift
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift
Input.fetchVersion 추가, VM에 appConfigRepository 주입 및 updateVersionPublisher: AnyPublisher<URL?,Never> 추가. 뷰 로드 시 .fetchVersion 트리거; 업데이트 URL 수신 시 업데이트/취소 알럿 표시(선택 후 앱 종료 또는 앱스토어 열기 후 종료).
MyPage: UI/Binding 정리
Projects/Presentation/Sources/MyPage/View/MypageView.swift
viewWillAppear에서 네비게이션 바 표시, 설정 버튼 아이콘을 .alwaysOriginal로 사용, 퍼블리셔 바인딩에 .receive(on: DispatchQueue.main) 추가.
Setting: 레이아웃/네비게이션
Projects/Presentation/Sources/Setting/View/SettingView.swift
테이블 상단 여백 32 → 80, 시스템 네비바 숨기고 커스텀 바 구성, 레이아웃에 safeArea 사용, 헤더 구성 로직 정리(SettingHeaderView 활용), notification 섹션 주석 처리(제거).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant HVC as HomeViewController
  participant HVM as HomeViewModel
  participant ACR as AppConfigRepository
  participant Store as App Store

  rect rgb(242,247,255)
  note right of HVC: 뷰 로드 시 버전 점검 시작
  HVC->>HVM: action(.fetchVersion)
  HVM->>ACR: fetchAppStoreVersion()
  ACR-->>HVM: appStoreVersion, updateURL?
  alt 업데이트 필요
    HVM-->>HVC: output.updateVersionPublisher(updateURL)
    HVC->>HVC: 업데이트 알럿 표시(업데이트 / 취소)
    alt 사용자: 업데이트
      HVC->>Store: open(updateURL)
      HVC->>HVC: exit(0)
    else 사용자: 취소
      HVC->>HVC: exit(0)
    end
  else 최신 상태
    HVM-->>HVC: output.updateVersionPublisher(nil)
    HVC->>HVC: 아무 동작 없음
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25–30 minutes

Poem

새 아이콘 반짝, 주황 단추 깡충 깡충 🐇
버전 한 번 훑고, 스토어로 폴짝 폴짝.
레이아웃 다듬어 길을 넓히고, 바인딩은 안전히 메인으로.
오늘도 깡충, 코드밭에 발자국 남기네.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/setting

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@taipaise taipaise changed the title [Refactor-T3-182]: 마이페이지, 설정 V2디자인 반영. 강제 업데이트 구현 [Refactor-T3-182] 마이페이지, 설정 V2디자인 반영. 강제 업데이트 구현 Aug 29, 2025
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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (1)

35-58: SettingView 호출부와 시그니처 불일치로 컴파일 에러 발생

현재 셀의 configure(title:buttonTitle:isButtonEnabled:)만 존재하는데, SettingView에서는 cell.configure(title:)로 호출합니다(SettingView.swift Line 321). 오버로드 추가 또는 호출부 수정이 필요합니다.

두 가지 중 하나로 정리하세요(오버로드 추가를 권장):

  • 옵션 A: 셀에 오버로드 추가
 func configure(
     title: String,
     buttonTitle: String,
     isButtonEnabled: Bool
 ) {
   ...
 }
+
+// 기본 상태(비활성)로 제목만 세팅하는 오버로드
+func configure(title: String) {
+    super.configure(title: title)
+    button.setTitle(nil, for: .normal)
+    button.isEnabled = false
+    button.backgroundColor = BitnagilColor.gray98
+    button.setTitleColor(BitnagilColor.gray70, for: .disabled)
+}
  • 옵션 B: SettingView 호출부 수정(아래 해당 코멘트 참고).
Projects/Presentation/Sources/Setting/View/SettingView.swift (3)

188-205: 버전 셀 업데이트: 비가시 영역에서 반영 누락 가능

cellForRow(at:)는 가시 영역에 없으면 nil을 반환하므로 스크롤 시 최신 상태가 아니게 됩니다. 상태를 보존하고 행을 리로드하세요.

간단한 보완:

-        viewModel.output.versionPublisher
-            .receive(on: DispatchQueue.main)
-            .sink(receiveValue: { [weak self] versionType in
-                let indexPath = IndexPath(row: InformationSection.version.rawValue, section: Section.information.rawValue)
-                guard
-                    let self,
-                    let cell = self.tableView.cellForRow(at: indexPath) as? BitnagilButtonTableViewCell
-                else { return }
-
-                switch versionType {
-                case .needUpdate(let version):
-                    cell.configure(title: "버전 \(version)", buttonTitle: "업데이트", isButtonEnabled: true)
-                case .latest(let version):
-                    cell.configure(title: "버전 \(version)", buttonTitle: "최신", isButtonEnabled: false)
-                }
-            })
+        viewModel.output.versionPublisher
+            .receive(on: DispatchQueue.main)
+            .sink(receiveValue: { [weak self] versionType in
+                guard let self else { return }
+                let indexPath = IndexPath(row: InformationSection.version.rawValue, section: Section.information.rawValue)
+                if let cell = self.tableView.cellForRow(at: indexPath) as? BitnagilButtonTableViewCell {
+                    switch versionType {
+                    case .needUpdate(let version):
+                        cell.configure(title: "버전 \(version)", buttonTitle: "업데이트", isButtonEnabled: true)
+                    case .latest(let version):
+                        cell.configure(title: "버전 \(version)", buttonTitle: "최신", isButtonEnabled: false)
+                    }
+                } else {
+                    // 비가시 상태면 행 리로드
+                    self.tableView.reloadRows(at: [indexPath], with: .none)
+                }
+            })

추가로, cellForRowAt에서 현재 버전 상태를 반영하도록 로컬 상태(예: versionCellState)를 두면 더 견고합니다. 필요하면 코드 제안 드리겠습니다.


320-325: 컴파일 에러: BitnagilButtonTableViewCell.configure(title:) 미존재

현재 시그니처와 맞지 않습니다. (본 셀에 오버로드 추가가 어렵다면) 호출부를 다음처럼 수정하세요.

-        case .button(let title):
+        case .button(let title):
             guard let cell = tableView.dequeueReusableCell(withIdentifier: BitnagilButtonTableViewCell.className, for: indexPath) as? BitnagilButtonTableViewCell else { return .init() }
-            cell.configure(title: title)
+            // 초기 상태: 버튼 비활성(버전 정보 수신 시 갱신)
+            cell.configure(title: title, buttonTitle: "", isButtonEnabled: false)
             cell.delegate = self
             cell.selectionStyle = .none
             return cell

대안: BitnagilButtonTableViewCell에 configure(title:) 오버로드 추가(해당 파일 코멘트 참조).


206-226: 윈도우 교체 로직: delegate에서 window 추출은 안전하지 않음

UIWindowSceneDelegate로 캐스팅해 window에 접근하면 타입에 따라 실패할 수 있습니다. UIWindowScene.windows에서 직접 가져오세요.

-                if !isAuthenticated {
-                    guard
-                        let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
-                        let sceneDelegate = windowScene.delegate as? UIWindowSceneDelegate,
-                        let window = sceneDelegate.window
-                    else { return }
+                if !isAuthenticated {
+                    guard
+                        let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
+                        let window = windowScene.windows.first
+                    else { return }
 
                     guard let loginViewModel = DIContainer.shared.resolve(type: LoginViewModel.self)
                     else { fatalError("loginViewModel 의존성이 등록되지 않았습니다.") }
 
                     let loginView = LoginViewController(viewModel: loginViewModel)
                     let navigationController = UINavigationController(rootViewController: loginView)
                     window?.rootViewController = navigationController
                     window?.makeKeyAndVisible()
                 }
🧹 Nitpick comments (3)
Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/Contents.json (1)

1-23: 템플릿 렌더링 의도 명시로 코드 단순화 제안

에셋에 template-rendering-intent를 지정하면, 코드에서 매번 withRenderingMode(.alwaysTemplate) 호출을 피할 수 있습니다.

 {
   "images" : [
     ...
   ],
+  "properties" : {
+    "template-rendering-intent" : "template"
+  },
   "info" : {
     "author" : "xcode",
     "version" : 1
   }
 }
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (1)

30-34: 사소한 UI 제안: 컨텐트 모드 명시

16x16 고정 사이즈이므로 크롭 방지를 위해 contentMode = .scaleAspectFit을 명시하면 안전합니다.

     chevronImageView.tintColor = .black
     chevronImageView.image = BitnagilIcon
         .bitnagilChevronIcon(direction: .right)?
         .withRenderingMode(.alwaysTemplate)
+    chevronImageView.contentMode = .scaleAspectFit
Projects/Presentation/Sources/MyPage/View/MypageView.swift (1)

62-63: 아이콘 렌더링을 alwaysOriginal로 변경 — 다크모드 대비 확인 필요

템플릿 틴트 해제를 선택했으므로, 에셋 자체가 다크/라이트 모드에 적합한 색상인지 확인하세요. 멀티컬러 또는 다크모드 전용 에셋이 없으면 대비가 낮아질 수 있습니다.

필요 시 에셋 카탈로그에 Appearance(Any, Dark) 별 이미지를 추가해 자동 전환을 지원하거나, 템플릿 유지 + 틴트 컬러를 디자인 토큰으로 관리하는 방식을 고려해주세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b3f9d55 and ebda5bb.

⛔ Files ignored due to path filters (6)
  • Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/bitnagil_chevron_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/bitnagil_chevron_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/bitnagil_chevron_icon@3x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/setting_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/setting_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/setting_icon@3x.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Home/View/HomeViewController.swift (2 hunks)
  • Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (7 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (3 hunks)
  • Projects/Presentation/Sources/Setting/View/SettingView.swift (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-16T09:09:13.869Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.

Applied to files:

  • Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: 2025-08-01T06:38:18.687Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.687Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.

Applied to files:

  • Projects/Presentation/Sources/Home/View/HomeViewController.swift
🧬 Code graph analysis (6)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (1)
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)
  • bitnagilChevronIcon (19-21)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)
Projects/Presentation/Sources/Common/Extension/UIImage+.swift (2)
  • rotate (56-67)
  • rotate (69-71)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (2)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
  • fetchVersion (139-155)
Projects/DataSource/Sources/Repository/AppConfigRepository.swift (1)
  • fetchAppVersion (13-21)
Projects/Presentation/Sources/Setting/View/SettingView.swift (2)
Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (1)
  • configureCustomNavigationBar (19-29)
Projects/Presentation/Sources/Setting/View/Component/SettingHeaderView.swift (1)
  • configure (58-66)
Projects/Presentation/Sources/Home/View/HomeViewController.swift (2)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
  • action (84-117)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1)
  • fetchVersion (139-155)
⏰ 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 (10)
Projects/Presentation/Resources/Images.xcassets/bitnagil_chevron_icon.imageset/Contents.json (1)

1-23: 체브론 에셋 추가 LGTM

1x/2x/3x 스케일 정의와 메타데이터 구성 문제 없습니다.

Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (1)

51-54: 활성 테마 변경(오렌지) 적용 적절

UI 정책 반영으로 보이며 색상 전환 로직 문제 없습니다.

Projects/Presentation/Sources/Setting/View/SettingView.swift (1)

344-346: 정보 섹션 헤더 Divider 숨김 처리 적절

V2 디자인 의도와 일치합니다.

Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (1)

30-34: 새 아이콘 API로 교체 LGTM

BitnagilIcon.bitnagilChevronIcon(direction:) 적용 및 템플릿 렌더링 유지 모두 적절합니다.

Projects/Presentation/Sources/MyPage/View/MypageView.swift (2)

52-53: 네비게이션 바 표시 전환 적절

SettingView에서 커스텀 네비게이션 바를 숨기는 흐름과 충돌 없이 MyPage에서는 시스템 바 표시로 복귀됩니다.


110-125: 메인 스레드 디스패치 추가 LGTM

UI 바인딩에 receive(on: .main)을 추가한 점 적절합니다.

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (2)

35-40: HomeViewModel 호출부 점검 완료
HomeViewModel( 호출은 PresentationDependencyAssembler.swift의 한 곳뿐이며, 다른 수동 생성 호출부는 존재하지 않습니다.


31-33: AppConfigRepositoryProtocol DI 등록 확인 — 누락 아님
DataSourceDependencyAssembler에서 DIContainer.shared.register(type: AppConfigRepositoryProtocol.self)가 이미 호출되고 있어 PresentationDependencyAssembler의 resolve가 정상 동작합니다.

Likely an incorrect or invalid review comment.

Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (2)

23-24: 승인: .fetchVersion 액션 트리거 배선 확인
SettingView.swift(134)와 HomeViewController.swift(106)에서 .action(input: .fetchVersion) 호출이 확인되었습니다.


35-36: DI 주입 검증 필요: Assembler에서 appConfigRepository 등록 보장

  • updateVersionPublisher/updateVersionSubject 추가는 적절합니다.
  • appConfigRepository를 non-optional로 DI하는 건 좋으나, Assembler 조립 단계에서 반드시 등록되어야 런타임 크래시를 방지할 수 있습니다.

Comment on lines +18 to +21
static let bitnagilChevronIcon = UIImage(named: "chevron_icon", in: bundle, with: nil)
static func bitnagilChevronIcon(direction: Direction) -> UIImage? {
return BitnagilIcon.bitnagilChevronIcon?.rotate(degrees: direction.rotation)?.withRenderingMode(.alwaysTemplate)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

새 체브론 API가 기존 chevron API와 중복이고, 에셋명이 불일치합니다.

  • 이미 47-50라인에 chevronIcon(direction:)가 동일한 역할을 합니다. 두 API가 공존하면 호출부 혼란과 유지보수 비용이 커집니다.
  • 신규 에셋이 bitnagil_chevron_icon으로 추가되었다면, 현재 코드의 "chevron_icon"은 오타/불일치로 보입니다. 런타임에서 이미지 로드 실패 가능성이 높습니다.

아래처럼 에셋명을 바로잡고, 기존 API를 신규 API로 위임시키거나(deprecate) 하나로 정리해 주세요.

-    static let bitnagilChevronIcon = UIImage(named: "chevron_icon", in: bundle, with: nil)
-    static func bitnagilChevronIcon(direction: Direction) -> UIImage? {
-        return BitnagilIcon.bitnagilChevronIcon?.rotate(degrees: direction.rotation)?.withRenderingMode(.alwaysTemplate)
-    }
+    static let bitnagilChevronIcon = UIImage(named: "bitnagil_chevron_icon", in: bundle, with: nil)
+    static func bitnagilChevronIcon(direction: Direction) -> UIImage? {
+        BitnagilIcon.bitnagilChevronIcon?
+            .rotate(degrees: direction.rotation)?
+            .withRenderingMode(.alwaysTemplate)
+    }

추가 제안(선택): 파일 내 기존 API를 신규 API로 위임하여 중복 제거

-    static let chevronIcon = UIImage(named: "chevron_icon", in: bundle, with: nil)
-    static func chevronIcon(direction: Direction) -> UIImage? {
-        return BitnagilIcon.chevronIcon?.rotate(degrees: direction.rotation)?.withRenderingMode(.alwaysTemplate)
-    }
+    @available(*, deprecated, message: "Use bitnagilChevronIcon(direction:) instead.")
+    static let chevronIcon = UIImage(named: "chevron_icon", in: bundle, with: nil)
+    @available(*, deprecated, message: "Use bitnagilChevronIcon(direction:) instead.")
+    static func chevronIcon(direction: Direction) -> UIImage? {
+        BitnagilIcon.bitnagilChevronIcon(direction: direction)
+    }

참고: 본 파일 하단 Direction.rotation 계산 프로퍼티에 return 키워드가 없어 컴파일 오류가 발생합니다. 아래처럼 수정해 주세요(선택 코드 블록).

var rotation: Float {
    switch self {
    case .up: return 90
    case .down: return -90
    case .left: return 0
    case .right: return 180
    }
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift around
lines 18-21, the new bitnagilChevron API duplicates the existing
chevronIcon(direction:) and references the wrong asset name ("chevron_icon" vs.
the actual "bitnagil_chevron_icon"), causing runtime image load failures and API
confusion; fix by updating the UIImage(named:) asset name to
"bitnagil_chevron_icon", then remove the duplicate implementation by making the
older or newer API delegate to the canonical method (or mark the duplicate
deprecated) so only one implementation loads, and finally correct the
Direction.rotation computed property by adding explicit return statements for
each case to fix the compile error.

super.viewDidLoad()
showIndicatorView()
viewModel.action(input: .loadNickname)
viewModel.action(input: .fetchVersion)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

버전 체크 트리거 위치 재고 필요(백그라운드 복귀 시 재검 없음)

viewDidLoad 한 번만 호출되므로, 사용자가 App Store 다녀온 뒤 앱으로 복귀해도 재검이 실행되지 않습니다. viewWillAppear 추가 트리거 또는 willEnterForeground 노티 관찰을 권장합니다.

예시(선택, 변경은 파일 범위 밖):

private var foregroundObserver: Any?

override func viewDidLoad() {
    super.viewDidLoad()
    foregroundObserver = NotificationCenter.default.addObserver(
        forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main
    ) { [weak self] _ in
        self?.viewModel.action(input: .fetchVersion)
    }
}

deinit {
    if let obs = foregroundObserver { NotificationCenter.default.removeObserver(obs) }
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/View/HomeViewController.swift around line
106, the version check is triggered only in viewDidLoad (viewModel.action(input:
.fetchVersion)), so returning from the App Store or background won’t re-run it;
modify the controller to also trigger fetchVersion on viewWillAppear(_:) or
observe UIApplication.willEnterForegroundNotification and call
viewModel.action(input: .fetchVersion) from the observer using [weak self], and
ensure you remove the observer in deinit (or use the new NotificationCenter
token removal) to avoid leaks.

Comment on lines +466 to 494
viewModel.output.updateVersionPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] updateURL in
guard let updateURL else { return }

let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert
)

let cancel = UIAlertAction(
title: "취소",
style: .default,
handler: { _ in exit(0) })
let update = UIAlertAction(
title: "업데이트",
style: .default,
handler: { _ in
UIApplication.shared.open(updateURL, options: [:], completionHandler: { _ in exit(0) })
})

alert.addAction(cancel)
alert.addAction(update)
alert.preferredAction = update
self?.present(alert, animated: true)
}
.store(in: &cancellables)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

exit(0) 사용은 앱스토어 심사 리젝 및 UX 저하 가능성이 큽니다. 강제 업데이트는 ‘닫기 불가’ 알럿으로 대체하세요.

iOS에서 임의 종료는 가이드라인 위반 소지가 큽니다. ‘취소’ 없이 업데이트만 노출해 화면을 벗어날 수 없게 하고, 백그라운드 전환 시에도 알럿이 유지되도록 하는 방식이 일반적입니다. 아래와 같이 수정해 주세요.

-                let cancel = UIAlertAction(
-                    title: "취소",
-                    style: .default,
-                    handler: { _ in exit(0) })
                 let update = UIAlertAction(
                     title: "업데이트",
                     style: .default,
-                    handler: { _ in
-                        UIApplication.shared.open(updateURL, options: [:], completionHandler: { _ in exit(0) })
-                    })
+                    handler: { _ in
+                        if UIApplication.shared.canOpenURL(updateURL) {
+                            UIApplication.shared.open(updateURL, options: [:], completionHandler: nil)
+                        }
+                    })
 
-                alert.addAction(cancel)
                 alert.addAction(update)
                 alert.preferredAction = update
                 self?.present(alert, animated: true)

추가 권장:

  • 알럿 외부 닫힘 방지를 위해 ‘취소’ 액션 제거(현재처럼 .alert는 바깥 탭으로 닫히지 않음).
  • 앱 전역(예: 루트 코디네이터/인트로 흐름)에서 업데이트 게이트를 관리해 화면 전환/탭 이동도 차단.

🏁 Script executed:

#!/bin/bash
# 위험한 프로세스 종료 사용 위치 전수 조사
rg -nP '\bexit\s*\(\s*0\s*\)' -C3

Length of output: 1352


exit(0) 호출 제거 및 강제 업데이트 불가 닫힘(alert)으로 변경
iOS 임의 종료는 심사·UX 문제를 일으키므로, 취소 액션·completionHandler 내 exit(0) 호출을 제거하고 업데이트만 가능한 .alert로 구현하세요.

  • 앱 전역(루트 코디네이터)에서 업데이트 게이트를 관리해 화면 전환·백그라운드 전환 시에도 알럿이 유지되도록 차단
-                let cancel = UIAlertAction(
-                    title: "취소",
-                    style: .default,
-                    handler: { _ in exit(0) })
                 let update = UIAlertAction(
                     title: "업데이트",
                     style: .default,
-                    handler: { _ in
-                        UIApplication.shared.open(updateURL, options: [:], completionHandler: { _ in exit(0) })
-                    })
+                    handler: { _ in
+                        if UIApplication.shared.canOpenURL(updateURL) {
+                            UIApplication.shared.open(updateURL, options: [:], completionHandler: nil)
+                        }
+                    })
 
-                alert.addAction(cancel)
                 alert.addAction(update)
                 alert.preferredAction = update
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
viewModel.output.updateVersionPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] updateURL in
guard let updateURL else { return }
let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert
)
let cancel = UIAlertAction(
title: "취소",
style: .default,
handler: { _ in exit(0) })
let update = UIAlertAction(
title: "업데이트",
style: .default,
handler: { _ in
UIApplication.shared.open(updateURL, options: [:], completionHandler: { _ in exit(0) })
})
alert.addAction(cancel)
alert.addAction(update)
alert.preferredAction = update
self?.present(alert, animated: true)
}
.store(in: &cancellables)
}
viewModel.output.updateVersionPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] updateURL in
guard let updateURL else { return }
let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert
)
let update = UIAlertAction(
title: "업데이트",
style: .default,
handler: { _ in
if UIApplication.shared.canOpenURL(updateURL) {
UIApplication.shared.open(updateURL, options: [:], completionHandler: nil)
}
})
alert.addAction(update)
alert.preferredAction = update
self?.present(alert, animated: true)
}
.store(in: &cancellables)
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/View/HomeViewController.swift around lines
466 to 494, remove all exit(0) calls and make the update alert a forced-update
modal by eliminating the "취소" action (do not add exit in any handlers), keep
only the "업데이트" action which opens the URL without calling exit, and present the
alert from the app's root coordinator (or root view controller) instead of self
so the alert remains across screen/background transitions; ensure the alert
style remains .alert and do not add a cancel/dismiss action so users can only
tap "업데이트".

Comment on lines +277 to +297
private func checkVersion() {
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
let major = currentVersion?.split(separator: ".").first

Task {
do {
let appStoreAppVersion = try await appConfigRepository.fetchAppVersion()
let appStoreMajor = appStoreAppVersion?.split(separator: ".").first

if major != appStoreMajor {
let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
updateVersionSubject.send(url)
} else {
updateVersionSubject.send(nil)
}

} catch {

}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

버전 비교 로직의 nil/파싱 예외 처리 보강 및 실패 시 안전 동작 필요

현재는 옵셔널 비교와 에러 삼키기 때문에, 일시적 네트워크/파싱 실패에도 ‘강제 업데이트’로 오인될 위험이 있습니다. 실패 시에는 업데이트 미필요로 처리하고, 비교값은 확정적으로 언랩해 주세요.

-    private func checkVersion() {
-        let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
-        let major = currentVersion?.split(separator: ".").first
-
-        Task {
-            do {
-                let appStoreAppVersion = try await appConfigRepository.fetchAppVersion()
-                let appStoreMajor = appStoreAppVersion?.split(separator: ".").first
-
-                if major != appStoreMajor {
-                    let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
-                    updateVersionSubject.send(url)
-                } else {
-                    updateVersionSubject.send(nil)
-                }
-
-            } catch {
-
-            }
-        }
-    }
+    private func checkVersion() {
+        guard
+            let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+            let currentMajor = currentVersion.split(separator: ".").first
+        else {
+            updateVersionSubject.send(nil)
+            return
+        }
+
+        Task {
+            do {
+                guard
+                    let appStoreVersion = try await appConfigRepository.fetchAppVersion(),
+                    let appStoreMajor = appStoreVersion.split(separator: ".").first
+                else {
+                    updateVersionSubject.send(nil)
+                    return
+                }
+
+                if currentMajor != appStoreMajor {
+                    let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
+                    updateVersionSubject.send(url)
+                } else {
+                    updateVersionSubject.send(nil)
+                }
+            } catch {
+                updateVersionSubject.send(nil)
+            }
+        }
+    }

보완 포인트:

  • 메이저 비교 기준은 여기서는 “상이하면 강제 업데이트”, SettingViewModel은 “버전 전체 불일치”로 보입니다. 사용자 경험 일관성을 위해 기준 통합을 검토해 주세요.

🏁 Script executed:

#!/bin/bash
# SettingViewModel의 버전 비교 기준 확인
rg -nP 'SettingViewModel' -n --type swift -C3
rg -nP 'fetchVersion\(\)|versionSubject|needUpdate|latest' -n --type swift -C3

Length of output: 13336


버전 비교 로직 nil/파싱 예외 처리 강화 및 비교 기준 통합 검토

  • checkVersion()에서 currentVersion/appStoreVersionguard로 확실히 언랩하고, 실패 또는 에러 시 updateVersionSubject.send(nil) 처리
  • HomeViewModel(메이저 버전)과 SettingViewModel(전체 버전) 비교 기준 불일치하므로 UX 일관성에 맞춰 기준 통합 검토
     private func checkVersion() {
-        let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
-        let major = currentVersion?.split(separator: ".").first
+        guard
+            let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+            let currentMajor = currentVersion.split(separator: ".").first
+        else {
+            updateVersionSubject.send(nil)
+            return
+        }

         Task {
             do {
-                let appStoreAppVersion = try await appConfigRepository.fetchAppVersion()
-                let appStoreMajor = appStoreAppVersion?.split(separator: ".").first
+                guard
+                    let appStoreVersion = try await appConfigRepository.fetchAppVersion(),
+                    let appStoreMajor = appStoreVersion.split(separator: ".").first
+                else {
+                    updateVersionSubject.send(nil)
+                    return
+                }

                 if currentMajor != appStoreMajor {
                     let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
                     updateVersionSubject.send(url)
                 } else {
                     updateVersionSubject.send(nil)
                 }
             } catch {
-                
+                updateVersionSubject.send(nil)
             }
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private func checkVersion() {
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
let major = currentVersion?.split(separator: ".").first
Task {
do {
let appStoreAppVersion = try await appConfigRepository.fetchAppVersion()
let appStoreMajor = appStoreAppVersion?.split(separator: ".").first
if major != appStoreMajor {
let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
updateVersionSubject.send(url)
} else {
updateVersionSubject.send(nil)
}
} catch {
}
}
}
private func checkVersion() {
guard
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
let currentMajor = currentVersion.split(separator: ".").first
else {
updateVersionSubject.send(nil)
return
}
Task {
do {
guard
let appStoreVersion = try await appConfigRepository.fetchAppVersion(),
let appStoreMajor = appStoreVersion.split(separator: ".").first
else {
updateVersionSubject.send(nil)
return
}
if currentMajor != appStoreMajor {
let url = URL(string: "itms-apps://itunes.apple.com/app/id6749437799")
updateVersionSubject.send(url)
} else {
updateVersionSubject.send(nil)
}
} catch {
updateVersionSubject.send(nil)
}
}
}

Comment on lines 111 to 115
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// configureNavigationBar(navigationStyle: .withBackButton(title: "설정"))
navigationController?.navigationBar.isHidden = true
configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설정"))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

viewWillAppear에서 커스텀 네비게이션 바를 매번 추가 — 중복 추가 위험

configureCustomNavigationBar(...)가 매 진입마다 새로운 뷰를 붙이면 중복/레이아웃 누적 문제가 발생합니다. 1회만 추가하도록 가드하세요.

 final class SettingView: BaseViewController<SettingViewModel> {
+    private var didAddCustomNavigationBar = Bool(false)
 ...
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar.isHidden = true
-        configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설정"))
+        if !didAddCustomNavigationBar {
+            configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설정"))
+            didAddCustomNavigationBar = true
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// configureNavigationBar(navigationStyle: .withBackButton(title: "설정"))
navigationController?.navigationBar.isHidden = true
configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설정"))
}
final class SettingView: BaseViewController<SettingViewModel> {
private var didAddCustomNavigationBar = Bool(false)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.isHidden = true
if !didAddCustomNavigationBar {
configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설정"))
didAddCustomNavigationBar = true
}
}
// ... rest of the class ...
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Setting/View/SettingView.swift around lines
111–115, viewWillAppear currently calls configureCustomNavigationBar every time
which can attach the custom nav bar repeatedly; change to only add it once by
guarding (either move the call to viewDidLoad or add a boolean flag like
hasConfiguredCustomNavBar that you set after first configuration, or check for
an existing custom nav bar subview/tag before calling
configureCustomNavigationBar), keep the
navigationController?.navigationBar.isHidden = true behavior but ensure
configureCustomNavigationBar is skipped when already added.

Comment thread Projects/Presentation/Sources/Setting/View/SettingView.swift
Copy link
Copy Markdown
Contributor

@choijungp choijungp left a comment

Choose a reason for hiding this comment

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

띵푸루부 !! 확인했습니다 !!!!
만약 강종 때문에 리젝된다면 임의의 빈 화면을 보여주는 식으로 수정하는 것은 어떠신지요 ?!?!
만약 리젝이 된다며는 ㅠ...

그리고 .. MypageView, SettingView에서 테이블뷰 옆에 있는 화살표도 지글지글이로 바꿔야할 것 같아용 !!!!!
image

고생하셨숩니다 띵 !!!!!!!!!!!
굿띵 !!!!!!! 😇

Comment on lines +485 to +486
UIApplication.shared.open(updateURL, options: [:], completionHandler: { _ in exit(0) })
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

딩이 강종이 심사 리젝 사유라고 해가주구 생각해봤는데욤 ... 그냥 검정 화면 혹은 빈 화면을 띄우는건 어떤지 ??

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

아하 앱에 다시 돌아왔을때 빈 화면만 보여주자는 말씀이신가요??

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

네네네 !!!!!! 만약 리젝 된다면 .. ㅎㅎㅎㅎㅎ

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

조아요 꼼수더라도 일단 우회 고~!

Comment on lines +471 to +475
let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert
)
let alert = UIAlertController(
title: "업데이트가 필요합니다",
message: "원활한 이용을 위해, 빛나길을 업데이트 해주세요!",
preferredStyle: .alert)

요것이 컨벤션에 맞는 것 같습니두아 !!!!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

89bd156 에서 반영했습니다. 감사합니다!

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 (3)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (3)

31-35: withRenderingMode 중복 제거 + RTL 대응 + 다크 모드 컬러 제안

  • BitnagilIcon.bitnagilChevronIcon(direction:)에서 이미 .withRenderingMode(.alwaysTemplate)를 적용하고 있어 여기서의 재호출은 중복입니다.
  • 방향을 .right로 고정하면 RTL 환경(아랍어 등)에서 시각적으로 부자연스럽습니다. semanticContentAttribute 기반으로 방향을 유추해 주세요.
  • 틴트 컬러를 .black로 고정하면 다크 모드에서 대비/일관성이 떨어질 수 있습니다. .label 또는 DesignSystem 컬러 토큰 사용을 권장합니다.
  • 아이콘 왜곡 방지를 위해 contentMode = .scaleAspectFit 추가 추천.
-        chevronImageView.tintColor = .black
-        chevronImageView.image = BitnagilIcon
-            .bitnagilChevronIcon(direction: .right)?
-            .withRenderingMode(.alwaysTemplate)
+        chevronImageView.tintColor = .label // 또는 DesignSystem 컬러 토큰
+        let layoutDir = UIView.userInterfaceLayoutDirection(for: contentView.semanticContentAttribute)
+        let chevronDirection: BitnagilIcon.Direction = (layoutDir == .rightToLeft) ? .left : .right
+        chevronImageView.image = BitnagilIcon
+            .bitnagilChevronIcon(direction: chevronDirection)
+        chevronImageView.contentMode = .scaleAspectFit

13-15: 사이즈 상수 통합 및 스페이싱 토큰화 검토

  • width/height를 별도 상수로 둘 필요가 크지 않습니다. CGSize 하나로 통합하면 가독성이 좋아지고 제약 설정도 간결해집니다.
  • trailing 스페이싱(20pt)은 다른 셀/컴포넌트와의 일관성 유지가 중요합니다. DesignSystem의 spacing 토큰(예: Spacing.lg)으로 관리하는 방안을 고려해 주세요.
-        static let chevronImageViewTrailingSpacing: CGFloat = 20
-        static let chevronImageWidth: CGFloat = 7
-        static let chevronImageHeight: CGFloat = 11
+        static let chevronImageViewTrailingSpacing: CGFloat = 20 // TODO: DS 토큰으로 교체 검토
+        static let chevronImageSize = CGSize(width: 7, height: 11)

45-46: SnapKit 제약 간결화 (size(equalTo:))

위 사이즈 상수 통합 제안과 함께 사용하면 제약을 더 간단히 표현할 수 있습니다.

-            make.width.equalTo(Layout.chevronImageWidth)
-            make.height.equalTo(Layout.chevronImageHeight)
+            make.size.equalTo(Layout.chevronImageSize)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ebda5bb and 89bd156.

📒 Files selected for processing (4)
  • Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (3 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Home/View/HomeViewController.swift (2 hunks)
  • Projects/Presentation/Sources/Setting/View/SettingView.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • Projects/Presentation/Sources/Home/View/HomeViewController.swift
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift
  • Projects/Presentation/Sources/Setting/View/SettingView.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (1)
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)
  • bitnagilChevronIcon (19-21)

@taipaise taipaise merged commit 05b1f53 into develop Sep 1, 2025
1 of 2 checks passed
@choijungp choijungp deleted the fix/setting branch November 23, 2025 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants