Skip to content

[Feat-T3-132] 설정 화면 구현#32

Merged
taipaise merged 8 commits into
developfrom
feat/setting
Aug 3, 2025
Merged

[Feat-T3-132] 설정 화면 구현#32
taipaise merged 8 commits into
developfrom
feat/setting

Conversation

@taipaise
Copy link
Copy Markdown
Collaborator

@taipaise taipaise commented Aug 2, 2025

🌁 Background

  • 설정 화면 구현
  • 로그 아웃, 회원 탈퇴에서 사용되는 custom alert 화면 구현

📱 Screenshot

설정

iPhone SE3 iPhone 13 mini iPhone 16 Pro

alert

iPhone SE3 iPhone 13 mini iPhone 16 Pro

👩‍💻 Contents

  • 커스텀 alert 화면 구현
    • 텍스트만 표시되는 버전
    • 이미지가 함께 표시되는 버전
  • 설정 화면 구현

📝 Review Note

커스텀 alert 화면 구현

  • custom alert 화면을 아래 두 가지로 구현했습니다.
    • (아마도 아마도) custom alert가 한 번 정해지면, 많은 서비스에서 구현된 alert 화면을 재활용한다는 점에서, 이미지 없이 text로만 구현된 alert가 쓰일 수도 있지 않을까? 라는 생각을 했습니다. 때문에 alert를 생성하는 생성자에서 이미지 존재 여부를 설정할 수 있게 했습니다.
이미지 있음 텍스트만 있음
  • 생성자는 아래와 같습니다. alert의 경우 configure와 같은 setter를 사용할 필요 없이, 생성 시점에 필요한 모든 프로퍼티의 값을 정할 수 있다 생각했습니다.
    • 취소/완료시 동작을 delegate가 아닌 클로저로 처리했습니다. 한 화면에서 여러 case의 alert를 사용하는 경우가 많이 생길 수 있다 생각했기 때문입니다. (ex.한 화면에서 로그아웃 alert, 회원탈퇴 alert를 띄울 수 있음)
    • delegate로 취소/완료 로직을 관리했다면, 한 화면에서 표시할 수 있는 alert에 따라 delegate 함수 내에서 많은 분기를 치게 되지 않을까.?.? 라고 생각합니다.
init(
       alertType: AlertType,                                           // 여기서 이미지를 보여줄건지, 텍스트만 표시할건지 선택
       title: String,                                                          // 알림의 제목
       content: String,                                                   // 알림의 내용
       cancelButtonTitle: String,                                  // 취소 버튼에 올 버튼의 텍스트
       confirmButtonTitle: String,                                // 확인 버튼에 올 버튼의 텍스트
       cancelHandler: (() -> Void)?,                           // 취소 버튼 누를 시 실행할 핸들러
       confirmHandler: (() -> Void)?                          // 확인 버튼 누를 시 실행할 핸들러
   ) {
       self.alertType = alertType
       super.init(nibName: nil, bundle: nil)

       self.cancelHandler = cancelHandler
       self.confirmHandler = confirmHandler
       titleLabel.text = title
       contentLabel.text = content
       cancelButton.setTitle(cancelButtonTitle, for: .normal)
       confirmButton.setTitle(confirmButtonTitle, for: .normal)

       configureAttribute()
       configureLayout()
   }

tableViewCell 커스텀

  • 기존에 마이페이지에서 사용하던 MypageTableViewCell의 이름을 BitnagilBaseTableViewCell로 수정했습니다. BitnagilBaseTableViewCell은 단순하게 cell의 Title 부분만 표시하도록 구현되어 있습니다.
  • 이름을 수정한 이유는, 여러 군데에서 재활용할 여지가 굉장히 많다고 생각했기 때문입니다. 그래서 해당 TableViewCell을 상속해서 여러 TableViewCell을 구성할 수 있도록 했습니다.
  • 구현한 cell은 아래와 같습니다.
    • BitnagilBaseTableViewCell
    • BitnagilButtonTableViewCellDelegate : 우측에 버튼이 있다. 추후 앱스토어 업데이트 리디렉션 구현할 때 사용할 예정
    • BitnagilChevronTableViewCell : 우측에 chevron 이미지가 있다. 주로 다음 화면이나 웹 페이지로 이동할 때 사용됨
    • BitnagilToggleTableViewCellDelegate : UISwitch가 우측에 위치. 켜고 끌 수 있다. (toggle)

매직 넘버(Magic Number) 없이 TableView 조작하기

  • 코드가 뻥튀기 된 주 원인이 요 부분이 아닐까 합니다. 설정 화면을 처음 봤을 때, TableView를 여러 Section으로 나누어 표시해야겠다는 생각을 했습니다. 다만 코드의 가독성과 유지보수성을 고려해서 indexPath를 조작할 때 매직 넘버 없이, 제한된 namespace로 section과 row를 조작할 수 있도록 했습니다.
  • Section과 각 Section에 대한 정보를 열거형(enum)으로 관리하고 있기 때문에, tableViewDatasource의 메서드나 tableViewDelegate 메서드 내에서 switch를 통한 분기처리가 많이 들어가 코드가 길어지는 부분이 있습니다. 하지만 매직 넘버 없이 Section과 Row를 명확하게 관리할 수 있어, 유지보수에는 더 유리하다고 판단했습니다.
  • 혹시나 관련해서 더 좋은 방법이나, 너무 길어진 코드 때문에 오히려 가독성이 떨어진다!! 차라리 숫자로 컨트롤하는게 낫다 하시는 의견이 있으시다면 편하게 말씀해 주시면 감사하겠습니다!!

Setting View가 조금 똑똑해요 ㅠ

  • 이번 코드는 View가 알고 있는 정보가 조금 많다고 생각합니다.. Section의 구성 정보나, customAlert에 표시할 문구를 알고 있다던지, cell에 표시할 문자를 알고 있다던지 하는 부분입니다.
  • 제가 요렇게 구현한 이유는, viewModel이 알고 있을 때 view에 대한 의존성이 생긴다 (다시 말해 vm이 view를 알게 된다)고 판단한 부분이 있기 때문입니다. 예를 들면 아래와 같은 항목들이 그렇습니다.
    • 몇번째 section의 몇번째 row가 어떤 cellType으로 되어 있는지 (토글이 가능한 cell인지, 버튼이 있는 cell인지 등)
    • 각 section의 구성 정보 (몇개의 section으로 view가 구성되어 있고, section안에는 어떤 내용이 있는지 등)
  • 아직 View에서 관리하고 있는 정보가 많아 이상적인 구조라 보긴 어렵다고 생각합니다. 더 나은 방식이나 구조가 있으시다면 편하게 의견 부탁드립니다!

Summary by CodeRabbit

  • 신규 기능

    • 설정 화면이 새롭게 추가되어 알림, 정보, 계정 관련 옵션을 관리할 수 있습니다.
    • 알림 토글, 버튼, 체브론 등 다양한 커스텀 테이블 뷰 셀과 헤더 뷰가 도입되었습니다.
    • 커스텀 알림창(비트나길 알럿) 컴포넌트가 제공되어 다양한 스타일의 안내를 지원합니다.
    • 새로운 아이콘(느낌표 채움)이 추가되었습니다.
  • 개선 사항

    • 마이페이지의 셀 및 네비게이션 바 스타일이 개선되었습니다.
    • 설정 화면에서 각종 외부 링크, 로그아웃, 회원탈퇴 등 주요 기능이 동작합니다.
    • 네비게이션 바의 외관이 더욱 깔끔하게 개선되었습니다.
  • 버그 수정

    • 테이블 뷰의 바운스 동작이 비활성화되어 더욱 일관된 스크롤 경험을 제공합니다.

@taipaise taipaise requested a review from choijungp August 2, 2025 17:37
@taipaise taipaise self-assigned this Aug 2, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 2, 2025

Walkthrough

이 변경사항은 설정(Setting) 화면의 전체 구현을 추가하며, 새로운 테이블 뷰 셀 컴포넌트, 헤더 뷰, 커스텀 알럿, 뷰모델, 그리고 디자인 리소스(아이콘 및 에셋)를 도입합니다. 마이페이지의 셀 교체, DI 컨테이너 등록, 네비게이션 바 appearance 개선 등도 포함되어 있습니다.

Changes

Cohort / File(s) Change Summary
설정 화면 전체 구현
Projects/Presentation/Sources/Setting/View/SettingView.swift, .../Setting/ViewModel/SettingViewModel.swift, .../Setting/View/Component/SettingHeaderView.swift
SettingView, SettingViewModel, SettingHeaderView 등 설정 화면의 UI·로직·헤더 구현 및 섹션/셀/상호작용/바인딩 추가
테이블 뷰 셀 컴포넌트
.../TableViewCell/BitnagilBaseTableViewCell.swift, .../TableViewCell/BitnagilButtonTableViewCell.swift, .../TableViewCell/BitnagilChevronTableViewCell.swift, .../TableViewCell/BitnagilToggleTableViewCell.swift
공통 베이스 셀 및 버튼/체브론/토글 셀 신규 도입, 기존 셀 리팩토링 및 클래스명·상속 구조 변경
커스텀 알럿
.../View/BitnagilAlert.swift
BitnagilAlert 커스텀 알럿 뷰 컨트롤러 추가, 이미지/텍스트/버튼/핸들러 등 옵션 지원
디자인 시스템 및 리소스
.../DesignSystem/BitnagilIcon.swift, .../Resources/Images.xcassets/exclamation_filled_icon.imageset/Contents.json
exclamation_filled_icon 에셋 및 BitnagilIcon에 해당 이미지 프로퍼티 추가
DI 컨테이너 등록
.../PresentationDependencyAssembler.swift
SettingViewModel DI 컨테이너에 등록 추가
마이페이지 뷰 변경
.../MyPage/View/MypageView.swift
마이페이지 셀을 BitnagilChevronTableViewCell로 교체, SettingView 연결 구현
UIViewController 네비게이션 바 appearance 개선
.../Extension/UIViewController+.swift
네비게이션 바 appearance 설정 코드 추가

Sequence Diagram(s)

sequenceDiagram
actor User
participant SettingView
participant SettingViewModel
participant BitnagilAlert
participant Safari

User->>SettingView: 토글/버튼/체브론 셀 터치
SettingView->>SettingViewModel: action(input:)
SettingViewModel-->>SettingView: Output publisher emit
SettingView->>BitnagilAlert: (필요 시) 알럿 표시
SettingView->>Safari: (필요 시) 외부 URL 오픈
BitnagilAlert-->>SettingView: 버튼 액션 콜백
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • YAPP-Github/Bitnagil-iOS#21:
    비슷하게 BitnagilIcon.swift에 새로운 아이콘(static UIImage 프로퍼티) 추가 및 에셋 등록을 다루는 PR로, 코드 레벨에서 강하게 연관되어 있습니다.

Poem

🐰
새로운 셀과 알럿이 깡총,
설정 화면이 한껏 멋져졌지!
버튼, 토글, 체브론 춤을 추고,
알림과 정보가 한눈에 쏙.
토끼도 기뻐 깡총깡총,
Bitnagil 세팅, 이제 완성!
🌟

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/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.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

Documentation and Community

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

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: 3

🧹 Nitpick comments (10)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift (1)

13-13: 오타 수정이 필요합니다.

상수명에 오타가 있습니다: titleLableLeadingSpacingtitleLabelLeadingSpacing

-        static let titleLableLeadingSpacing: CGFloat = 20
+        static let titleLabelLeadingSpacing: CGFloat = 20

그리고 41번 라인에서도 같은 상수명을 수정해야 합니다:

-            make.leading.equalToSuperview().offset(Layout.titleLableLeadingSpacing)
+            make.leading.equalToSuperview().offset(Layout.titleLabelLeadingSpacing)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilToggleTableViewCell.swift (1)

38-41: 사용하지 않는 클로저 매개변수를 처리해주세요.

정적 분석 도구에서 지적한 대로, action 매개변수를 사용하지 않으므로 _로 대체해주세요.

-        toggleSwitch.addAction(UIAction { [weak self] action in
+        toggleSwitch.addAction(UIAction { [weak self] _ in
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (3)

11-13: 델리게이트 메서드명을 간소화하세요

델리게이트 메서드명이 불필요하게 장황합니다. 이미 BitnagilButtonTableViewCellDelegate 프로토콜 내부에 있으므로 메서드명을 간소화할 수 있습니다.

 protocol BitnagilButtonTableViewCellDelegate: AnyObject {
-    func bitnagilButtonTableViewCellDidTapButton(_ sender: BitnagilButtonTableViewCell)
+    func didTapButton(_ sender: BitnagilButtonTableViewCell)
 }

이에 따라 호출 부분도 수정이 필요합니다:

-                self.delegate?.bitnagilButtonTableViewCellDidTapButton(self)
+                self.delegate?.didTapButton(self)

42-42: 일관된 초기화 스타일을 사용하세요

.init 대신 타입명을 직접 사용하는 것이 더 명확합니다.

-        let font = BitnagilFont.init(style: .body2, weight: .semiBold).font
+        let font = BitnagilFont(style: .body2, weight: .semiBold).font

63-63: 일관된 초기화 스타일을 사용하세요

여기서도 .init 대신 타입명을 직접 사용하세요.

-        button.titleLabel?.font = BitnagilFont.init(style: .body2, weight: .semiBold).font
+        button.titleLabel?.font = BitnagilFont(style: .body2, weight: .semiBold).font
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (2)

94-96: 앱스토어 열기 구현

TODO 주석이 있는 앱스토어 열기 기능을 구현해야 합니다.

앱스토어 열기 기능을 구현하는 코드를 생성해드릴까요? 또는 이 작업을 추적하기 위한 새 이슈를 열어드릴까요?


98-100: 로그아웃 및 회원탈퇴 API 구현

로그아웃과 회원탈퇴 API 호출이 구현되어 있지 않습니다.

로그아웃과 회원탈퇴 API 호출을 구현하는 코드를 생성해드릴까요? 인증 관련 로직이 구현되어야 설정 화면이 완전히 작동할 것입니다.

Also applies to: 102-104

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

183-183: 버전 표시 문자열에 공백을 추가하세요

버전 번호 앞에 공백이 없어 가독성이 떨어집니다.

-                    cell.configure(title: "버전\(version)", buttonTitle: "업데이트", isButtonEnabled: true)
+                    cell.configure(title: "버전 \(version)", buttonTitle: "업데이트", isButtonEnabled: true)
-                    cell.configure(title: "버전\(version)", buttonTitle: "최신", isButtonEnabled: false)
+                    cell.configure(title: "버전 \(version)", buttonTitle: "최신", isButtonEnabled: false)

Also applies to: 185-185


190-195: 사용하지 않는 클로저 파라미터를 언더스코어로 교체하세요

isAuthenticated 파라미터가 사용되지 않으므로 언더스코어로 교체해야 합니다.

         viewModel.output.isAuthenticatedPublisher
             .receive(on: DispatchQueue.main)
-            .sink(receiveValue: { isAuthenticated in
+            .sink(receiveValue: { _ in
             // 로그아웃 완료 후 홈 화면으로
             })
             .store(in: &cancellables)

339-340: 델리게이트 메서드명 업데이트

BitnagilButtonTableViewCell의 델리게이트 메서드명을 간소화하면 여기도 함께 수정해야 합니다.

 extension SettingView: BitnagilButtonTableViewCellDelegate {
-    func bitnagilButtonTableViewCellDidTapButton(_ sender: BitnagilButtonTableViewCell) {
+    func didTapButton(_ sender: BitnagilButtonTableViewCell) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5587dc2 and 09d2cf2.

⛔ Files ignored due to path filters (3)
  • Projects/Presentation/Resources/Images.xcassets/exclamation_filled_icon.imageset/exclamation_filled_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/exclamation_filled_icon.imageset/exclamation_filled_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/exclamation_filled_icon.imageset/exclamation_filled_icon@3x.png is excluded by !**/*.png
📒 Files selected for processing (13)
  • Projects/Presentation/Resources/Images.xcassets/exclamation_filled_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift (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/Component/TableViewCell/BitnagilToggleTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Common/View/BitnagilAlert.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (4 hunks)
  • Projects/Presentation/Sources/Setting/View/Component/SettingHeaderView.swift (1 hunks)
  • Projects/Presentation/Sources/Setting/View/SettingView.swift (1 hunks)
  • Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
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/MyPage/View/MypageView.swift
  • Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift
  • Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/Presentation/Sources/MyPage/View/MypageView.swift
  • Projects/Presentation/Sources/Setting/View/SettingView.swift
🧬 Code Graph Analysis (4)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • register (14-16)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift (3)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (2)
  • configureAttribute (27-34)
  • configureLayout (36-46)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (3)
  • configureAttribute (60-71)
  • configureLayout (73-86)
  • configure (35-58)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilToggleTableViewCell.swift (2)
  • configureAttribute (33-42)
  • configureLayout (44-55)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilChevronTableViewCell.swift (2)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift (2)
  • configureAttribute (32-34)
  • configureLayout (36-43)
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)
  • chevronIcon (20-22)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilButtonTableViewCell.swift (2)
Projects/Presentation/Sources/Setting/View/SettingView.swift (1)
  • bitnagilButtonTableViewCellDidTapButton (340-355)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilBaseTableViewCell.swift (3)
  • configure (28-30)
  • configureAttribute (32-34)
  • configureLayout (36-43)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Common/Component/TableViewCell/BitnagilToggleTableViewCell.swift

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

(unused_closure_parameter)

Projects/Presentation/Sources/Setting/View/SettingView.swift

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

(unused_closure_parameter)

Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift

[Warning] 95-95: TODOs should be resolved (- 앱스토어 열기)

(todo)


[Warning] 99-99: TODOs should be resolved (- 로그아웃 api)

(todo)


[Warning] 103-103: TODOs should be resolved (- 회원탈퇴 api)

(todo)

🔇 Additional comments (13)
Projects/Presentation/Resources/Images.xcassets/exclamation_filled_icon.imageset/Contents.json (1)

1-23: LGTM!

에셋 카탈로그 JSON 파일이 표준 구조를 정확히 따르고 있으며, 모든 필요한 스케일 변형(1x, 2x, 3x)이 적절히 정의되어 있습니다.

Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)

43-43: LGTM!

새로운 아이콘이 기존 패턴을 정확히 따르고 있으며, 적절한 섹션에 배치되어 있습니다.

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

80-82: LGTM!

SettingViewModel의 DI 등록이 기존 패턴을 정확히 따르고 있으며, 의존성이 없는 단순한 뷰모델에 적합한 구현입니다.

Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (1)

13-18: 네비게이션 바 외관 설정 개선이 좋습니다.

UINavigationBarAppearance를 사용한 일관된 네비게이션 바 스타일링이 잘 구현되었습니다. standardAppearance와 scrollEdgeAppearance 모두에 적용하여 모든 상황에서 일관된 외관을 보장합니다.

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

48-48: 명시적 타겟 설정이 좋습니다.

UIBarButtonItem의 target을 명시적으로 설정하여 코드의 명확성을 향상시켰습니다.


64-64: 테이블 뷰 셀 리팩토링이 적절합니다.

MypageTableViewCell에서 BitnagilChevronTableViewCell로 변경하여 공통 컴포넌트 체계에 맞게 개선되었습니다.

Also applies to: 139-139


68-68: bounces 설정으로 UX 개선.

테이블 뷰의 bounces를 false로 설정하여 스크롤이 필요하지 않은 콘텐츠에 대한 사용자 경험을 개선했습니다.


119-122: 설정 화면 네비게이션 구현이 적절합니다.

DI 컨테이너를 통한 안전한 의존성 해결과 표준 네비게이션 패턴을 따르는 구현이 좋습니다.

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

11-44: 잘 구현된 베이스 클래스입니다.

템플릿 메서드 패턴을 사용하여 하위 클래스들이 공통 기능을 재사용할 수 있도록 잘 설계되었습니다. configureAttribute()configureLayout() 메서드를 통해 확장 가능한 구조를 제공하고 있습니다.

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

11-47: 베이스 클래스 상속이 올바르게 구현되었습니다.

BitnagilBaseTableViewCell에서 상속받아 super.configureAttribute()super.configureLayout()을 적절히 호출하고 있으며, 셰브론 이미지 설정과 레이아웃이 깔끔하게 구현되었습니다. 기존 MypageTableViewCell에서 리팩터링이 성공적으로 이루어졌습니다.

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

15-60: 델리게이트 패턴과 메모리 관리가 잘 구현되었습니다.

weak delegate 사용과 클로저에서의 weak self 캡처를 통해 순환 참조를 방지하고 있으며, 토글 상태 변경을 적절히 델리게이트로 전달하고 있습니다. 베이스 클래스 상속도 올바르게 구현되었습니다.

Projects/Presentation/Sources/Setting/View/Component/SettingHeaderView.swift (1)

11-67: 섹션 헤더 뷰가 잘 구현되었습니다.

shouldShowDivider 매개변수에 따라 구분선 표시 여부와 타이틀 레이블의 top constraint를 동적으로 조정하는 로직이 깔끔하게 구현되었습니다. SnapKit을 사용한 레이아웃 관리와 상수 정의도 일관성 있게 처리되었습니다.

Projects/Presentation/Sources/Common/View/BitnagilAlert.swift (1)

11-179: 커스텀 알럿이 잘 구현되었습니다.

두 가지 알럿 타입(withImage, plainText)을 지원하며, 클로저 기반의 버튼 핸들러로 델리게이트 패턴보다 간단하게 처리할 수 있도록 설계되었습니다. 메모리 관리(weak self 캡처)와 배경 탭으로 닫기 기능도 적절히 구현되었습니다.

이전 학습 내용에 따르면 에러 처리를 위한 CustomAlertView 계획이 있었는데, 이 구현체가 그 목적에 부합하는 것으로 보입니다.

Comment thread Projects/Presentation/Sources/Setting/View/SettingView.swift Outdated
Comment thread Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift Outdated
Comment thread Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (2)

18-21: 하드코딩된 URL을 환경별로 관리하는 것을 고려하세요

현재 이용약관과 개인정보처리방침 URL이 하드코딩되어 있습니다. 향후 개발/스테이징/프로덕션 환경별로 다른 URL을 사용해야 할 수 있으므로 설정 파일이나 환경변수로 관리하는 것을 고려해보세요.


94-105: TODO 주석들에 대한 구현 계획을 명확히 하세요

SwiftLint에서 지적된 TODO 주석들이 있습니다. 이들 기능의 구현 우선순위와 일정을 명확히 하거나, 당장 구현하지 않을 경우 이슈로 등록하는 것을 고려해보세요.

특히 logout(), withdrawal(), updateBitnagil() 메서드들은 설정 화면의 핵심 기능이므로 구현 계획이 필요합니다.

이들 기능의 구체적인 구현을 도와드릴까요? 예를 들어:

  • 로그아웃 API 호출 및 토큰 삭제
  • 회원탈퇴 API 호출 및 사용자 데이터 정리
  • App Store URL 열기
Projects/Presentation/Sources/Setting/View/SettingView.swift (5)

192-194: 사용하지 않는 클로저 매개변수를 언더스코어로 대체하세요

SwiftLint에서 지적한 대로 사용하지 않는 isAuthenticated 매개변수를 _로 대체해야 합니다.

-            .sink(receiveValue: { isAuthenticated in
+            .sink(receiveValue: { _ in
             // 로그아웃 완료 후 홈 화면으로
             })

183-185: 버전 문자열 포맷팅에 공백을 추가하세요

현재 "버전1.0.0" 형태로 표시되는데, 가독성을 위해 "버전 1.0.0" 형태로 공백을 추가하는 것이 좋겠습니다.

-                    cell.configure(title: "버전\(version)", buttonTitle: "업데이트", isButtonEnabled: true)
+                    cell.configure(title: "버전 \(version)", buttonTitle: "업데이트", isButtonEnabled: true)
-                    cell.configure(title: "버전\(version)", buttonTitle: "최신", isButtonEnabled: false)
+                    cell.configure(title: "버전 \(version)", buttonTitle: "최신", isButtonEnabled: false)

266-301: 셀 생성 로직이 복잡합니다 - 리팩토링을 고려해보세요

현재 cellForRowAt 메서드가 매우 복잡합니다. 각 섹션별로 별도의 메서드로 분리하거나, 셀 팩토리 패턴을 도입하는 것을 고려해보세요.

private func createCell(for cellStyle: CellStyle, at indexPath: IndexPath) -> UITableViewCell {
    switch cellStyle {
    case .toggle(let title):
        return createToggleCell(title: title, at: indexPath)
    case .button(let title):
        return createButtonCell(title: title, at: indexPath)
    case .chevron(let title):
        return createChevronCell(title: title, at: indexPath)
    }
}

308-316: 불필요한 UIView 생성을 제거하세요

notification 섹션 헤더에서 단순히 공백을 위해 UIView를 생성하는 것보다는 nil을 반환하고 높이만 조정하는 것이 더 효율적입니다.

        case .notification:
-            let view = UIView()
-            view.backgroundColor = .white
-            return view
+            return nil

130-132: 레이아웃 제약 조건에 중복이 있습니다

edgeshorizontalEdges가 중복됩니다. edges만 사용하면 됩니다.

-            make.edges.horizontalEdges.equalToSuperview()
+            make.edges.equalToSuperview()
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 09d2cf2 and f5d1078.

📒 Files selected for processing (2)
  • Projects/Presentation/Sources/Setting/View/SettingView.swift (1 hunks)
  • Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
📚 Learning: routinecreationinputview에서 textfield(_:shouldchangecharactersin:replacementstring:) 메서드가 변경 전 텍스트를 d...
Learnt from: taipaise
PR: YAPP-Github/Bitnagil-iOS#25
File: Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationInputView.swift:104-111
Timestamp: 2025-07-27T10:05:46.043Z
Learning: RoutineCreationInputView에서 textField(_:shouldChangeCharactersIn:replacementString:) 메서드가 변경 전 텍스트를 delegate에 전달하는 이유: 변경된 텍스트를 전달하면 텍스트 입력 중에 다른 UI 액션(예: 서브루틴 추가)이 발생할 때 입력 중이던 텍스트가 손실되는 문제가 발생하기 때문. 이는 의도적인 구현으로 동시성 문제를 방지함.

Applied to files:

  • Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/Presentation/Sources/Setting/View/SettingView.swift
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
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
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Setting/View/SettingView.swift

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

(unused_closure_parameter)

Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift

[Warning] 95-95: TODOs should be resolved (- 앱스토어 열기)

(todo)


[Warning] 99-99: TODOs should be resolved (- 로그아웃 api)

(todo)


[Warning] 103-103: TODOs should be resolved (- 회원탈퇴 api)

(todo)

🔇 Additional comments (5)
Projects/Presentation/Sources/Setting/ViewModel/SettingViewModel.swift (2)

34-34: 과거 리뷰 코멘트의 오타가 수정되었습니다

이전 리뷰에서 지적된 udpate -> update 오타가 정상적으로 수정되었습니다.


70-70: 과거 리뷰 코멘트의 메서드명 오타가 수정되었습니다

이전 리뷰에서 지적된 togglelPushNotification -> togglePushNotification 오타가 정상적으로 수정되었습니다.

Also applies to: 88-88

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

349-349: 과거 리뷰 코멘트의 오타가 수정되었습니다

이전 리뷰에서 지적된 .udpate -> .update 오타가 정상적으로 수정되었습니다.


221-242: 알럽 설정이 잘 구현되었습니다

CustomAlertView를 사용한 로그아웃과 회원탈퇴 확인 알럽이 잘 구현되었습니다. 이전 학습 내용에서 언급된 CustomAlertView 사용 계획이 잘 반영되었네요.


109-112: BaseViewController 패턴을 올바르게 사용하고 있습니다

이전 학습 내용에 따르면 BaseViewController의 viewDidLoad()에서 이미 필요한 메서드들을 호출하므로, viewWillAppear에서 네비게이션 바만 설정하는 현재 구현이 올바릅니다.

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.

SettingView가 똒똒한 이유는 알림, 정보, 계정 요로케 분기처리 해야 해서 그른거죠 ???
그럼 어쩔 수 없는 것 같어요 .. (현재 상황에서는 ...ResultRecommendedRoutineView 애도 너무너무 똑똑함 ....... but 개선해야죠 )

완존 추후 확장성 + 재사용성을 고려한 PR인 것 같슴니다 !!!!!
완고 띵동 !!!!!!!!!! 👍🏻 띵푸루부

Comment on lines +100 to +101
self?.cancelHandler?()
self?.dismiss(animated: false)
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.

여기서 dismiss까지 처리하려고 delegate가 아닌 closure로 해주신건가요 !!!??!?

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.

Delegate로 구현해도 버튼의 액션 내부에서 결국 dismiss()를 호출했을 것 같아요! (물론, 제가 모르는 제한이 없다면요.)
클로저 방식으로 구현한 이유는, alert를 호출하는 ViewController에서 여러 종류의 alert를 띄울 수 있기 때문입니다!

Delegate를 쓰면 취소/확인 버튼 탭 시 모두 동일한 delegate 메서드로 들어오게 되는데, 이 때마다 “지금 뜬 alert가 로그아웃인지, 탈퇴인지”를 분기해야 하는 문제가 있었습니다. 그리고 분기를 위해서는 AlertView역시 본인이 어떤 Alert인지에 대한 정보를 가지고 있고, delegate 실행 시 해당 값을 넘겨주어야 합니다. (예: alert 생성 시에 alertId 같은 값을 전달해서, delegate 실행 시 id값을 넘기고, 위임받은 객체에서 id를 기반으로 분기)

이렇게 분기처리하는 것보다는, 클로저로 각 alert에 필요한 로직을 직접 넘겨주는 편이 더 유연하고 직관적이라고 생각했어요!

@taipaise taipaise merged commit a5eff16 into develop Aug 3, 2025
2 checks passed
@taipaise taipaise deleted the feat/setting branch August 5, 2025 13:13
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