Skip to content

[Feat-T3-104] 마이페이지 UI 구현#22

Merged
taipaise merged 5 commits into
developfrom
feat/mypage
Jul 21, 2025
Merged

[Feat-T3-104] 마이페이지 UI 구현#22
taipaise merged 5 commits into
developfrom
feat/mypage

Conversation

@taipaise

@taipaise taipaise commented Jul 20, 2025

Copy link
Copy Markdown
Collaborator

🌁 Background

  • 마이페이지 화면 구현

📱 Screenshot

iPhone SE3 iPhone 13 mini iPhone 16 Pro
se3 13mini 16pro

👩‍💻 Contents

  • 마이페이지 화면 구현
  • 유저의 정보 (닉네임, accessToken)을 가져올 수 있는 repository 구현
  • NSObject의 class이름을 String으로 가져올 수 있는 extension 추가

📝 Review Note

  • 짧은 회의에서 말씀 드린대로, UseCase가 단순 데이터 바이패싱하는 것을 지양하기 위해 ViewModel에서 Repository를 직접 참조하도록 구현했습니다. (요 부분은 추후 다른 개발자 분들의 의견과, 추가적인 토론을 통해 결정되는 내용으로 수정하겠습니다)
  • UserDataRepository가 할 수 있는 역할을 러프하게 닉네임 가져오기 / accessToken 가져오기만 protocol에 추가해두었습니다.
    • 해당 레포가 할 수 있는 역할에 대해서는 조금 더 논의해보면 좋을 것 같습니다!

📬 Reference

Summary by CodeRabbit

  • 신규 기능

    • 마이페이지 화면 및 뷰모델이 추가되어 닉네임 표시, 메뉴(목표 재설정, 공지사항, FAQ) 제공, 외부 링크 연결 기능이 제공됩니다.
    • 마이페이지용 커스텀 셀(우측 화살표 아이콘 포함)이 추가되었습니다.
    • 설정 아이콘, 우측 화살표 아이콘이 앱 리소스에 추가되었습니다.
    • 사용자 데이터 저장소 프로토콜과 구현체가 추가되어 액세스 토큰과 닉네임 로드 기능이 강화되었습니다.
  • 리팩터링/기타

    • NSObject에 클래스명 반환 프로퍼티가 추가되었습니다.
    • 의존성 주입 컨테이너에 관련 뷰모델 및 저장소가 등록되었습니다.
    • 탭바 뷰에서 마이페이지 뷰가 의존성 주입을 통해 초기화되도록 변경되었습니다.

@taipaise
taipaise requested a review from choijungp July 20, 2025 14:27
@taipaise taipaise self-assigned this Jul 20, 2025
@coderabbitai

coderabbitai Bot commented Jul 20, 2025

Copy link
Copy Markdown
## Walkthrough

이번 변경에서는 마이페이지 화면 및 관련 기능을 전면적으로 도입하였습니다. 사용자 데이터 저장소 프로토콜 및 구현체, 에러 타입, 뷰모델, 뷰, 테이블뷰 셀 등이 새로 추가되었으며, DI 컨테이너에 필요한 의존성 등록도 이루어졌습니다. 아이콘 리소스와 공통 유틸리티 확장도 포함됩니다.

## Changes

| 파일/경로                                                                                              | 변경 요약                                                                                             |
|--------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
| .../DataSourceDependencyAssembler.swift<br>.../PresentationDependencyAssembler.swift                   | DI 컨테이너에 UserDataRepositoryProtocol 및 MypageViewModel 등록 로직 추가                             |
| .../Error/UserError.swift                                                                              | UserError enum 신설, 사용자 관련 에러 및 한글 설명 제공                                                |
| .../Repository/UserDataRepository.swift                                                                | UserDataRepository 클래스 및 사용자 데이터 로딩 메서드 추가                                            |
| .../Protocol/Repository/UserDataRepositoryProtocol.swift                                               | UserDataRepositoryProtocol 프로토콜 신설, 토큰/닉네임 로딩 메서드 선언                                 |
| .../Images.xcassets/chevron_right_icon.imageset/Contents.json<br>.../setting_icon.imageset/Contents.json | chevron_right_icon, setting_icon 이미지셋 리소스 추가                                                  |
| .../DesignSystem/BitnagilIcon.swift                                                                    | BitnagilIcon에 settingIcon, chevronRightIcon static 프로퍼티 추가                                      |
| .../Extension/NSObject+.swift                                                                          | NSObject에 className 인스턴스/타입 프로퍼티 확장                                                       |
| .../TabBarView.swift                                                                                   | DI에서 MypageViewModel 주입, MypageView 파라미터화 및 기존 MypageView 클래스 정의 제거                 |
| .../MyPage/View/Component/MypageTableViewCell.swift                                                    | MypageTableViewCell 커스텀 셀 클래스 추가, 타이틀/체브론 이미지 포함                                   |
| .../MyPage/View/MypageView.swift                                                                       | MypageView 뷰 컨트롤러 신설, UI 구성 및 뷰모델 바인딩, 테이블뷰/이벤트 처리 구현                      |
| .../MyPage/ViewModel/MypageViewModel.swift                                                             | MypageViewModel 클래스 신설, 메뉴 선택 처리 및 닉네임/외부URL 퍼블리셔 제공                            |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User as 사용자
    participant MypageView as MypageView
    participant MypageViewModel as MypageViewModel
    participant UserDataRepo as UserDataRepository

    User->>MypageView: 메뉴 선택(예: FAQ)
    MypageView->>MypageViewModel: action(input: .didSelectMenu(menu))
    MypageViewModel->>MypageViewModel: handleMenuSelection(menu)
    alt FAQ/Notice 선택
        MypageViewModel->>MypageView: externalURLPublisher로 URL 발행
        MypageView->>User: 외부 브라우저로 URL 오픈
    else resetGoal 선택
        MypageView->>DIContainer: OnboardingViewModel resolve
        MypageView->>User: 온보딩 화면으로 이동
    end

    Note over MypageView: 최초 진입 시
    MypageView->>MypageViewModel: 닉네임 요청
    MypageViewModel->>UserDataRepo: loadNickname()
    UserDataRepo-->>MypageViewModel: 닉네임 반환/에러
    MypageViewModel->>MypageView: nickNamePublisher로 닉네임 발행
    MypageView->>User: 닉네임 표시

Estimated code review effort

1 (<30 minutes)

Poem

🥕
마이페이지 토끼가 깡총깡총,
새 아이콘과 셀, 닉네임도 뿅!
저장소에서 토큰과 이름을 찾아,
뷰모델과 뷰가 함께 춤춰요.
메뉴를 누르면 외부로 점프,
오늘도 변화는 당근처럼 달콤! 🐇✨


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings
<details>
<summary>🧪 Generate unit tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `feat/mypage`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=YAPP-Github/Bitnagil-iOS&utm_content=22):

- 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](https://www.coderabbit.ai/contact-us/support) 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](https://docs.coderabbit.ai/finishing-touches/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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (11)
Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (2)

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

titleLableLeadingSpacing에서 titleLabelLeadingSpacing으로 수정해주세요.

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

53-53: Layout 상수명 오타로 인한 불일치

위에서 수정된 Layout 상수명과 일치시켜주세요.

-            make.leading.equalToSuperview().offset(Layout.titleLableLeadingSpacing)
+            make.leading.equalToSuperview().offset(Layout.titleLabelLeadingSpacing)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)

20-27: TODO 주석에 대한 후속 조치가 필요합니다.

정적 분석 도구에서도 지적했듯이 accessToken fetch 로직에 대한 TODO가 남아있습니다. 팀 회의를 통해 결정된 후 이 주석을 제거하거나 구현을 업데이트해주세요.

현재 구현 자체는 올바르게 작동하지만, 향후 변경될 가능성이 있는 부분이라는 점을 팀원들과 공유하는 것이 좋겠습니다.

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

44-46: 불필요한 메서드 오버라이드 제거 필요

BaseViewController의 viewDidLoad()에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, super.viewDidLoad() 호출만으로 충분합니다. 현재 오버라이드는 추가 로직이 없어 불필요합니다.

-    override func viewDidLoad() {
-        super.viewDidLoad()
-    }

60-60: 임시 배경색 처리 개선 제안

프로필 이미지뷰의 배경색이 임시로 설정되어 있습니다. 실제 프로필 이미지 로딩 로직이나 기본 플레이스홀더 이미지 처리를 고려해보세요.


147-158: 테이블뷰 셀 구성 로직 개선

guard 문에서 실패 시 빈 셀을 반환하는 대신, 적절한 에러 처리나 로깅을 추가하는 것이 좋습니다.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard
            let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
-        else { return .init() }
+        else { 
+            BitnagilLogger.log(logType: .error, message: "Failed to dequeue MypageTableViewCell")
+            return UITableViewCell()
+        }

        let title = MypageViewModel.MypageMenu
            .allCases[indexPath.row]
            .rawValue
        cell.configure(title: title)

        return cell
    }

44-46: 불필요한 viewDidLoad 오버라이드 제거를 고려해보세요

Static analysis에서 지적한 대로, 이 메서드는 super.viewDidLoad()만 호출하고 있어 불필요합니다. Retrieved learning에 따르면 BaseViewController의 viewDidLoad()에서 이미 필요한 메서드들(configureAttribute, configureLayout, bind)을 호출하므로 이 오버라이드는 제거할 수 있습니다.

다음과 같이 메서드를 제거할 수 있습니다:

-    override func viewDidLoad() {
-        super.viewDidLoad()
-    }

60-60: 임시 배경색에 대한 TODO 주석 추가를 고려해보세요

프로필 이미지뷰의 배경색이 임시로 설정되어 있다는 주석이 있습니다. 향후 실제 프로필 이미지 로직이 구현될 때 놓치지 않도록 TODO 주석을 추가하는 것을 권장합니다.

-        profileImageView.backgroundColor = BitnagilColor.gray40 // 임시
+        profileImageView.backgroundColor = BitnagilColor.gray40 // TODO: 실제 프로필 이미지 로직 구현 필요
Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (3)

58-59: resetGoal 메뉴의 처리 로직 명확화 필요

현재 resetGoal 메뉴는 빈 break문으로 처리되어 있습니다. View에서 별도로 처리한다는 의도인지 명확하게 주석으로 표시하거나, 일관성을 위해 ViewModel에서 처리하는 것을 고려해보세요.

        case .resetGoal:
-            break
+            // View에서 직접 네비게이션 처리
+            break

58-59: resetGoal 케이스 처리 로직 명확화가 필요합니다

현재 resetGoal 케이스는 break만 하고 있어서 실제로는 아무것도 하지 않습니다. 이 케이스는 뷰에서 직접 처리하고 있으니, 주석으로 이를 명확히 해주거나 다른 방식으로 처리하는 것을 고려해보세요.

        case .resetGoal:
-            break
+            break // 뷰에서 직접 온보딩으로 네비게이션 처리

60-67: 임시 URL을 상수로 관리하는 것을 권장합니다

하드코딩된 임시 URL들을 별도 상수로 관리하거나, 향후 실제 API 엔드포인트로 교체할 때를 대비해 설정 가능한 구조로 만드는 것을 고려해보세요.

클래스 상단에 URL 상수들을 추가할 수 있습니다:

final class MypageViewModel: ViewModel {
+    private enum ExternalURL {
+        static let notice = "https://www.google.com" // TODO: 실제 공지사항 URL로 변경
+        static let faq = "https://www.naver.com" // TODO: 실제 FAQ URL로 변경
+    }
+
    enum Input {
        case didSelectMenu(menu: MypageMenu)
    }

그리고 handleMenuSelection에서:

        case .notice: // 임시 url
-            if let url = URL(string: "https://www.google.com") {
+            if let url = URL(string: ExternalURL.notice) {
                externalURLPublisher.send(url)
            }
        case .faq: // 임시 url
-            if let url = URL(string: "https://www.naver.com") {
+            if let url = URL(string: ExternalURL.faq) {
                externalURLPublisher.send(url)
            }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 211de3a and d7047ca.

⛔ Files ignored due to path filters (6)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_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 (13)
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Error/UserError.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/UserDataRepository.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Extension/NSObject+.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Common/TabBarView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
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 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Projects/Presentation/Sources/Common/TabBarView.swift (1)
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 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (2)
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Projects/Presentation/Sources/MyPage/View/MypageView.swift (3)
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 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
🧬 Code Graph Analysis (5)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (2)
  • register (14-16)
  • resolve (18-25)
Projects/Presentation/Sources/Common/TabBarView.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (2)
  • loadAccessToken (21-27)
  • loadNickname (29-35)
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • register (14-16)
Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (1)
Projects/Shared/Sources/Logger/Logger.swift (1)
  • log (13-22)
🪛 SwiftLint (0.57.0)
Projects/DataSource/Sources/Repository/UserDataRepository.swift

[Warning] 20-20: TODOs should be resolved (- accessToken fetch 로직 상의 후 결정)

(todo)

Projects/Presentation/Sources/MyPage/View/MypageView.swift

[Warning] 44-44: Remove overridden functions that don't do anything except call their super

(unneeded_override)

🔇 Additional comments (22)
Projects/Presentation/Sources/Common/Extension/NSObject+.swift (2)

10-18: 간결하고 재사용성 높은 유틸리티 👍
인스턴스/타입 양쪽에서 모두 사용할 수 있게 정의한 점이 유용합니다. 추가 의견 없이 승인드립니다.


10-18: 저장소 내 NSObject 확장 className 중복 정의 없음 확인

  • 확인 파일: Projects/Presentation/Sources/Common/Extension/NSObject+.swift
    • var className: String { … } (11행)
    • class var className: String { … } (15행)
  • 다른 파일에서 동일 시그니처를 찾을 수 없음

서드파티/사내 모듈(RxSwift, UIKit 등)에 동일 확장 정의가 있는지 별도 검토 부탁드립니다.

Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json (1)

1-23: 표준 애셋 카탈로그 구조로 올바르게 구현됨

Xcode의 표준 이미지 애셋 카탈로그 구조를 정확히 따르고 있으며, 1x, 2x, 3x 스케일 변형을 모두 포함하고 있습니다.

Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json (1)

1-23: 표준 애셋 카탈로그 구조로 올바르게 구현됨

Xcode의 표준 이미지 애셋 카탈로그 구조를 정확히 따르고 있으며, 모든 스케일 변형을 포함하고 있습니다.

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

32-34: 새로운 아이콘이 일관된 패턴으로 추가됨

기존 아이콘들과 동일한 번들 해상도 방식과 명명 규칙을 따라 구현되어 있어 일관성이 유지되고 있습니다.

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

39-44: ✅ UserDataRepositoryProtocol DI 등록 완료

DataSource 레이어의 DataSourceDependencyAssembler.swift에서 UserDataRepositoryProtocol이 올바르게 등록되어 있어, Presentation 레이어의 MypageViewModel 의존성 해결에 문제가 없습니다. 추가 확인이나 조치가 필요 없습니다.

  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift
    DIContainer.shared.register(type: UserDataRepositoryProtocol.self) { _ in UserDataRepository(...) }
Projects/Presentation/Sources/Common/TabBarView.swift (2)

41-44: 의존성 주입이 올바르게 구현됨

MypageViewModel의 의존성 해상도가 프로젝트의 일관된 패턴을 따르고 있으며, 오류 처리도 적절합니다.


49-49: ViewModel 주입으로 MVVM 아키텍처 적용

MypageView가 ViewModel을 생성자를 통해 주입받도록 변경되어 적절한 의존성 주입 패턴을 따르고 있습니다.

Projects/DataSource/Sources/Common/Error/UserError.swift (1)

8-24: 에러 처리 구현이 잘 되어 있습니다.

ErrorCustomStringConvertible 프로토콜을 적절히 구현하고, 각 에러 케이스에 대한 명확한 한국어 메시지를 제공합니다. unknown 케이스에서 원본 에러의 localizedDescription을 포함하는 것도 디버깅에 도움이 될 것입니다.

Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)

41-43: 의존성 주입 등록이 올바르게 구현되었습니다.

기존의 다른 Repository 등록 패턴과 일관성 있게 구현되었으며, 필요한 storage 의존성들을 올바르게 주입하고 있습니다.

Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1)

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

사용자 데이터 로딩에 필요한 메서드들이 잘 정의되어 있으며, 한국어 문서화도 충실합니다. throws를 사용하여 실패 케이스를 적절히 처리할 수 있도록 설계된 점이 좋습니다.

Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (1)

11-57: 테이블뷰 셀 구현이 잘 되어 있습니다.

SnapKit을 활용한 Auto Layout 설정과 Layout enum을 통한 상수 관리, 디자인 시스템 활용 등이 프로젝트의 기존 패턴과 일관성 있게 구현되었습니다.

Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)

11-36: Repository 구현이 프로토콜 명세를 잘 따르고 있습니다.

적절한 storage 추상화를 사용하고, 실패 시 명확한 에러를 던지도록 구현되어 있습니다. 의존성 주입을 통한 초기화도 깔끔합니다.

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

13-25: 레이아웃 상수 구성이 잘 되어있음

Layout enum을 통한 상수 관리가 깔끔하게 구성되어 있고, 코드 가독성과 유지보수성을 향상시킵니다.


83-116: SnapKit을 활용한 Auto Layout 구성이 적절함

제약 조건 설정이 명확하고 일관성 있게 구성되어 있습니다. safeArea를 적절히 활용하여 다양한 화면 크기에 대응할 수 있도록 구현되었습니다.


12-25: Layout 상수 관리가 잘 구조화되어 있습니다

private enum을 사용하여 레이아웃 상수들을 체계적으로 관리하고 있어 유지보수성이 좋습니다. 특히 profileImageViewCornerRadiusprofileImageViewSize의 절반으로 계산되는 것도 일관성 있게 처리되어 있습니다.


35-42: 초기화 메서드 구현이 적절합니다

DI를 통한 ViewModel 주입과 Combine의 cancellables 초기화가 올바르게 구현되어 있습니다. Storyboard 초기화를 막는 fatalError 처리도 적절합니다.

Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (5)

41-46: 초기화 시 에러 처리가 적절함

사용자 데이터 로딩 실패 시 로그를 남기면서도 앱이 계속 동작하도록 구현된 것이 좋습니다. 닉네임 로딩 실패 시에도 UI가 정상적으로 표시됩니다.


23-27: MypageMenu enum 구성이 깔끔함

String 원시값을 가진 CaseIterable enum으로 메뉴 항목을 관리하는 방식이 깔끔하고, UI에서 사용하기에 적합합니다.


37-39: Output 구조체를 통한 퍼블리셔 관리가 적절함

AnyPublisher를 사용하여 구현 세부사항을 숨기고, Output 구조체로 퍼블리셔들을 그룹핑한 것이 MVVM 패턴에 잘 맞습니다.


13-27: Input/Output 패턴과 메뉴 구조가 잘 설계되어 있습니다

  • Input/Output 패턴으로 뷰모델의 인터페이스가 명확하게 정의되어 있습니다
  • MypageMenu enum을 CaseIterable로 구현하여 테이블뷰에서 활용하기 좋게 구조화되어 있습니다
  • 한국어로 된 rawValue가 UI에 직접 사용될 수 있어 편리합니다

30-31: Subject 타입 선택이 적절합니다

  • 닉네임은 CurrentValueSubject로 초기값과 최신 상태를 유지할 수 있도록 했고
  • 외부 URL은 PassthroughSubject로 일회성 이벤트를 처리하도록 한 것이 적절한 선택입니다

Comment on lines +126 to +113
viewModel.output.externalURLPublisher
.sink { url in
UIApplication.shared.open(url)
}
.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.

🛠️ Refactor suggestion

외부 URL 열기 시 에러 처리 추가 필요

UIApplication.shared.open(url)이 실패할 가능성을 고려하여 에러 처리를 추가하는 것이 좋습니다.

        viewModel.output.externalURLPublisher
            .sink { url in
-                UIApplication.shared.open(url)
+                UIApplication.shared.open(url) { success in
+                    if !success {
+                        BitnagilLogger.log(logType: .error, message: "Failed to open URL: \(url)")
+                    }
+                }
            }
            .store(in: &cancellables)
📝 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.externalURLPublisher
.sink { url in
UIApplication.shared.open(url)
}
.store(in: &cancellables)
viewModel.output.externalURLPublisher
.sink { url in
- UIApplication.shared.open(url)
+ UIApplication.shared.open(url) { success in
+ if !success {
+ BitnagilLogger.log(logType: .error, message: "Failed to open URL: \(url)")
+ }
+ }
}
.store(in: &cancellables)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/View/MypageView.swift around lines 126
to 130, the call to UIApplication.shared.open(url) does not handle potential
failures. Modify the code to use the version of
open(_:options:completionHandler:) that provides a completion handler, and add
error handling logic inside the completion handler to properly manage cases
where opening the URL fails.

🛠️ Refactor suggestion

외부 URL 열기 시 에러 처리 추가를 권장합니다

UIApplication.shared.open(url)이 실패할 수 있는 경우가 있으므로 에러 처리를 추가하는 것이 좋겠습니다.

-        viewModel.output.externalURLPublisher
-            .sink { url in
-                UIApplication.shared.open(url)
-            }
-            .store(in: &cancellables)
+        viewModel.output.externalURLPublisher
+            .sink { url in
+                UIApplication.shared.open(url) { success in
+                    if !success {
+                        BitnagilLogger.log(logType: .error, message: "Failed to open URL: \(url)")
+                    }
+                }
+            }
+            .store(in: &cancellables)
📝 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.externalURLPublisher
.sink { url in
UIApplication.shared.open(url)
}
.store(in: &cancellables)
viewModel.output.externalURLPublisher
.sink { url in
UIApplication.shared.open(url) { success in
if !success {
BitnagilLogger.log(logType: .error, message: "Failed to open URL: \(url)")
}
}
}
.store(in: &cancellables)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/View/MypageView.swift around lines 126
to 130, the call to UIApplication.shared.open(url) does not handle potential
failures. Modify the code to use the version of
open(_:options:completionHandler:) that provides a completion handler, and add
error handling logic inside the completion handler to manage cases where opening
the URL fails.

Comment on lines +148 to +150
guard
let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
else { return .init() }

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

테이블뷰 셀 dequeue 실패 시 더 안전한 처리를 권장합니다

현재 빈 UITableViewCell을 반환하고 있지만, 이는 예상치 못한 UI 동작을 일으킬 수 있습니다. 더 명확한 에러 처리나 적절한 fallback 셀을 제공하는 것이 좋겠습니다.

        guard
            let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
-        else { return .init() }
+        else { 
+            BitnagilLogger.log(logType: .error, message: "Failed to dequeue MypageTableViewCell")
+            return UITableViewCell()
+        }
📝 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
guard
let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
else { return .init() }
guard
let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
else {
BitnagilLogger.log(logType: .error, message: "Failed to dequeue MypageTableViewCell")
return UITableViewCell()
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/View/MypageView.swift around lines 148
to 150, the dequeueReusableCell call returns an empty UITableViewCell on
failure, which can cause unexpected UI issues. Instead, implement clearer error
handling by either logging the failure, asserting, or providing a fallback cell
that matches the expected type to ensure UI consistency and easier debugging.

Comment on lines +170 to +159
guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
}

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

fatalError 사용에 대한 검토 필요

의존성이 등록되지 않았을 때 앱이 크래시되는 것보다는, 사용자에게 적절한 에러 메시지를 보여주거나 이전 화면으로 돌아가는 것이 더 나은 UX를 제공할 수 있습니다.

        guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
-            fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
+            BitnagilLogger.log(logType: .error, message: "onboardingViewModel 의존성이 등록되지 않았습니다.")
+            // TODO: 사용자에게 에러 알림 표시
+            return
        }
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/View/MypageView.swift around lines 170
to 172, replace the fatalError call used when onboardingViewModel dependency is
not resolved with a more user-friendly error handling approach. Instead of
crashing, implement logic to show an appropriate error message to the user or
navigate back to the previous screen to improve UX when the dependency is
missing.

🛠️ Refactor suggestion

의존성 해결 실패 시 더 우아한 에러 처리를 고려해보세요

fatalError는 앱을 강제 종료시키므로 프로덕션 환경에서는 위험할 수 있습니다. 대안적인 에러 처리나 fallback 로직을 고려해보시기 바랍니다.

        guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
-            fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
+            BitnagilLogger.log(logType: .error, message: "onboardingViewModel 의존성이 등록되지 않았습니다.")
+            // 적절한 에러 처리나 사용자 알림 로직 추가
+            return
        }
📝 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
guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
}
guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
BitnagilLogger.log(logType: .error, message: "onboardingViewModel 의존성이 등록되지 않았습니다.")
// 적절한 에러 처리나 사용자 알림 로직 추가
return
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/View/MypageView.swift around lines 170
to 172, replace the fatalError call used when onboardingViewModel dependency
resolution fails with a more graceful error handling approach. Implement
alternative error handling such as returning a default view model, showing an
error message to the user, or safely recovering without crashing the app to
avoid forced termination in production.

Comment on lines +41 to +46
do {
let nickname = try userDataRepository.loadNickname()
nicknamePublisher.send(nickname)
} catch {
BitnagilLogger.log(logType: .debug, message: "\(error.localizedDescription)")
}

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

초기화 시 에러 처리를 개선할 수 있습니다

현재는 닉네임 로드 실패 시 로그만 남기고 빈 문자열을 유지하고 있습니다. 사용자 경험을 위해 기본값이나 대체 텍스트를 제공하는 것을 고려해보세요.

        do {
            let nickname = try userDataRepository.loadNickname()
            nicknamePublisher.send(nickname)
        } catch {
            BitnagilLogger.log(logType: .debug, message: "\(error.localizedDescription)")
+            nicknamePublisher.send("사용자") // 기본 닉네임 제공
        }
📝 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
do {
let nickname = try userDataRepository.loadNickname()
nicknamePublisher.send(nickname)
} catch {
BitnagilLogger.log(logType: .debug, message: "\(error.localizedDescription)")
}
do {
let nickname = try userDataRepository.loadNickname()
nicknamePublisher.send(nickname)
} catch {
BitnagilLogger.log(logType: .debug, message: "\(error.localizedDescription)")
nicknamePublisher.send("사용자") // 기본 닉네임 제공
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift around
lines 41 to 46, the error handling when loading the nickname only logs the error
but leaves the nickname empty. Improve this by providing a default or fallback
nickname value in the catch block and sending it via nicknamePublisher to ensure
a better user experience.

Comment on lines +60 to +67
case .notice: // 임시 url
if let url = URL(string: "https://www.google.com") {
externalURLPublisher.send(url)
}
case .faq: // 임시 url
if let url = URL(string: "https://www.naver.com") {
externalURLPublisher.send(url)
}

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

하드코딩된 URL을 설정으로 관리 제안

임시 URL들이 하드코딩되어 있습니다. 향후 유지보수를 위해 설정 파일이나 Constants로 관리하는 것을 고려해보세요.

        case .notice: // 임시 url
-            if let url = URL(string: "https://www.google.com") {
+            if let url = URL(string: AppConfig.noticeURL) {
                externalURLPublisher.send(url)
            }
        case .faq: // 임시 url
-            if let url = URL(string: "https://www.naver.com") {
+            if let url = URL(string: AppConfig.faqURL) {
                externalURLPublisher.send(url)
            }
📝 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
case .notice: // 임시 url
if let url = URL(string: "https://www.google.com") {
externalURLPublisher.send(url)
}
case .faq: // 임시 url
if let url = URL(string: "https://www.naver.com") {
externalURLPublisher.send(url)
}
case .notice: // 임시 url
if let url = URL(string: AppConfig.noticeURL) {
externalURLPublisher.send(url)
}
case .faq: // 임시 url
if let url = URL(string: AppConfig.faqURL) {
externalURLPublisher.send(url)
}
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift around
lines 60 to 67, the URLs for .notice and .faq cases are hardcoded. To improve
maintainability, move these URLs to a configuration file or a Constants struct,
then reference those constants here instead of hardcoding the strings directly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (3)

126-130: 외부 URL 열기 시 에러 처리 추가를 권장합니다

UIApplication.shared.open(url)이 실패할 수 있는 경우가 있으므로 에러 처리를 추가하는 것이 좋겠습니다.

-        viewModel.output.externalURLPublisher
-            .sink { url in
-                UIApplication.shared.open(url)
-            }
-            .store(in: &cancellables)
+        viewModel.output.externalURLPublisher
+            .sink { url in
+                UIApplication.shared.open(url) { success in
+                    if !success {
+                        BitnagilLogger.log(logType: .error, message: "Failed to open URL: \(url)")
+                    }
+                }
+            }
+            .store(in: &cancellables)

148-150: 테이블뷰 셀 dequeue 실패 시 더 안전한 처리를 권장합니다

현재 빈 UITableViewCell을 반환하고 있지만, 이는 예상치 못한 UI 동작을 일으킬 수 있습니다. 더 명확한 에러 처리나 적절한 fallback 셀을 제공하는 것이 좋겠습니다.

        guard
            let cell = tableView.dequeueReusableCell(withIdentifier: MypageTableViewCell.className) as? MypageTableViewCell
-        else { return .init() }
+        else { 
+            BitnagilLogger.log(logType: .error, message: "Failed to dequeue MypageTableViewCell")
+            return UITableViewCell()
+        }

170-172: 의존성 해결 실패 시 더 우아한 에러 처리를 고려해보세요

fatalError는 앱을 강제 종료시키므로 프로덕션 환경에서는 위험할 수 있습니다. 대안적인 에러 처리나 fallback 로직을 고려해보시기 바랍니다.

        guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
-            fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
+            BitnagilLogger.log(logType: .error, message: "onboardingViewModel 의존성이 등록되지 않았습니다.")
+            // 적절한 에러 처리나 사용자 알림 로직 추가
+            return
        }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d7047ca and 0d00b2f.

⛔ Files ignored due to path filters (6)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_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 (13)
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Error/UserError.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/UserDataRepository.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Extension/NSObject+.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Common/TabBarView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json
  • Projects/Presentation/Sources/Common/Extension/NSObject+.swift
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
  • Projects/Presentation/Sources/Common/TabBarView.swift
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift
  • Projects/DataSource/Sources/Common/Error/UserError.swift
  • Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift
  • Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift
🧰 Additional context used
🧠 Learnings (1)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (5)
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 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
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로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/MyPage/View/MypageView.swift

[Warning] 44-44: Remove overridden functions that don't do anything except call their super

(unneeded_override)

Projects/DataSource/Sources/Repository/UserDataRepository.swift

[Warning] 20-20: TODOs should be resolved (- accessToken fetch 로직 상의 후 결정)

(todo)

🔇 Additional comments (4)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (2)

11-18: 의존성 주입을 통한 깔끔한 구조

저장소 프로토콜들을 의존성 주입으로 받아서 테스트 가능하고 유연한 구조를 만들어주셨네요. 잘 구현되었습니다.


29-36: 일관된 에러 처리 패턴

loadAccessToken과 동일한 패턴으로 구현되어 일관성이 좋고, 적절한 커스텀 에러를 사용해서 에러 핸들링이 잘 되어있습니다.

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

48-72: 체계적인 UI 설정 구현

디자인 시스템을 잘 활용하고 있고, UI 컴포넌트들이 체계적으로 설정되어 있습니다. 임시로 설정된 프로필 이미지 배경색도 적절히 주석 처리되어 있네요.


74-117: 깔끔한 레이아웃 구성

SnapKit을 활용한 Auto Layout 설정이 체계적이고, Layout enum의 상수들을 잘 활용해서 가독성과 유지보수성이 좋습니다.

Comment on lines +20 to +27
// TODO: - accessToken fetch 로직 상의 후 결정
func loadAccessToken() throws -> String {
guard
let token = keychainStorage.load(forKey: TokenType.accessToken.rawValue)
else { throw UserError.accessTokenLoadFailed }

return token
}

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

❓ Verification inconclusive

TODO 주석 해결 필요

SwiftLint에서 TODO 주석에 대한 경고가 발생하고 있습니다. 현재 구현이 적절하다면 TODO를 제거하거나, 실제로 논의가 필요하다면 구체적인 계획을 세우시는 것이 좋겠습니다.

- // TODO: - accessToken fetch 로직 상의 후 결정
+ // 키체인에서 액세스 토큰을 로드

또는 실제로 로직 변경이 필요하다면 이슈를 생성해서 추적하는 것을 고려해보세요.


TODO 주석 정리 필요

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다.
현재 구현이 확정된 상태라면 해당 주석을 삭제하거나, 실제 의도를 명확히 설명하는 주석으로 변경해주세요.
아직 로직 변경이 논의 중이라면 별도의 이슈를 생성해 추적하는 방안을 권장드립니다.

  • 대상 파일: Projects/DataSource/Sources/Repository/UserDataRepository.swift (20–27행)
  • 예시 변경안:
- // TODO: - accessToken fetch 로직 상의 후 결정
+ // 키체인에서 액세스 토큰을 로드
📝 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
// TODO: - accessToken fetch 로직 상의 후 결정
func loadAccessToken() throws -> String {
guard
let token = keychainStorage.load(forKey: TokenType.accessToken.rawValue)
else { throw UserError.accessTokenLoadFailed }
return token
}
// 키체인에서 액세스 토큰을 로드
func loadAccessToken() throws -> String {
guard
let token = keychainStorage.load(forKey: TokenType.accessToken.rawValue)
else { throw UserError.accessTokenLoadFailed }
return token
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 20-20: TODOs should be resolved (- accessToken fetch 로직 상의 후 결정)

(todo)

🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/UserDataRepository.swift around lines
20 to 27, the TODO comment causes SwiftLint warnings. If the accessToken fetch
logic is finalized, remove the TODO comment entirely or replace it with a clear
explanatory comment. If the logic is still under discussion, create a separate
issue to track it and remove the TODO from the code to avoid lint warnings.

Comment thread Projects/Presentation/Sources/MyPage/View/MypageView.swift Outdated

@choijungp choijungp left a comment

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.

띵동 매우 감사 ... 웰끔입니다 ......... 🥺

Comment thread Projects/DataSource/Sources/Repository/UserDataRepository.swift Outdated
Comment thread Projects/DataSource/Sources/Repository/UserDataRepository.swift Outdated
Comment on lines +11 to +17
var className: String {
return String(describing: type(of: self))
}

class var className: String {
return String(describing: self)
}

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.

요 className 너무너무 좋은 것 같아요 !!
테이블 뷰 셀 정의할 때 하드코딩 안하고 이렇게 하는 고지능적 개발 !!! 감사합니다.

다만 궁금한게 var classNameclass var className가 어떻게 다른지 알 수 있을까요 ??
둘 다 사용해야만 className을 빼올 수 있는건가용 ??

제가 feat/myPage 브랜치에서 다음 부분을 주석처리해도 마이페이지가 잘 작동하는 것 같아 궁금해서 질문 남겨봅니다 !!!

 var className: String {
    return String(describing: type(of: self))
}

추가적으로 이 extension이 public인 이유도 궁금합니다 !!

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.

우선 public일 필요는 전혀 없습니다!! 추가의 이뉴는 아시다시피 UIView가 NSObject의 서브 클래스이기 때문에 UIView 서브 클래스들의 클래스 면 사용을 편하게 하기 위해서 인데요!! 때문에 Presentation 모듈 밖에서 사용할 일이 없으니 떼는게 좋을거 같아요!! 진짜 꼼꼼하게 봐주셨군요 감사합니다!!!

두 변수의 차이점은 인스턴스 변수냐, 클래스 변수냐의 차이입니다.

  • var class 의 경우, 인스턴스가 존재할 때 해당 인스턴스의 클래스 이름을 불러올 필요가 있을때 사용하시면 되구
  • class var의 경우, 타입 자체의 이름을 String으로 불러올 때 사용하시면 좋을 것 같습니다!

아직은 두 번째 class var 만 사용하고 있지만, 정말 추후 혹시나 인스턴스의 클래스 이름을 String으로 빼오는 경우도 고려해서 한 번에 추가해 두었습니다!

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.

헐 ~ 바로 이해됩니다 .... 띵천재 띵고수 😎
많이 배워갑니다 ~~

Comment thread Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift Outdated
Comment thread Projects/Presentation/Sources/MyPage/View/MypageView.swift Outdated
Comment thread Projects/Presentation/Sources/MyPage/View/MypageView.swift Outdated
Comment thread Projects/DataSource/Sources/Common/Error/UserError.swift Outdated
Comment on lines +170 to +158
guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")

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.

요 부분에 대해서 혼자 진짜 정말정말 많이 고민했는데 아직 잘 모르겠는 부분이 있습니다 ...........

현재 OnboardingView를 재사용하고 있잖아요 ..
그러면서 필요한 OnboardingViewModel을 DIContainer에서 뽑아 쓰게 됩니다 !!


DIContainer의 구현 세부사항을 확인해보면 딕셔너리로 value에 @escaping 클로저를 저장하여
의존성이 정말 필요할 때 해당 인스턴스를 반환해서 사용하고 있습니다 ...

// DIContainer 일부 .. 
private var storage: [String: (DIContainer) -> Any] = [:]

public func resolve<T>(type: T.Type) -> T? {
    guard let instance = storage["\(type)"]?(self) as? T else {
        BitnagilLogger.log(logType: .error, message: "\(type) Resolve Fail: 등록되지 않은 의존성입니다.")
        return nil
    }

    return instance
}

그럼 이미 OnboardingViewModel은 OnboaringView를 처음 생성(ex. 로그인 후 온보딩 진입)할 때 이미 인스턴스가 생성되어 사용되게 되고,
OnboardingView를 재사용할 때(ex. 마이페이지에서 목표 재설정) DIContianer에서 OnboardingViewModel을 꺼내 쓰게 된다면 OnboaringViewModel의 새로운 인스턴스를 생성해서 사용하게 되는 구조가 맞을까요 ??

이건 불가피한 상황인지 .. 어떻게 생각하시나요 ....

이 코멘트로 .... 제 고민이 잘 전달될지 모루겠네요 ㅠㅠ

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.

생성해야하는 의존성이 굉장히 무겁다면 하나의 인스턴스를 DIContainer에 등록해두고, 같은 인스턴스를 재활용해서 사용하는 방법을 고려해보겠지만, 현재 구조도 괜찮지 않을까요?
항상 새로운 인스턴스를 반환하기 때문에 메모리 상에 독립적으로 존재한다는 점도, 단점이 될 수도 있지만, 장점이 될 수도 있다고 생각합니다! 상태를 공유하지 않기 때문에 예상치 못한 동작을 방지할 수도 있으니까요
조이의 의견도 궁금합니다!!

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.

음 뷰모델의 경우는 장점이 될 수 있다고 생각하는데 .. 저희 Repository나 UseCase는 따로 상태를 갖고 있는 녀석들은 아닌데 요 부분에서는 약간 단점이 될 수 있을 것 같다는 고민이 됩니다 !!!

하지만 23일 자료에 넣어버릴깝쇼 ??

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.

좋은 생각입니다!! 일단 빠르게 쳐내는 것이 중요하니까요.. 이 부분에 대해서는 다음주에 길게 이야기 나눠보면 좋을거 같습니다!

taipaise added 5 commits July 21, 2025 21:32
- BitnagilIcon에 이미지 추가
- {class이름}.className을 통해 클래스의 이름을 스트링으로 사용할 수 있도록 구현
- 유저 정보 (닉네임, accessToken)을 가져올 수 있는 repository 구현

- UserDataRepository 의존성 등록
- 마이페이지 화면 구현
- 의존성 등록
- TabBar에서 Myapge 클래스 삭제
@taipaise

Copy link
Copy Markdown
Collaborator Author

@choijungp 1ae7ba7 에서 리뷰 주신 내용 반영했습니다. 감사합니다!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (9)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (3)

20-20: TODO 주석 해결 필요

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다. 현재 구현이 확정된 상태라면 해당 주석을 삭제하거나, 실제 의도를 명확히 설명하는 주석으로 변경해주세요.


20-20: TODO 주석 해결 필요

SwiftLint에서 TODO 주석으로 인한 경고가 계속 발생하고 있습니다. 현재 구현이 확정되었다면 주석을 제거하거나 구체적인 설명으로 변경해주세요.


20-20: TODO 주석 정리가 필요합니다

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다. 현재 구현이 확정되었다면 해당 주석을 제거하거나 명확한 설명으로 변경해주세요.

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

134-137: 셀 dequeue 실패 시 더 안전한 처리 필요

현재 빈 UITableViewCell을 반환하고 있지만, 이는 예상치 못한 UI 동작을 일으킬 수 있습니다. 더 명확한 에러 처리나 적절한 fallback 로직을 고려해보세요.


157-159: 의존성 해결 실패 시 더 우아한 에러 처리 필요

fatalError는 앱을 강제 종료시키므로 프로덕션 환경에서는 위험할 수 있습니다. 로깅 후 적절한 fallback 처리나 사용자 알림을 고려해보세요.


135-137: 테이블뷰 셀 dequeue 실패 시 더 안전한 처리가 필요합니다

현재 빈 UITableViewCell을 반환하고 있어 예상치 못한 UI 동작을 일으킬 수 있습니다. 더 명확한 에러 처리나 적절한 fallback 셀 제공을 고려해주세요.


157-159: 의존성 해결 실패 시 더 우아한 에러 처리를 고려해주세요

fatalError는 앱을 강제 종료시키므로 프로덕션 환경에서는 위험할 수 있습니다. 대안적인 에러 처리나 fallback 로직을 고려해보시기 바랍니다.


135-137: 셀 dequeue 실패 시 더 안전한 처리가 필요합니다

현재 빈 UITableViewCell을 반환하고 있어 예상치 못한 UI 동작을 일으킬 수 있습니다. 로깅이나 적절한 fallback 처리를 고려해주세요.


157-159: 의존성 해결 실패 시 더 우아한 에러 처리를 권장합니다

fatalError 사용은 프로덕션 환경에서 위험할 수 있습니다. 로깅과 함께 더 안전한 에러 처리 방식을 고려해주세요.

🧹 Nitpick comments (3)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (3)

117-117: TODO 주석 정리 권장

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다. 설정 페이지 구현이 확정된 계획이라면 별도 이슈로 추적하고 TODO를 제거하는 것을 권장합니다.


117-117: TODO 주석 해결이 필요합니다

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다. 설정 페이지 연결 계획이 있다면 구체적인 일정을 명시하거나, 당분간 구현 예정이 없다면 주석을 제거해주세요.


117-117: TODO 주석 정리를 권장합니다

SwiftLint에서 TODO 주석으로 인한 경고가 발생하고 있습니다. 설정 페이지 구현이 계획되어 있다면 별도 이슈로 추적하거나, 명확한 설명 주석으로 변경하는 것을 고려해주세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0d00b2f and 1ae7ba7.

⛔ Files ignored due to path filters (6)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@1x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_icon@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/chevron_right_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 (13)
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Error/UserError.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/UserDataRepository.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Extension/NSObject+.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Common/TabBarView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (1 hunks)
  • Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • Projects/Presentation/Resources/Images.xcassets/chevron_right_icon.imageset/Contents.json
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
  • Projects/Presentation/Resources/Images.xcassets/setting_icon.imageset/Contents.json
  • Projects/DataSource/Sources/Common/Error/UserError.swift
  • Projects/Presentation/Sources/Common/Extension/NSObject+.swift
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift
  • Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift
  • Projects/Presentation/Sources/Common/TabBarView.swift
  • Projects/Presentation/Sources/MyPage/View/Component/MypageTableViewCell.swift
  • Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift
🧰 Additional context used
🧠 Learnings (1)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (5)

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

Learnt from: choijungp
PR: #19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.

Learnt from: choijungp
PR: #19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.

Learnt from: choijungp
PR: #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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.

Learnt from: choijungp
PR: #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()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.

🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/MyPage/View/MypageView.swift

[Warning] 117-117: TODOs should be resolved (- 추후 설정 페이지 연결)

(todo)

Projects/DataSource/Sources/Repository/UserDataRepository.swift

[Warning] 20-20: TODOs should be resolved (- accessToken fetch 로직 상의 후 결정)

(todo)

🔇 Additional comments (10)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (2)

11-36: 깔끔한 Repository 구현

의존성 주입 패턴과 프로토콜 추상화를 잘 활용한 구현입니다. 키체인과 UserDefaults를 적절히 분리하여 사용하고, 일관된 에러 처리를 통해 안정성을 확보했습니다.


11-36: 사용자 데이터 접근 레이어 구현이 적절합니다

Repository 패턴을 통한 데이터 접근 로직이 잘 구현되어 있습니다. KeychainStorage와 UserDefaultsStorage를 적절히 활용하고 있으며, 에러 처리도 명확하게 되어 있습니다.

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

108-113: SFSafariViewController 사용으로 UX 개선

이전 리뷰 피드백을 반영하여 UIApplication.shared.open 대신 SFSafariViewController를 사용하도록 개선되었습니다. 사용자가 앱을 벗어나지 않고 외부 콘텐츠를 볼 수 있어 더 나은 사용자 경험을 제공합니다.


13-119: 잘 구성된 MVVM 아키텍처

BaseViewController를 활용한 깔끔한 구조와 Combine을 통한 반응형 바인딩이 잘 구현되어 있습니다. Layout enum을 통한 상수 관리와 명확한 책임 분리가 돋보입니다.


108-113: SFSafariViewController 사용으로 개선되었습니다

이전 리뷰에서 제안된 SFSafariViewController 사용으로 변경되어 사용자가 앱을 벗어나지 않고도 웹 페이지를 볼 수 있게 되었습니다. 좋은 개선입니다.


13-40: 뷰 컨트롤러 구조와 초기화가 잘 구성되었습니다

BaseViewController를 상속받은 구조가 적절하며, Combine을 활용한 cancellables 관리와 의존성 주입을 통한 뷰모델 전달이 잘 구현되어 있습니다.


14-23: Layout 상수 관리가 체계적입니다

Layout enum을 통한 상수 관리가 잘 되어 있어 유지보수성과 가독성이 좋습니다. 특히 profileImageViewCornerRadius를 size의 절반으로 계산한 부분이 적절합니다.


8-8: SafariServices import 추가 - 좋은 개선사항입니다

이전 피드백을 반영하여 SFSafariViewController 사용을 위한 SafariServices import가 추가되었습니다. 앱 내에서 웹페이지를 보여주는 더 나은 사용자 경험을 제공할 수 있게 되었습니다.


44-45: Navigation 구조 개선 - 좋은 변경사항입니다

이전 피드백을 반영하여 커스텀 title label 대신 navigation title을 사용하도록 변경되었습니다. 다른 뷰들과의 일관성이 개선되었고 더 자연스러운 레이아웃을 제공합니다.


108-113: SFSafariViewController 사용으로 개선되었습니다

이전 피드백을 반영하여 UIApplication.shared.open 대신 SFSafariViewController를 사용하도록 변경되었습니다. 사용자가 앱을 벗어나지 않고도 웹 콘텐츠를 볼 수 있어 더 나은 사용자 경험을 제공합니다.

@choijungp choijungp left a comment

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.

띵푸루부 ~~ 👍🏻👍🏻👍🏻 짱 고생하셨습니다 !!!!

@taipaise
taipaise merged commit 21db344 into develop Jul 21, 2025
2 checks passed
@taipaise
taipaise deleted the feat/mypage branch July 27, 2025 13:51
choijungp added a commit that referenced this pull request Aug 8, 2025
* Feat: SnapKit, Then 외부 라이브러리 추가

* Feat: Logger 구현

* Feat: Logger 구현

* Del: 임시 파일 (Shared.swift) 삭제

* Fix: log 메시지 출력 형식 변경

- 파일명, 라인 수, 함수명 default로 출력하도록 수정

* Feat: DIContainer 구현 (#T3-56)

* Update Package.swift

* [Del}  불필요한 이미지 파일 삭제

* Feat: Keychain 및 UserDefaults 기반 Persistence 구현 (#T3-60)

* Feat: Persistence 저장소 리팩토링 (#T3-60)

* Feat: NetworkService 구현 (#T3-57)

* Del: 임시 파일 (Domain.swift) 삭제

* Feat: Endpoint 구현 (#T3-57)

- Endpoint 프로토콜 구현
- Network 레이어에 Endpoint extension 구현
- 편의성을 위한 URLRequest extension 구현

* Feat: 네트워크 로직을 위한 enum 값들 구현 (#T3-57)

- NetworkError, HTTPMethod, AppProperties 구현

* Feat: NetworkService 구현

- NetworkServiceProtocol 추상체 구현
- NetworkService 구현체 구현

* Feat: BaseResponseDTO 구현 (#T3-57)

* Feat: 개발 서버 테스트 구현

- health-check API를 통한 테스트 진행
- 이를 위한 Endpoint, UseCase, Repository 구현
- App 모듈에 의존성 추가 (NetworkService, Persistence)

* Refactor: 피드백 기반 UserDefaultsStore 네이밍 및 불필요 타입 제거 (#T3-60)

* Refactor: KeyValueStorage 네이밍 및 계층 구조 정리 (#T3-60)

* Refactor: UserDefaults 밑 KeychainStorageProtocol (#T3-60)

* Refactor: KeychainStorage 오탈자 수 (#T3-60)

* Fix: UserDefaultsStorageProtocol 구현  타입 수정 (#T3-60)

* Refactor: UserDefaultsStorage 제네릭 타입 대응 및 프로토콜 정합성 개선 (#T3-60)

* Del: Git Cache 및 불필요한 파일 삭제

* Docs: gitignore 정보 추가

* Del: .xcworkspace 파일 삭제

* Feat: Design System 구현 (#T3-64)

* Feat: Gray 색상 Asset 추가 (#T3-64)

* Feat: BitnagilColor Enum 정의 (#T3-64)

* Feat: Pretendard 폰트 파일 추가

* Feat: BitnagilFont Enum 정의 (#T3-64)

- BitnagilFont Enum 정의
- FontName Enum 정의
- UIFont extension 구현

* Chore: Project 파일 수정

- 각 모듈의 infoPlist 설정 제거
- App 단의 info.plist 파일 사용하도록 수정

* Feat: BaseViewController 및 ViewModel protocol 구현 (#T3-65)

* Feat: ViewModel protocol 구현 (#T3-65)

* Feat: BaseViewController 구현 (#T3-56)

* Refactor: Test를 위한 HomeViewController 및 Test 객체들 수정

* Feat: Design System 구현 (#T3-64)

* Feat: Gray 색상 Asset 추가 (#T3-64)

* Feat: BitnagilColor Enum 정의 (#T3-64)

* Feat: Pretendard 폰트 파일 추가

* Feat: BitnagilFont Enum 정의 (#T3-64)

- BitnagilFont Enum 정의
- FontName Enum 정의
- UIFont extension 구현

* Chore: Project 파일 수정

- 각 모듈의 infoPlist 설정 제거
- App 단의 info.plist 파일 사용하도록 수정

* Refactor: BaseViewController의 viewModel 접근 제한자 수정 및 중복 제거

* Feat: ViewModel protocol 구현 (#T3-65)

* Feat: BaseViewController 구현 (#T3-56)

* Refactor: Test를 위한 HomeViewController 및 Test 객체들 수정

* Refactor: BaseViewController의 viewModel 접근 제한자 수정 및 중복 제거

* Refactor: HomeViewController에 디자인 시스템 적용

* Feat: Dependency Assembler 구현 (#T3-63)

* Refactor: DIContainer 로직 수정 (#T3-63)

- 기존 Any 타입으로 인스턴스를 저장하던 의존성을 factory closure 형태로 변경
- resolve 시 클로저 실행을 통해 인스턴스를 생성하도록 개선

* Feat: DependencyAssemblerProtocol 구현 (#T3-63)

* Feat: 각 모듈별 DependencyAssembler 구현

- Network, Persistence, DataSource, Domain, Presentation 모듈의 DependencyAssembler 구현
- previous 의존성을 명시하여 DI 등록 순서를 보장하도록 함

* Feat: App 모듈에 DependencyInjection 함수 구현 (#T3-63)

* Del: DataSoure의 불필요한 Resources 파일 제거

* Refactor: NetworkService 테스트 가능한 구조를 위해 NetworkProviderProtocol 구현 및 채택

* Feat: 빌드 체크 자동화 Github Actions 추가

* Chore: Kakao SDK 설치 및 Tuist 외부 의존성 추가

* Chore: Kakao SDK init 및 필요한 프로젝트 설정 세팅

* Refactor: 네트워크 모듈 세부 구현 수정

- Endpoint, URLRequest 수정
- 기존 parameter만 받던 Endpoint에서 queryParameters, bodyParameters를 구분지어서 받도록 수정
- 확장해서 만든 커스텀 URLRequest 생성자에서 url 뿐만 아니라 queryParamters도 받아 쿼리 파라미터가 있다면 붙여서 url 만들어주도록 수정
- 그에 따른 TestEndpoint 수정

* Feat: 카카오 로그인 기능 구현 (#T3-66)

- Enum, Error 폴더 구조 정리
- AuthEndpoint 구현
- AuthRepository 구현체 및 추상체 구현
- LoginUseCase 구현체 및 추상체 구현

* Feat: LoginView 및 LoginViewModel 구현 (#T3-66)

* Feat: 각 모듈 Assembler에 Login 및 Auth 의존성 추가

* Chore: info.plist 카카오 네이티브 키 설정 값 오타 수정

* Fix: 서버 요청사항에 맞게 Endpoint 수정

- AuthEndpoint 수정
- accessToken, refreshToken 저장 후 로그 찍기

* Feat: Apple 로그인 구현 (#T3-67)

* Refactor: LoginView Layout Enum 상수값 네이밍 수정

* Feat: LoginViewController에 Apple Login 버튼 추가

* Fix: TokenResponse, TokenEntity 값 서버에 맞게 수정

* Refactor: Apple 로그인 토큰 값 및 유저 정보 가져오는 로직 ViewController로 이동 (#T3-67)

- ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding 채택해주는 로직 LoginViewController로 이동 (기존 AuthRepository)

* Feat: LoginViewModel, LoginUseCase에 appleLogin 함수 구현 (#T3-67)

- LoginViewModel에서 apple 로그인 결과에 따라 값을 방출할 수 있도록 구현
- LoginUseCaseProtocol, LoginUseCase appleLogin 함수 정의 및 구현

* Feat: AuthError 및 UserDefaultsKey Enum 값 정의

* Refactor: 카카오 로그인 에러 구체화

* Feat: AuthRepository appleLogin 구현 (#T3-67)

- AuthRepositoryProtocol, AuthRepository appleLogin 정의 및 구현
- 받은 닉네임, 토큰 값을 통해 서버 통신 로직 구현
- UserDefaults에 닉네임 저장 및 불러오는 로직 구현

* Feat: AuthRepository UserDefaults 의존성 추가

* Chore: 프로젝트 세팅에 entitlements 지정

* Chore: 코드래빗 설정 파일 추가

* Refactor: Height 오타 수정

* Feat: 로그아웃, 탈퇴 및 토큰 재발급 로직 구현 (#T3-75)

* Del: health-check 관련 테스트 코드 삭제

* Feat: AuthRepository에 로그아웃 함수 구현 (#T3-75)

- AuthRepositoryProtocol, AuthRepository에 logout 함수 정의 및 구현
- AuthError에 토큰 삭제 에러 추가

* Feat: LogoutUseCase 구현 (#T3-75)

- LogoutUseCase Protocol, LogoutUsecase logout 함수 정의 및 구현
- LoginUseCase 폴더 이동

* Feat: LoginViewController, LoginViewModel에 logout 함수 추가

- 테스트 진행을 위해 LoginViewModel, LoginViewController에 로그아웃 버튼 추가 및 함수 구현

* Feat: LogoutUseCase 의존성 조립

* Refactor: 서버 응답 값에 맞게 BaseResponseDTO 수정

* Feat: 소셜 로그인 탈퇴 구현 (#T3-75)

- AuthEndpoint에 withdraw case 추가
- AuthRepositoryProtocol 및 AuthRepository에 withdraw 함수 정의 및 구현
- WithdrawUseCaseProtocol 및 WithdrawUseCase 정의 및 구현
- Domain, Presentation 의존성 등록 및 조립
- LoginViewModel, LoginViewController에 테스트를 위한 함수 및 버튼 추가

* Feat: 토큰 재발급 함수 reissueToken 구현

- AuthEndpoint에 reissue 케이스 추가
- AuthRepositoryProtocol 및 AuthRepository에 reissueToken 정의 및 구현
- LoginViewModel, LoginViewController에 reissue 테스트 관련 버튼 및 함수 추가

* Feat: 인트로 · 로그인 · 이용 약관 UI 구현 및 로직 정리 (#T3-86)

* Chore: 디자인 시스템 색상 Color Asset 추가 및 BitnagilColor 변수 값 추가

- Figma 디자인 시스템에 정의된 색상 값 Asset 추가
- 그에 따른 BitnagilColor 변수 추가
- Bundle을 해당 모듈에서 찾을 수 있도록 코드 변경

* Chore: 디자인 시스템 폰트 정리

- 기존 Enum으로 정리된 BitnagilFont에서 Struct로 변경하여 weight 값에 따른 폰트를 받을 수 있도록 수정
- FontAttributes, FontStyle, FontWeight 등 폰트 정의에 필요한 enum 및 struct 정의

* Chore: 디자인 시스템 icon 이미지 Asset 추가

- apple, kakao, check icon 이미지 추가
- BitnagilIcon Enum 정의

* Feat: IntroView 구현 (#T3-86)

- PrimaryButton Component 구현
- IntroView 구현

* Feat: LoginView UI 구현 및 로직 수정 (#T3-86)

* Refactor: 로그인 서버 통신 관련 AuthRepository 및 LoginUseCase 수정

* Feat: IntroView UI 구현 (#T3-86)

- IntroView UI 구현
- SceneDelegate 앱 진입점 수정

* Feat: LoginView UI 구현 (#T3-86)

- LoginView UI 구현
- SocialLoginButton Component 구현 (카카오, 애플)

* Feat: 이용 약관 동의 UI 구현 (#T3-86)

- TermAgreementItemView 컴포넌트 구현
- TermAgreementView UI 구현
- LoginViewModel과 로직 구현

* Feat: 이용 약관 동의 로직 구현 (#T3-86)

- 기존 TokenResponseDTO에서 LoginResponseDTO로 수정 (서버 응답에 User의 role이 추가되었음)
- AuthEndpoint에 agreements 케이스 추가
- AuthRepositoryProtocol 및 AuthRepository에 submitAgreements 정의 및 구현
- LoginUseCaseProtocol 및 LoginUseCase에 submitAgreements 정의 및 구현
- LoginViewModel에 submitAgreement 로직 구현

* Feat: UIViewController extension에 공통 backbutton을 사용할 수 있도록 커스텀

* Fix: IntroView, TermsAgreementView에 Label 색상 적용

* Refactor: label 오타 수정

* Feat: 온보딩 UI 구현 (#T3-68)

* Feat: 온보딩 선택지 버튼 Components UI 구현 (#T3-68)

* Feat: GradientProgressBar UI 구현

- Gradient 색상 Asset 추가
- GradientProgressBar를 포함한 navigationItem 구현

* Refactor: 온보딩 선택지 UI 수정 (#T3-68)

- OnboardingChoiceProtocol 구현
- 온보딩 선택 화면에서 해당 프로토콜을 채택한 타입이라면 OnboardingChoiceButton을 그릴 수 있도록 수정

* Feat: 온보딩에서 사용될 enum 타입 정의 (#T3-68)

- OnboardingType (온보딩 유형 값)
- OnboardingChoiceType (온보딩 선택지 값)

* Feat: OnboardingView 구현 (#T3-68)

- 온보딩 선택 화면에서 재사용될 OnboardingView 구현

* Feat: OnboardingResultView 구현 (#T3-68)

- 온보딩 결과 화면 UI 구현
- 특정 텍스트만 semiBold로 보여지기 위해 NSAttributedString extension 구현

* Feat: 온보딩 추천 루틴 UI 구현 (#T3-68)

* Feat: OnboardingViewModel 구현 (#T3-68)

* Feat: OnboardingViewModel 의존성 조립 및 이용 약관 화면에서 온보딩 화면으로 넘어가는 로직 구현 (#T3-68)

* Feat: 온보딩 서버 통신 구현 (#T3-102)

* Style: 통일성을 위한 폴더명 변경

* Refactor: BaseViewController 및 ViewModel에 public 접근제어자 삭제

* Refactor: NetworkService 리팩토링

- BaseResponseDTO DataSource에서 Network 모듈로 이관
- T 타입 반환에서 T? 타입 반환으로 수정 (EmptyResponse 대응)
- Server Message 출력

* Feat: 온보딩 선택지 등록 및 추천 루틴 받기 구현 (#T3-102)

- OnboardingEndpoint 정의
- OnboardingRepositoryProtocol · OnboardingRepository 구현
- OnboardingUseCaseProtocol · OnboardingUseCase 구현

* Feat: Onboarding 관련 의존성 조립 (#T3-102)

- DataSource, Domain, Presentaion 레이어에서 OnboardingRepository, OnboardingUseCase, OnboardingViewModel 의존성 등록

* Feat: 로그인 결과에 따른 이용약관 vs 온보딩 화면 분기 처리

* Feat: CustomFontStyle 추가

- 디자인 시스템에 정의되어 있는 폰트 외에도 정의해서 사용할 수 있도록 custom 케이스 추가

* Feat: 온보딩 선택 결과 바탕으로 추천 받은 루틴 등록 구현 (#T3-102)

* Refactor: AuthRepository do-catch 블록 수정

* Refactor: 의존성 없을 시 fatalError 발생하도록 수정

* Refactor: 온보딩 선택지 Dictonary 변경 로직 수정

* Feat: 탭바 구현

* Refactor: BitnagilColor 수정

- 기존 White 계열 컬러들 Orange로 수정
- HomeGradient 색상 및 Error 색상 추가

* Docs: 홈 탭바 아이콘 이미지 Asset 추가

* Feat: TabBarView 구현

* Style: 코드 컨벤션을 위한 import 순서 정렬

* Refactor: Gradient 오타 수정

* Feat: 추천 루틴 UI 구현 (#T3-70)

* Feat: 아이콘 이미지 Asset 추가 및 BitnagilIcon 케이스 추가

- plus, chevron 아이콘 이미지 asset 추가
- BitnagilIcon plus, chevron 아이콘 케이스 추가
- UIImage extension을 통해 resize 및 roate 함수 구현

* Feat: 추천 루틴 카테고리 타입 및 뷰 구현 (#T3-70)

* Feat: 추천 루틴 난이도 타입 및 뷰 구현 (#T3-70)

- RoutineLevelType enum 정의
- RoutineLevelView UI 구현 (+ RoutineLevelCell)
- RoutineLevelButton 구현

* Feat: 감정 등록 버튼 구현 (#T3-70)

* Feat: 추천 루틴 카드 뷰 구현 (#T3-70)

* Style: BaseViewController, ViewModel 폴더 위치 변경

* Feat: CustomButtonSheet 구현

* Feat: RecommendedRoutineView 구현 (#T3-70)

* Feat: RecommendedRoutineViewModel 구현 (#T3-70)

* Feat: Presentation 모듈에 RecommendedViewModel 의존성 추가

* Feat: TabBarView에 추천 루틴 View 변경

- 기존 테스트용 View 삭제

* Fix: ScrollViewBottomSpacing 값 수정

* Refactor: Delegate 함수 네이밍 수정

* Refactor: RoutineLevelView 테이블 뷰 셀 UI 수정

- RoutineLevelView 테이블 뷰 셀 밑줄 간격 조정 및 마지막 셀 밑줄 생략
- CustomBottomSheet 투명도 변경

* Refactor: RoutineLevelType 정렬 중복 로직 정리

* [Feat-T3-104] 마이페이지 UI 구현 (#22)

* Feat: 이미지 asset 추가 (#T3-104)

- BitnagilIcon에 이미지 추가

* Feat: NSObject extension 추가  (#T3-104)

- {class이름}.className을 통해 클래스의 이름을 스트링으로 사용할 수 있도록 구현

* Feat: UserDataRepository 구현  (#T3-104)

- 유저 정보 (닉네임, accessToken)을 가져올 수 있는 repository 구현

- UserDataRepository 의존성 등록

* Feat: 마이페이지 화면 구현  (#T3-104)

- 마이페이지 화면 구현
- 의존성 등록
- TabBar에서 Myapge 클래스 삭제

* refactor: 코드 리뷰 반영

* Feat: 홈 화면 UI 구현 (#T3-105)

* Feat: 메인 루틴 Component UI 구현 (#T3-105)

- epllipsis 아이콘 에셋 추가 및 BitnagilIcon 프로퍼티 추가
- MainRoutineView 구현

* Refactor: MainRoutineView 리팩토링

- Layout enum 추가
- 불필요한 do 문법 제거
- MainRoutine 구조체 폴더 위치 변경

* Feat: SubRoutineButton UI 구현 (#T3-105)

* Feat: RoutineView UI 구현 (#T3-105)

* Feat: DateView UI 구현 (#T3-105)

* Feat: HomeEmptyView UI 구현 (#T3-105)

* Feat: 이미지 에셋 추가 및 BitnagilIcon에 정의

- informationIcon, sortIcon HomeView에서 필요한 이미지 에셋 추가

* Feat: 홈 감정구슬 등록하기 버튼 UI 구현 (#T3-105)

* Feat: WeekView UI 구현 (#T3-105)

* Feat: HomeView UI Layout 구현 (#T3-105)

* Feat: 루틴 정렬, 필터링 테이블뷰 재사용 SelectableItemTableView로 구현 (#T3-105)

- SelectableItem protocol 정의 (정렬, 필터링 시 선택지)
- SelectableItemCell UI 구현
- SelectableItemTableView UI 구현
- RoutineLevelView, RoutineLevelCell 삭제 -> SelectableItemTableView 재사용
- RoutineSortType 정의

* Feat: HomeViewModel + HomeView 연동 (#T3-105)

* Refactor: routinesSubject 오타 수정

* Feat: 모듈 구조 변경 및 토큰 로직

* Refactor: NetworkService, Persistence 모듈 DataSource 모듈 합치기

* Refactor: Then 외부 라이브러리 제거 및 사용 코드 제거

* Feat: TokenManager 구현

* Refactor: Endpoint에 isAuthorized 값 추가

- Endpoint에 isAuthorized 값 추가 (인증이 필요한지 여부)
- 그렇다면 Endpoint 만들 때 header에 TokenManager에서 accessToken 빼와서 저장

* Refactor: UserDataRepository에 token 재발급 로직 추가

* Feat: 로그인 시 소셜 로그인 타입, 프로필 이미지 저장

- 카카오 로그인 시 nickname, profileImageUrl을 KakaoSDK에서 받아와 저장하도록 로직 추가
- 카카오, 애플 로그인 시 소셜 로그인 타입 저장하도록 로직 추가

* Fix: 온보딩 서버 Request Enum 값 변경

- 온보딩 서버 Request Enum 값 변경
- 온보딩 UI 수정 (SubTitle 여부)

* Feat: UserDataRepository에서 토큰 재발급 가능 여부에 따라 앱 진입점 분기처리

* Fix: 토큰 재발급 로직 Response 타입 변경

- TokenResponseDTO 구현
- reissue response 타입 교체

* Style: 불필요한 주석 제거

* Style: UserDataRepository 오타 수정

* Refactor: 코드 리뷰 반영

- NetworkService에서 endpoint.isAuthorized 값 확인하여 헤더에 토큰 값 세팅해주는 로직으로 수정
- 카카오 유저 정보 (닉네임, 프로필 이미지 URL) 메인스레드에서 동작하는 것 제거
- SceneDelegate에서 SplashView를 먼저 표시하고, reissueToken 결과에 따라 rootViewController 교체하도록 구조 개선

* Refactor: import SnapKit 추가

* [Feat-T3-110] 루틴 생성 화면 구현

* feat: 루틴 추가 화면 이미지 asset 추가 (#T3-110)

* feat: ToolTipView 구현  (#T3-110)

* feat: 서브루틴 추가 View 구현  (#T3-110)

* feat: 루틴 생성 ViewModel 구현  (#T3-110)

* feat: 기타 subview 구현  (#T3-110)

- 루틴 시작 시간 선택 버튼
- 서브 루틴 추가 버튼

* feat: 루틴 추가 화면 구현  (#T3-110)

* fix: Tooltip 오타 수정  (#T3-110)

* refactor: 코드 리뷰 반영

* [Feat] 날짜 선택가능한  DatePickerView 구현

* feat: DatePickerView 구현

* fix: 하루 종일 설정 버튼 추가

- 하루 종일 UILabel을 눌러도 하루 종일 버튼이 눌리도록 수정

* refactor: 코드 리뷰 반영

* Feat: 감정 등록 화면 구현 (#T3-69)

* Docs: 감정 구슬 이미지 Asset 추가 및 BitnagilGraphic에 케이스 추가 (#T3-69)

- 감정 구슬 이미지 Asset 추가
- 이미지 Asset 폴더링 정리
- BitnagilGraphic에 감정 구슬 변수 추가

* Feat: 감정 구슬 화면 UI 구현 (#T3-69)

- 감정 구슬 enum 타입 정의 (EmotionType)
- 감정 구슬 CollectionViewCell UI 구현 (EmotionOrbCollectionViewCell)
- 감정 구슬 등록 화면 UI 구현 (EmotionRegisterView)

* Feat: EmotionRegisterViewModel 의존성 조립 및 감정 등록 화면 연결 (홈, 추천 루틴)

* Style: emotionOrbCollectionView let으로 수정

* Feat: 추천 루틴 API 연동 (#T3-121)

* Style: 일부 파일들 import 컨벤션 맞추기

* Feat: RecommendedRoutineRepository 구현 (#T3-121)

- RecommendedRoutineDTO, RecommendedRoutineDictionaryResponseDTO, RecommendedRoutineListResponseDTO 타입 정의
- RecommendedRoutineEndpoint 생성
- RecommendedRoutineRepositoryProtocol, RecommendedRoutineRepository 정의 및 구현

* Feat: RecommendedRoutineUseCase 구현 (#T3-121)

- RecommendedRoutineUseCaseProtocol, RecommendedRoutineUseCase 정의 및 구현
- RecommendedRoutineEntity 수정 (카테고리, 난이도 값 추가)
- RoutineCategoryType, RoutineLevelType 위치 변경 (기존 Presentation -> Domain)

* Refactor: 추천 루틴 뷰 수정 (#T3-121)

- 감정 등록 버튼이 추천 루틴 목록들보다 상단에 뜨도록 UI 수정
- RecommendedRoutineCardView에서 Label trailing Layout 추가

* Refactor: RoutineLevelType, RoutineCategoryType 모듈 위치 변경으로 인한 Presentation 모듈 수정

- Presentation 모듈에 있는 RoutineLevelType, RoutineCategoryType가 Domain을 extension해서 필요한 값을 추가하여 사용할 수 있도록 수정
- 그에 따른 import Domain 추가

* Feat: RecommendedRoutineView 및 RecommendedRoutineViewModel 로직 구현 (#T3-121)

- 전체 추천 루틴 받아오는 로직 추가 + 더미 데이터 삭제
- 카테고리 및 난이도에 대해 받아온 루틴 필터링하는 로직 추가
- 의존성 조립 (DataSource, Domain, Presentation)

* Feat: FloatingButton, FloatingMenuView UI 구현

* Feat: 추천 루틴 화면에 FloatingButton 추가 및 등록 화면 이동 (#T3-121)

- RecommendedRoutineView에 FloatingButton, FloatingMenu, dimmedView 추가
- 루틴 등록 화면으로 이동해야 하므로 RoutineCreationViewModel 의존성 등록

* Style: 오타 수정

* Refactor: 맞춤 추천 카테고리인 경우 감정 등록 버튼 보여주기

* [Feat] NetworkService 플러그인 구현 

* feat: 네트워크 플러그인 프로토콜 구현

* feat: 네트워크 플러그인 구현

* feat: networkService에 플러그인 적용

* refactor: 코드래빗 리뷰 반영

* refactor: 코드 리뷰 반영

* Feat: 감정 구슬 API 연동 및 추천 루틴 결과 화면 재사용 (#T3-127)

* Style: 클라에서 저장하고 있는 감정 구슬 값 삭제 (이미지 Asset, EmotionType 등)

* Feat: Emotion 조회 및 등록 API 연동 (#T3-127)

- EmotionEndpoint 정의
- EmotionRepositoryProtocol, EmotionRepository 정의 및 구현 (+ EmotionResponseDTO)
- EmotionUseCaseProtocol, EmotionUseCase 정의 및 구현 (+ EmotionEntity)

* Chore: Kingfisher 외부 라이브러리 의존성 추가

* Refatcor: 감정구슬 Cell, 감정구슬 등록 화면 수정 (#T3-127)

- 서버 통신으로 받은 값들을 보여주도록 수정
- 감정 구슬 이미지 Kingfisher로 출력

* Docs: 그래픽 이미지 Asset 추가 및 BitnagilGraphic 케이스 추가

* Refactor: 기존 OnboardingUseCase 삭제 및 추천 루틴 받아오는 로직 이동 (#T3-127)

- OnboardingUseCaseProtocol, OnboardingUseCase 삭제
- OnboardingRecommendedRoutineView 삭제
- 기존 온보딩 선택지 · 감정 값을 등록하고 추천 루틴을 받아오는 로직 이동

* Chore: Common View 폴더 생성 및 재사용 View 이동 (SplashView, TabBarView)

* Feat: ResultRecommendedRoutineView · ResultRecommendedRoutineViewModel 구현 (#T3-127)

- 추천 루틴 결과 값을 보여주는 화면(ResultRecommendedRoutineView) 구현
- 온보딩 선택지 or 감정 구슬 선택지에 따른 추천 루틴 받아오는 로직
- 선택한 추천 루틴 등록 로직

* Refactor: ResultRecommendedRoutineView 재사용을 위한 분기처리 (#T3-127)

- MypageView, OnboardingResultView, EmotionRegisterView에서 재사용할 수 있도록 분기 처리
- OnboardingResultView에 그래픽 추가

* Feat: 의존성 조립 (Domain, DataSource, Presentation)

* Refactor: 오타 수정

* Refactor: UIScreen.main 기준으로 height 계산하는 코드 수정

* Feat: 홈 API 연동 (1) - 루틴 조회, 루틴 상세 보기 (#T3-130)

* Feat: 유저 닉네임 가져오는 API 연동 (#T3-130)

- HomeView, MypageView에서 사용

* Chore: 이용 약관 및 공지사항 등 서비스 URL 교체

* Feat: HomeView에 FloatingButton 추가 (#T3-130)

* Feat: 루틴 목록 조회 API 연동 (#T3-130)

* Feat: 감정 구슬 관련 TooltipView 추가 (#T3-130)

* Feat: RoutineDetailView UI 구현 (#T3-130)

* Refactor: KakaoSDK에서 닉네임, 프로필 가져오는 로직 제거

* Refactor: 오타 수정 및 분기 처리

* Refactor: RoutineView, SubRoutineButton Layout 조정

* Refactor: LoginView, IntroView 그래픽 추가

* Chore: AppIcon 추가

* Feat: Home 화면에서 감정 구슬 보여주기 (#T3-130)

* Fix: IntroView Layout 수정

* Fix: handleTap 좌표 계산 수정

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

* feat: 이미지 asset 추가

* feat: TableViewCell 구현

- TableView에서 사용할 수 있는 3가지 타입의 cell 구현
- chevron 이미지로 다음 화면으로 넘어갈 수 있는 cell
- button이 있는 cell
- switch가 있는 cell

* feat: 커스텀 Alert View 추가

* feat: 설정 화면 커스텀 tableView Section HeaderView 구현

* feat: 설정 화면 구현

* feat: navigationController appearance 수정

* refactor: custom alert 파라미터 이름 수정, alert 표시 타입 수정

* refactor: 오타 수정 (토끼 리뷰 반영)

* Feat: 루틴 등록 / 수정 로직 구현

* feat: DTO -> Parameter 변환을 위한 Encodable extension 구현

* feat: 루틴 생성/수정 관련 DTO 정의

* feat: 루틴 endpoint 수정

- 루틴 생성 추가
- 루틴 수정 추가
- 루틴 단건 조회 추가

* fix: routine 엔티티의 id를 옵셔널로 수정

* feat: 루틴 repository 구현

* feat: 루틴 생성/수정 UseCase 구현

- 루틴 요약, 서브 루틴 요약 엔티티 생성

* feat: 루틴 등록/수정 로직 View와 연결

* [Feat] 네트워킹 로깅 플러그인 구현

* feat: 네트워킹 로깅 플러그인 구현

* refactor: 불필요한 공백, 상수 제거

* Feat: 루틴 삭제 구현 (#T3-134)

* Feat: 루틴 삭제 AlertView 구현 및 HomeView에 적용 (#T3-134)

* Feat: 반복 루틴 전체 삭제 구현 (#T3-134)

* Feat: 당일 루틴 삭제 구현 (#T3-134)

* Refactor: 서버 Request에 routineType 추가 및 Layout enum 정의

* Refactor: 오타 및 컨벤션 수정

* Feat: Splash view UI 구현

* Chore: Lottie 외부 라이브러리 의존성 추가

* Docs: Splash json 파일 및 bitnail logo 이미지 asset 추가

* Feat: SplashView UI 구현

* Chore: splash.json 파일 교체

- 용량이 너무 커서 git push가 안되는 이슈로 압축된 json 파일로 교체

* Chore: splash.json 파일 교체

* Refactor: SplashViewDelegate 구현하여 애니메이션 완료하도록 수정

* Feat: 온보딩 여부 저장 및 분기 처리

* Feat: 온보딩 여부 저장 및 불러오기, 삭제 로직 구현

- 온보딩 여부 저장, 불러오기, 삭제 로직 구현
- 필요없는 profileImageUrl 저장, 불러오기, 삭제 로직 제거

* Feat: 온보딩 여부에 따른 진입 뷰 분기 처리

- 온보딩 여부에 따른 진입 뷰 분기 처리
- 그에 따른 ViewController, ViewModel 접근제어자 변경 (internal -> public)

* Feat: 온보딩 네비게이션 Background 색상 변경

* Refactor: 코드 리뷰 반영

- 중복된 네비게이션 바 Appearance 설정 제거
- OnboardingView viewDidLoad 제거

* Refactor: 온보딩 첫 화면에서 백버튼 제거

* Feat: 설정 화면 로직 구현

* feat: 알림 section 주석 처리

* feat: 설정 로직 구현

* feat: 업데이트 로직 추가

* Feat: 프로필 default 이미지 asset 추가 및 MypageView에 세팅

* Refactor: isAuthenticated 값에 따른 화면 전환

---------

Co-authored-by: 최정인 <wjddls980912@naver.com>

* Feat: 루틴 완료 로직 구현 (#T3-144)

* Feat: 추천 루틴 화면에서 감정 등록에 따른 감정 버튼 안보이게 하기

* Fix: 루틴 삭제 performedDate 수정

* Feat: RoutineView UI 인터랙션 (#T3-144)

- 세부 루틴 완료 값에 따른 메인 루틴 완료 값 업데이트
- 메인 루틴 완료 값에 따른 세부 루틴 완료 값 업데이트

* Feat: 루틴 완료 API 연동 (#T3-144)

* Feat: loadingIndicatorView 추가

* Feat: 루틴 정렬 로직 구현

* Fix: 루틴 완료 배열 수정

* Feat: 추천 루틴 등록하기 기능 추가

* feat: 추천 루틴 등록하기 기능 추가

* Refactor: 오타 수정

---------

Co-authored-by: 최정인 <wjddls980912@naver.com>

* feat: 토스트 메시지 구현

* Fix: 토큰 재발급 role 추가

* Fix: reissue role에 따른 화면 분기 처리

* Feat: Home Toast View 적용

* Fix: 감정 구슬 등록 후 추천 루틴 등록 화면 이동

* Chore: 빌드 버전 업 (0.0.1 -> 0.0.3)

* Refactor: 오타 수정

* Refactor: nil일 때에도 감정 구슬 등록 action 추가

* [Fix] 루틴 불러오는 로직 수정 (#43)

* Fix: 루틴 fetch 로직 수정

* Fix: 루틴 등록 시, 키보드 내려가는 로직 수정

* Fix: 루틴 등록 수정

- 키보드 입력 끊김 이슈 수정
- 루틴 반복 선택하지 않아도 등록 활성화 되도록 수정

* feat: 로그 메시지 privacy 정책 적용

* Chore: 배포 전 설정 변경

---------

Co-authored-by: soungjunban <soungjunban@gmail.com>
Co-authored-by: Ban Soungjun <119939410+generalban@users.noreply.github.com>
Co-authored-by: taipaise <taipaise@gmail.com>
taipaise added a commit that referenced this pull request Aug 8, 2025
* Feat: SnapKit, Then 외부 라이브러리 추가

* Feat: Logger 구현

* Feat: Logger 구현

* Del: 임시 파일 (Shared.swift) 삭제

* Fix: log 메시지 출력 형식 변경

- 파일명, 라인 수, 함수명 default로 출력하도록 수정

* Feat: DIContainer 구현 (#T3-56)

* Update Package.swift

* [Del}  불필요한 이미지 파일 삭제

* Feat: Keychain 및 UserDefaults 기반 Persistence 구현 (#T3-60)

* Feat: Persistence 저장소 리팩토링 (#T3-60)

* Feat: NetworkService 구현 (#T3-57)

* Del: 임시 파일 (Domain.swift) 삭제

* Feat: Endpoint 구현 (#T3-57)

- Endpoint 프로토콜 구현
- Network 레이어에 Endpoint extension 구현
- 편의성을 위한 URLRequest extension 구현

* Feat: 네트워크 로직을 위한 enum 값들 구현 (#T3-57)

- NetworkError, HTTPMethod, AppProperties 구현

* Feat: NetworkService 구현

- NetworkServiceProtocol 추상체 구현
- NetworkService 구현체 구현

* Feat: BaseResponseDTO 구현 (#T3-57)

* Feat: 개발 서버 테스트 구현

- health-check API를 통한 테스트 진행
- 이를 위한 Endpoint, UseCase, Repository 구현
- App 모듈에 의존성 추가 (NetworkService, Persistence)

* Refactor: 피드백 기반 UserDefaultsStore 네이밍 및 불필요 타입 제거 (#T3-60)

* Refactor: KeyValueStorage 네이밍 및 계층 구조 정리 (#T3-60)

* Refactor: UserDefaults 밑 KeychainStorageProtocol (#T3-60)

* Refactor: KeychainStorage 오탈자 수 (#T3-60)

* Fix: UserDefaultsStorageProtocol 구현  타입 수정 (#T3-60)

* Refactor: UserDefaultsStorage 제네릭 타입 대응 및 프로토콜 정합성 개선 (#T3-60)

* Del: Git Cache 및 불필요한 파일 삭제

* Docs: gitignore 정보 추가

* Del: .xcworkspace 파일 삭제

* Feat: Design System 구현 (#T3-64)

* Feat: Gray 색상 Asset 추가 (#T3-64)

* Feat: BitnagilColor Enum 정의 (#T3-64)

* Feat: Pretendard 폰트 파일 추가

* Feat: BitnagilFont Enum 정의 (#T3-64)

- BitnagilFont Enum 정의
- FontName Enum 정의
- UIFont extension 구현

* Chore: Project 파일 수정

- 각 모듈의 infoPlist 설정 제거
- App 단의 info.plist 파일 사용하도록 수정

* Feat: BaseViewController 및 ViewModel protocol 구현 (#T3-65)

* Feat: ViewModel protocol 구현 (#T3-65)

* Feat: BaseViewController 구현 (#T3-56)

* Refactor: Test를 위한 HomeViewController 및 Test 객체들 수정

* Feat: Design System 구현 (#T3-64)

* Feat: Gray 색상 Asset 추가 (#T3-64)

* Feat: BitnagilColor Enum 정의 (#T3-64)

* Feat: Pretendard 폰트 파일 추가

* Feat: BitnagilFont Enum 정의 (#T3-64)

- BitnagilFont Enum 정의
- FontName Enum 정의
- UIFont extension 구현

* Chore: Project 파일 수정

- 각 모듈의 infoPlist 설정 제거
- App 단의 info.plist 파일 사용하도록 수정

* Refactor: BaseViewController의 viewModel 접근 제한자 수정 및 중복 제거

* Feat: ViewModel protocol 구현 (#T3-65)

* Feat: BaseViewController 구현 (#T3-56)

* Refactor: Test를 위한 HomeViewController 및 Test 객체들 수정

* Refactor: BaseViewController의 viewModel 접근 제한자 수정 및 중복 제거

* Refactor: HomeViewController에 디자인 시스템 적용

* Feat: Dependency Assembler 구현 (#T3-63)

* Refactor: DIContainer 로직 수정 (#T3-63)

- 기존 Any 타입으로 인스턴스를 저장하던 의존성을 factory closure 형태로 변경
- resolve 시 클로저 실행을 통해 인스턴스를 생성하도록 개선

* Feat: DependencyAssemblerProtocol 구현 (#T3-63)

* Feat: 각 모듈별 DependencyAssembler 구현

- Network, Persistence, DataSource, Domain, Presentation 모듈의 DependencyAssembler 구현
- previous 의존성을 명시하여 DI 등록 순서를 보장하도록 함

* Feat: App 모듈에 DependencyInjection 함수 구현 (#T3-63)

* Del: DataSoure의 불필요한 Resources 파일 제거

* Refactor: NetworkService 테스트 가능한 구조를 위해 NetworkProviderProtocol 구현 및 채택

* Feat: 빌드 체크 자동화 Github Actions 추가

* Chore: Kakao SDK 설치 및 Tuist 외부 의존성 추가

* Chore: Kakao SDK init 및 필요한 프로젝트 설정 세팅

* Refactor: 네트워크 모듈 세부 구현 수정

- Endpoint, URLRequest 수정
- 기존 parameter만 받던 Endpoint에서 queryParameters, bodyParameters를 구분지어서 받도록 수정
- 확장해서 만든 커스텀 URLRequest 생성자에서 url 뿐만 아니라 queryParamters도 받아 쿼리 파라미터가 있다면 붙여서 url 만들어주도록 수정
- 그에 따른 TestEndpoint 수정

* Feat: 카카오 로그인 기능 구현 (#T3-66)

- Enum, Error 폴더 구조 정리
- AuthEndpoint 구현
- AuthRepository 구현체 및 추상체 구현
- LoginUseCase 구현체 및 추상체 구현

* Feat: LoginView 및 LoginViewModel 구현 (#T3-66)

* Feat: 각 모듈 Assembler에 Login 및 Auth 의존성 추가

* Chore: info.plist 카카오 네이티브 키 설정 값 오타 수정

* Fix: 서버 요청사항에 맞게 Endpoint 수정

- AuthEndpoint 수정
- accessToken, refreshToken 저장 후 로그 찍기

* Feat: Apple 로그인 구현 (#T3-67)

* Refactor: LoginView Layout Enum 상수값 네이밍 수정

* Feat: LoginViewController에 Apple Login 버튼 추가

* Fix: TokenResponse, TokenEntity 값 서버에 맞게 수정

* Refactor: Apple 로그인 토큰 값 및 유저 정보 가져오는 로직 ViewController로 이동 (#T3-67)

- ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding 채택해주는 로직 LoginViewController로 이동 (기존 AuthRepository)

* Feat: LoginViewModel, LoginUseCase에 appleLogin 함수 구현 (#T3-67)

- LoginViewModel에서 apple 로그인 결과에 따라 값을 방출할 수 있도록 구현
- LoginUseCaseProtocol, LoginUseCase appleLogin 함수 정의 및 구현

* Feat: AuthError 및 UserDefaultsKey Enum 값 정의

* Refactor: 카카오 로그인 에러 구체화

* Feat: AuthRepository appleLogin 구현 (#T3-67)

- AuthRepositoryProtocol, AuthRepository appleLogin 정의 및 구현
- 받은 닉네임, 토큰 값을 통해 서버 통신 로직 구현
- UserDefaults에 닉네임 저장 및 불러오는 로직 구현

* Feat: AuthRepository UserDefaults 의존성 추가

* Chore: 프로젝트 세팅에 entitlements 지정

* Chore: 코드래빗 설정 파일 추가

* Refactor: Height 오타 수정

* Feat: 로그아웃, 탈퇴 및 토큰 재발급 로직 구현 (#T3-75)

* Del: health-check 관련 테스트 코드 삭제

* Feat: AuthRepository에 로그아웃 함수 구현 (#T3-75)

- AuthRepositoryProtocol, AuthRepository에 logout 함수 정의 및 구현
- AuthError에 토큰 삭제 에러 추가

* Feat: LogoutUseCase 구현 (#T3-75)

- LogoutUseCase Protocol, LogoutUsecase logout 함수 정의 및 구현
- LoginUseCase 폴더 이동

* Feat: LoginViewController, LoginViewModel에 logout 함수 추가

- 테스트 진행을 위해 LoginViewModel, LoginViewController에 로그아웃 버튼 추가 및 함수 구현

* Feat: LogoutUseCase 의존성 조립

* Refactor: 서버 응답 값에 맞게 BaseResponseDTO 수정

* Feat: 소셜 로그인 탈퇴 구현 (#T3-75)

- AuthEndpoint에 withdraw case 추가
- AuthRepositoryProtocol 및 AuthRepository에 withdraw 함수 정의 및 구현
- WithdrawUseCaseProtocol 및 WithdrawUseCase 정의 및 구현
- Domain, Presentation 의존성 등록 및 조립
- LoginViewModel, LoginViewController에 테스트를 위한 함수 및 버튼 추가

* Feat: 토큰 재발급 함수 reissueToken 구현

- AuthEndpoint에 reissue 케이스 추가
- AuthRepositoryProtocol 및 AuthRepository에 reissueToken 정의 및 구현
- LoginViewModel, LoginViewController에 reissue 테스트 관련 버튼 및 함수 추가

* Feat: 인트로 · 로그인 · 이용 약관 UI 구현 및 로직 정리 (#T3-86)

* Chore: 디자인 시스템 색상 Color Asset 추가 및 BitnagilColor 변수 값 추가

- Figma 디자인 시스템에 정의된 색상 값 Asset 추가
- 그에 따른 BitnagilColor 변수 추가
- Bundle을 해당 모듈에서 찾을 수 있도록 코드 변경

* Chore: 디자인 시스템 폰트 정리

- 기존 Enum으로 정리된 BitnagilFont에서 Struct로 변경하여 weight 값에 따른 폰트를 받을 수 있도록 수정
- FontAttributes, FontStyle, FontWeight 등 폰트 정의에 필요한 enum 및 struct 정의

* Chore: 디자인 시스템 icon 이미지 Asset 추가

- apple, kakao, check icon 이미지 추가
- BitnagilIcon Enum 정의

* Feat: IntroView 구현 (#T3-86)

- PrimaryButton Component 구현
- IntroView 구현

* Feat: LoginView UI 구현 및 로직 수정 (#T3-86)

* Refactor: 로그인 서버 통신 관련 AuthRepository 및 LoginUseCase 수정

* Feat: IntroView UI 구현 (#T3-86)

- IntroView UI 구현
- SceneDelegate 앱 진입점 수정

* Feat: LoginView UI 구현 (#T3-86)

- LoginView UI 구현
- SocialLoginButton Component 구현 (카카오, 애플)

* Feat: 이용 약관 동의 UI 구현 (#T3-86)

- TermAgreementItemView 컴포넌트 구현
- TermAgreementView UI 구현
- LoginViewModel과 로직 구현

* Feat: 이용 약관 동의 로직 구현 (#T3-86)

- 기존 TokenResponseDTO에서 LoginResponseDTO로 수정 (서버 응답에 User의 role이 추가되었음)
- AuthEndpoint에 agreements 케이스 추가
- AuthRepositoryProtocol 및 AuthRepository에 submitAgreements 정의 및 구현
- LoginUseCaseProtocol 및 LoginUseCase에 submitAgreements 정의 및 구현
- LoginViewModel에 submitAgreement 로직 구현

* Feat: UIViewController extension에 공통 backbutton을 사용할 수 있도록 커스텀

* Fix: IntroView, TermsAgreementView에 Label 색상 적용

* Refactor: label 오타 수정

* Feat: 온보딩 UI 구현 (#T3-68)

* Feat: 온보딩 선택지 버튼 Components UI 구현 (#T3-68)

* Feat: GradientProgressBar UI 구현

- Gradient 색상 Asset 추가
- GradientProgressBar를 포함한 navigationItem 구현

* Refactor: 온보딩 선택지 UI 수정 (#T3-68)

- OnboardingChoiceProtocol 구현
- 온보딩 선택 화면에서 해당 프로토콜을 채택한 타입이라면 OnboardingChoiceButton을 그릴 수 있도록 수정

* Feat: 온보딩에서 사용될 enum 타입 정의 (#T3-68)

- OnboardingType (온보딩 유형 값)
- OnboardingChoiceType (온보딩 선택지 값)

* Feat: OnboardingView 구현 (#T3-68)

- 온보딩 선택 화면에서 재사용될 OnboardingView 구현

* Feat: OnboardingResultView 구현 (#T3-68)

- 온보딩 결과 화면 UI 구현
- 특정 텍스트만 semiBold로 보여지기 위해 NSAttributedString extension 구현

* Feat: 온보딩 추천 루틴 UI 구현 (#T3-68)

* Feat: OnboardingViewModel 구현 (#T3-68)

* Feat: OnboardingViewModel 의존성 조립 및 이용 약관 화면에서 온보딩 화면으로 넘어가는 로직 구현 (#T3-68)

* Feat: 온보딩 서버 통신 구현 (#T3-102)

* Style: 통일성을 위한 폴더명 변경

* Refactor: BaseViewController 및 ViewModel에 public 접근제어자 삭제

* Refactor: NetworkService 리팩토링

- BaseResponseDTO DataSource에서 Network 모듈로 이관
- T 타입 반환에서 T? 타입 반환으로 수정 (EmptyResponse 대응)
- Server Message 출력

* Feat: 온보딩 선택지 등록 및 추천 루틴 받기 구현 (#T3-102)

- OnboardingEndpoint 정의
- OnboardingRepositoryProtocol · OnboardingRepository 구현
- OnboardingUseCaseProtocol · OnboardingUseCase 구현

* Feat: Onboarding 관련 의존성 조립 (#T3-102)

- DataSource, Domain, Presentaion 레이어에서 OnboardingRepository, OnboardingUseCase, OnboardingViewModel 의존성 등록

* Feat: 로그인 결과에 따른 이용약관 vs 온보딩 화면 분기 처리

* Feat: CustomFontStyle 추가

- 디자인 시스템에 정의되어 있는 폰트 외에도 정의해서 사용할 수 있도록 custom 케이스 추가

* Feat: 온보딩 선택 결과 바탕으로 추천 받은 루틴 등록 구현 (#T3-102)

* Refactor: AuthRepository do-catch 블록 수정

* Refactor: 의존성 없을 시 fatalError 발생하도록 수정

* Refactor: 온보딩 선택지 Dictonary 변경 로직 수정

* Feat: 탭바 구현

* Refactor: BitnagilColor 수정

- 기존 White 계열 컬러들 Orange로 수정
- HomeGradient 색상 및 Error 색상 추가

* Docs: 홈 탭바 아이콘 이미지 Asset 추가

* Feat: TabBarView 구현

* Style: 코드 컨벤션을 위한 import 순서 정렬

* Refactor: Gradient 오타 수정

* Feat: 추천 루틴 UI 구현 (#T3-70)

* Feat: 아이콘 이미지 Asset 추가 및 BitnagilIcon 케이스 추가

- plus, chevron 아이콘 이미지 asset 추가
- BitnagilIcon plus, chevron 아이콘 케이스 추가
- UIImage extension을 통해 resize 및 roate 함수 구현

* Feat: 추천 루틴 카테고리 타입 및 뷰 구현 (#T3-70)

* Feat: 추천 루틴 난이도 타입 및 뷰 구현 (#T3-70)

- RoutineLevelType enum 정의
- RoutineLevelView UI 구현 (+ RoutineLevelCell)
- RoutineLevelButton 구현

* Feat: 감정 등록 버튼 구현 (#T3-70)

* Feat: 추천 루틴 카드 뷰 구현 (#T3-70)

* Style: BaseViewController, ViewModel 폴더 위치 변경

* Feat: CustomButtonSheet 구현

* Feat: RecommendedRoutineView 구현 (#T3-70)

* Feat: RecommendedRoutineViewModel 구현 (#T3-70)

* Feat: Presentation 모듈에 RecommendedViewModel 의존성 추가

* Feat: TabBarView에 추천 루틴 View 변경

- 기존 테스트용 View 삭제

* Fix: ScrollViewBottomSpacing 값 수정

* Refactor: Delegate 함수 네이밍 수정

* Refactor: RoutineLevelView 테이블 뷰 셀 UI 수정

- RoutineLevelView 테이블 뷰 셀 밑줄 간격 조정 및 마지막 셀 밑줄 생략
- CustomBottomSheet 투명도 변경

* Refactor: RoutineLevelType 정렬 중복 로직 정리

* [Feat-T3-104] 마이페이지 UI 구현 (#22)

* Feat: 이미지 asset 추가 (#T3-104)

- BitnagilIcon에 이미지 추가

* Feat: NSObject extension 추가  (#T3-104)

- {class이름}.className을 통해 클래스의 이름을 스트링으로 사용할 수 있도록 구현

* Feat: UserDataRepository 구현  (#T3-104)

- 유저 정보 (닉네임, accessToken)을 가져올 수 있는 repository 구현

- UserDataRepository 의존성 등록

* Feat: 마이페이지 화면 구현  (#T3-104)

- 마이페이지 화면 구현
- 의존성 등록
- TabBar에서 Myapge 클래스 삭제

* refactor: 코드 리뷰 반영

* Feat: 홈 화면 UI 구현 (#T3-105)

* Feat: 메인 루틴 Component UI 구현 (#T3-105)

- epllipsis 아이콘 에셋 추가 및 BitnagilIcon 프로퍼티 추가
- MainRoutineView 구현

* Refactor: MainRoutineView 리팩토링

- Layout enum 추가
- 불필요한 do 문법 제거
- MainRoutine 구조체 폴더 위치 변경

* Feat: SubRoutineButton UI 구현 (#T3-105)

* Feat: RoutineView UI 구현 (#T3-105)

* Feat: DateView UI 구현 (#T3-105)

* Feat: HomeEmptyView UI 구현 (#T3-105)

* Feat: 이미지 에셋 추가 및 BitnagilIcon에 정의

- informationIcon, sortIcon HomeView에서 필요한 이미지 에셋 추가

* Feat: 홈 감정구슬 등록하기 버튼 UI 구현 (#T3-105)

* Feat: WeekView UI 구현 (#T3-105)

* Feat: HomeView UI Layout 구현 (#T3-105)

* Feat: 루틴 정렬, 필터링 테이블뷰 재사용 SelectableItemTableView로 구현 (#T3-105)

- SelectableItem protocol 정의 (정렬, 필터링 시 선택지)
- SelectableItemCell UI 구현
- SelectableItemTableView UI 구현
- RoutineLevelView, RoutineLevelCell 삭제 -> SelectableItemTableView 재사용
- RoutineSortType 정의

* Feat: HomeViewModel + HomeView 연동 (#T3-105)

* Refactor: routinesSubject 오타 수정

* Feat: 모듈 구조 변경 및 토큰 로직

* Refactor: NetworkService, Persistence 모듈 DataSource 모듈 합치기

* Refactor: Then 외부 라이브러리 제거 및 사용 코드 제거

* Feat: TokenManager 구현

* Refactor: Endpoint에 isAuthorized 값 추가

- Endpoint에 isAuthorized 값 추가 (인증이 필요한지 여부)
- 그렇다면 Endpoint 만들 때 header에 TokenManager에서 accessToken 빼와서 저장

* Refactor: UserDataRepository에 token 재발급 로직 추가

* Feat: 로그인 시 소셜 로그인 타입, 프로필 이미지 저장

- 카카오 로그인 시 nickname, profileImageUrl을 KakaoSDK에서 받아와 저장하도록 로직 추가
- 카카오, 애플 로그인 시 소셜 로그인 타입 저장하도록 로직 추가

* Fix: 온보딩 서버 Request Enum 값 변경

- 온보딩 서버 Request Enum 값 변경
- 온보딩 UI 수정 (SubTitle 여부)

* Feat: UserDataRepository에서 토큰 재발급 가능 여부에 따라 앱 진입점 분기처리

* Fix: 토큰 재발급 로직 Response 타입 변경

- TokenResponseDTO 구현
- reissue response 타입 교체

* Style: 불필요한 주석 제거

* Style: UserDataRepository 오타 수정

* Refactor: 코드 리뷰 반영

- NetworkService에서 endpoint.isAuthorized 값 확인하여 헤더에 토큰 값 세팅해주는 로직으로 수정
- 카카오 유저 정보 (닉네임, 프로필 이미지 URL) 메인스레드에서 동작하는 것 제거
- SceneDelegate에서 SplashView를 먼저 표시하고, reissueToken 결과에 따라 rootViewController 교체하도록 구조 개선

* Refactor: import SnapKit 추가

* [Feat-T3-110] 루틴 생성 화면 구현

* feat: 루틴 추가 화면 이미지 asset 추가 (#T3-110)

* feat: ToolTipView 구현  (#T3-110)

* feat: 서브루틴 추가 View 구현  (#T3-110)

* feat: 루틴 생성 ViewModel 구현  (#T3-110)

* feat: 기타 subview 구현  (#T3-110)

- 루틴 시작 시간 선택 버튼
- 서브 루틴 추가 버튼

* feat: 루틴 추가 화면 구현  (#T3-110)

* fix: Tooltip 오타 수정  (#T3-110)

* refactor: 코드 리뷰 반영

* [Feat] 날짜 선택가능한  DatePickerView 구현

* feat: DatePickerView 구현

* fix: 하루 종일 설정 버튼 추가

- 하루 종일 UILabel을 눌러도 하루 종일 버튼이 눌리도록 수정

* refactor: 코드 리뷰 반영

* Feat: 감정 등록 화면 구현 (#T3-69)

* Docs: 감정 구슬 이미지 Asset 추가 및 BitnagilGraphic에 케이스 추가 (#T3-69)

- 감정 구슬 이미지 Asset 추가
- 이미지 Asset 폴더링 정리
- BitnagilGraphic에 감정 구슬 변수 추가

* Feat: 감정 구슬 화면 UI 구현 (#T3-69)

- 감정 구슬 enum 타입 정의 (EmotionType)
- 감정 구슬 CollectionViewCell UI 구현 (EmotionOrbCollectionViewCell)
- 감정 구슬 등록 화면 UI 구현 (EmotionRegisterView)

* Feat: EmotionRegisterViewModel 의존성 조립 및 감정 등록 화면 연결 (홈, 추천 루틴)

* Style: emotionOrbCollectionView let으로 수정

* Feat: 추천 루틴 API 연동 (#T3-121)

* Style: 일부 파일들 import 컨벤션 맞추기

* Feat: RecommendedRoutineRepository 구현 (#T3-121)

- RecommendedRoutineDTO, RecommendedRoutineDictionaryResponseDTO, RecommendedRoutineListResponseDTO 타입 정의
- RecommendedRoutineEndpoint 생성
- RecommendedRoutineRepositoryProtocol, RecommendedRoutineRepository 정의 및 구현

* Feat: RecommendedRoutineUseCase 구현 (#T3-121)

- RecommendedRoutineUseCaseProtocol, RecommendedRoutineUseCase 정의 및 구현
- RecommendedRoutineEntity 수정 (카테고리, 난이도 값 추가)
- RoutineCategoryType, RoutineLevelType 위치 변경 (기존 Presentation -> Domain)

* Refactor: 추천 루틴 뷰 수정 (#T3-121)

- 감정 등록 버튼이 추천 루틴 목록들보다 상단에 뜨도록 UI 수정
- RecommendedRoutineCardView에서 Label trailing Layout 추가

* Refactor: RoutineLevelType, RoutineCategoryType 모듈 위치 변경으로 인한 Presentation 모듈 수정

- Presentation 모듈에 있는 RoutineLevelType, RoutineCategoryType가 Domain을 extension해서 필요한 값을 추가하여 사용할 수 있도록 수정
- 그에 따른 import Domain 추가

* Feat: RecommendedRoutineView 및 RecommendedRoutineViewModel 로직 구현 (#T3-121)

- 전체 추천 루틴 받아오는 로직 추가 + 더미 데이터 삭제
- 카테고리 및 난이도에 대해 받아온 루틴 필터링하는 로직 추가
- 의존성 조립 (DataSource, Domain, Presentation)

* Feat: FloatingButton, FloatingMenuView UI 구현

* Feat: 추천 루틴 화면에 FloatingButton 추가 및 등록 화면 이동 (#T3-121)

- RecommendedRoutineView에 FloatingButton, FloatingMenu, dimmedView 추가
- 루틴 등록 화면으로 이동해야 하므로 RoutineCreationViewModel 의존성 등록

* Style: 오타 수정

* Refactor: 맞춤 추천 카테고리인 경우 감정 등록 버튼 보여주기

* [Feat] NetworkService 플러그인 구현 

* feat: 네트워크 플러그인 프로토콜 구현

* feat: 네트워크 플러그인 구현

* feat: networkService에 플러그인 적용

* refactor: 코드래빗 리뷰 반영

* refactor: 코드 리뷰 반영

* Feat: 감정 구슬 API 연동 및 추천 루틴 결과 화면 재사용 (#T3-127)

* Style: 클라에서 저장하고 있는 감정 구슬 값 삭제 (이미지 Asset, EmotionType 등)

* Feat: Emotion 조회 및 등록 API 연동 (#T3-127)

- EmotionEndpoint 정의
- EmotionRepositoryProtocol, EmotionRepository 정의 및 구현 (+ EmotionResponseDTO)
- EmotionUseCaseProtocol, EmotionUseCase 정의 및 구현 (+ EmotionEntity)

* Chore: Kingfisher 외부 라이브러리 의존성 추가

* Refatcor: 감정구슬 Cell, 감정구슬 등록 화면 수정 (#T3-127)

- 서버 통신으로 받은 값들을 보여주도록 수정
- 감정 구슬 이미지 Kingfisher로 출력

* Docs: 그래픽 이미지 Asset 추가 및 BitnagilGraphic 케이스 추가

* Refactor: 기존 OnboardingUseCase 삭제 및 추천 루틴 받아오는 로직 이동 (#T3-127)

- OnboardingUseCaseProtocol, OnboardingUseCase 삭제
- OnboardingRecommendedRoutineView 삭제
- 기존 온보딩 선택지 · 감정 값을 등록하고 추천 루틴을 받아오는 로직 이동

* Chore: Common View 폴더 생성 및 재사용 View 이동 (SplashView, TabBarView)

* Feat: ResultRecommendedRoutineView · ResultRecommendedRoutineViewModel 구현 (#T3-127)

- 추천 루틴 결과 값을 보여주는 화면(ResultRecommendedRoutineView) 구현
- 온보딩 선택지 or 감정 구슬 선택지에 따른 추천 루틴 받아오는 로직
- 선택한 추천 루틴 등록 로직

* Refactor: ResultRecommendedRoutineView 재사용을 위한 분기처리 (#T3-127)

- MypageView, OnboardingResultView, EmotionRegisterView에서 재사용할 수 있도록 분기 처리
- OnboardingResultView에 그래픽 추가

* Feat: 의존성 조립 (Domain, DataSource, Presentation)

* Refactor: 오타 수정

* Refactor: UIScreen.main 기준으로 height 계산하는 코드 수정

* Feat: 홈 API 연동 (1) - 루틴 조회, 루틴 상세 보기 (#T3-130)

* Feat: 유저 닉네임 가져오는 API 연동 (#T3-130)

- HomeView, MypageView에서 사용

* Chore: 이용 약관 및 공지사항 등 서비스 URL 교체

* Feat: HomeView에 FloatingButton 추가 (#T3-130)

* Feat: 루틴 목록 조회 API 연동 (#T3-130)

* Feat: 감정 구슬 관련 TooltipView 추가 (#T3-130)

* Feat: RoutineDetailView UI 구현 (#T3-130)

* Refactor: KakaoSDK에서 닉네임, 프로필 가져오는 로직 제거

* Refactor: 오타 수정 및 분기 처리

* Refactor: RoutineView, SubRoutineButton Layout 조정

* Refactor: LoginView, IntroView 그래픽 추가

* Chore: AppIcon 추가

* Feat: Home 화면에서 감정 구슬 보여주기 (#T3-130)

* Fix: IntroView Layout 수정

* Fix: handleTap 좌표 계산 수정

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

* feat: 이미지 asset 추가

* feat: TableViewCell 구현

- TableView에서 사용할 수 있는 3가지 타입의 cell 구현
- chevron 이미지로 다음 화면으로 넘어갈 수 있는 cell
- button이 있는 cell
- switch가 있는 cell

* feat: 커스텀 Alert View 추가

* feat: 설정 화면 커스텀 tableView Section HeaderView 구현

* feat: 설정 화면 구현

* feat: navigationController appearance 수정

* refactor: custom alert 파라미터 이름 수정, alert 표시 타입 수정

* refactor: 오타 수정 (토끼 리뷰 반영)

* Feat: 루틴 등록 / 수정 로직 구현

* feat: DTO -> Parameter 변환을 위한 Encodable extension 구현

* feat: 루틴 생성/수정 관련 DTO 정의

* feat: 루틴 endpoint 수정

- 루틴 생성 추가
- 루틴 수정 추가
- 루틴 단건 조회 추가

* fix: routine 엔티티의 id를 옵셔널로 수정

* feat: 루틴 repository 구현

* feat: 루틴 생성/수정 UseCase 구현

- 루틴 요약, 서브 루틴 요약 엔티티 생성

* feat: 루틴 등록/수정 로직 View와 연결

* [Feat] 네트워킹 로깅 플러그인 구현

* feat: 네트워킹 로깅 플러그인 구현

* refactor: 불필요한 공백, 상수 제거

* Feat: 루틴 삭제 구현 (#T3-134)

* Feat: 루틴 삭제 AlertView 구현 및 HomeView에 적용 (#T3-134)

* Feat: 반복 루틴 전체 삭제 구현 (#T3-134)

* Feat: 당일 루틴 삭제 구현 (#T3-134)

* Refactor: 서버 Request에 routineType 추가 및 Layout enum 정의

* Refactor: 오타 및 컨벤션 수정

* Feat: Splash view UI 구현

* Chore: Lottie 외부 라이브러리 의존성 추가

* Docs: Splash json 파일 및 bitnail logo 이미지 asset 추가

* Feat: SplashView UI 구현

* Chore: splash.json 파일 교체

- 용량이 너무 커서 git push가 안되는 이슈로 압축된 json 파일로 교체

* Chore: splash.json 파일 교체

* Refactor: SplashViewDelegate 구현하여 애니메이션 완료하도록 수정

* Feat: 온보딩 여부 저장 및 분기 처리

* Feat: 온보딩 여부 저장 및 불러오기, 삭제 로직 구현

- 온보딩 여부 저장, 불러오기, 삭제 로직 구현
- 필요없는 profileImageUrl 저장, 불러오기, 삭제 로직 제거

* Feat: 온보딩 여부에 따른 진입 뷰 분기 처리

- 온보딩 여부에 따른 진입 뷰 분기 처리
- 그에 따른 ViewController, ViewModel 접근제어자 변경 (internal -> public)

* Feat: 온보딩 네비게이션 Background 색상 변경

* Refactor: 코드 리뷰 반영

- 중복된 네비게이션 바 Appearance 설정 제거
- OnboardingView viewDidLoad 제거

* Refactor: 온보딩 첫 화면에서 백버튼 제거

* Feat: 설정 화면 로직 구현

* feat: 알림 section 주석 처리

* feat: 설정 로직 구현

* feat: 업데이트 로직 추가

* Feat: 프로필 default 이미지 asset 추가 및 MypageView에 세팅

* Refactor: isAuthenticated 값에 따른 화면 전환

---------

Co-authored-by: 최정인 <wjddls980912@naver.com>

* Feat: 루틴 완료 로직 구현 (#T3-144)

* Feat: 추천 루틴 화면에서 감정 등록에 따른 감정 버튼 안보이게 하기

* Fix: 루틴 삭제 performedDate 수정

* Feat: RoutineView UI 인터랙션 (#T3-144)

- 세부 루틴 완료 값에 따른 메인 루틴 완료 값 업데이트
- 메인 루틴 완료 값에 따른 세부 루틴 완료 값 업데이트

* Feat: 루틴 완료 API 연동 (#T3-144)

* Feat: loadingIndicatorView 추가

* Feat: 루틴 정렬 로직 구현

* Fix: 루틴 완료 배열 수정

* Feat: 추천 루틴 등록하기 기능 추가

* feat: 추천 루틴 등록하기 기능 추가

* Refactor: 오타 수정

---------

Co-authored-by: 최정인 <wjddls980912@naver.com>

* feat: 토스트 메시지 구현

* Fix: 토큰 재발급 role 추가

* Fix: reissue role에 따른 화면 분기 처리

* Feat: Home Toast View 적용

* Fix: 감정 구슬 등록 후 추천 루틴 등록 화면 이동

* Chore: 빌드 버전 업 (0.0.1 -> 0.0.3)

* Refactor: 오타 수정

* Refactor: nil일 때에도 감정 구슬 등록 action 추가

* [Fix] 루틴 불러오는 로직 수정 (#43)

* Fix: 루틴 fetch 로직 수정

* Fix: 루틴 등록 시, 키보드 내려가는 로직 수정

* Fix: 루틴 등록 수정

- 키보드 입력 끊김 이슈 수정
- 루틴 반복 선택하지 않아도 등록 활성화 되도록 수정

* feat: 로그 메시지 privacy 정책 적용

* Chore: 배포 전 설정 변경

---------

Co-authored-by: soungjunban <soungjunban@gmail.com>
Co-authored-by: Ban Soungjun <119939410+generalban@users.noreply.github.com>
Co-authored-by: taipaise <taipaise@gmail.com>
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