Skip to content

[Feat] 온보딩 여부 저장 및 분기 처리#38

Merged
choijungp merged 5 commits intodevelopfrom
feat/save-onboarding
Aug 7, 2025
Merged

[Feat] 온보딩 여부 저장 및 분기 처리#38
choijungp merged 5 commits intodevelopfrom
feat/save-onboarding

Conversation

@choijungp
Copy link
Copy Markdown
Contributor

@choijungp choijungp commented Aug 6, 2025

🌁 Background

  • 온보딩 여부를 UserDefaults에 저장하여 분기 처리를 하도록 구현하였습니다.
  • 온보딩 View의 네비게이션 바 배경색을 맞춰주었습니다.

📱 Screenshot

수정 전 수정 후
Simulator Screenshot - iPhone 13 mini - 2025-08-06 at 22 00 53 Simulator Screenshot - iPhone 13 mini - 2025-08-06 at 21 33 51

머가 달라졌게요 ㅎㅎㅎㅎ..

👩‍💻 Contents

  • OnboardingRepository에 Onboarding 여부 값 저장하는 로직 추가
  • 회원 탈퇴 시 Onboarding 여부 값 삭제 로직 추가
  • OnboardingView navigation bar 배경 색상 변경

✅ Testing

  • 로그인 후 온보딩 하지 않고 앱을 날린 후 다시 앱 접속 > (온보딩을 진행하지 않았으므로) 온보딩 화면 ⭕️
  • 로그인 후 온보딩 완료 후 홈 접속, 이후 앱 날린 후 다시 앱 접속 > (온보딩을 진행하였으므로 온보딩 ❌) 홈 화면
  • 탈퇴 후 (임의로 탈퇴 로직 붙였습니다!!) 로그인 시도 > 온보딩 화면 ⭕️
  • 로그아웃 후 (임의로 로그아웃 로직 붙였습니다!!) 로그인 시도 > 온보딩 화면 ❌, 홈 화면

📝 Review Note

1. MypageView

MypageView를 제가 조금 건드렸어요 .. !!!

  • 건들인 이유: 온보딩 View(목표 재설정)에서 navigationBar의 색상을 바꿉니다. > 그리고 다시 MypageView로 돌아오면 변경된 색상으로 남아있어요 .. (BitnagilColor.gray99)
  • 무엇을 건들였나 : viewWillAppear를 override해서 navigationBar의 색상을 white로 변경해주었습니다.
    다른 View들은 보통 UIViewController+ 파일에서 navigationBar 세팅을 해주기 때문에 다른 뷰들에 대한 변경은 UIViewController+ 파일에 있습니다 !!
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    appearance.backgroundColor = .white
    appearance.shadowColor = .clear

    navigationController?.navigationBar.standardAppearance = appearance
    navigationController?.navigationBar.scrollEdgeAppearance = appearance
    navigationController?.navigationBar.compactAppearance = appearance
}

2. BaseViewController, ViewModel 접근제어자 public로 변경

온보딩 여부에 따라 분기처리가 이뤄어져야 해서 SceneDelegate에서 온보딩 뷰를 접근해야 합니다.

원래는 SceneDelegate에서 접근하는 뷰는 IntroView, TabBarView 뿐이라서 BaseViewController, ViewModel의 접근 제어자를 internal로 가져가도 괜찮았지만 ..
SceneDelegate에서 온보딩 뷰를 접근해야 하므로 BaseViewController, ViewModel 녀석들을 public으로 변경이 필수적으로 필요했습니다 .. ㅜㅜ

Task { @MainActor in
    let isLogined = await userDataRepository.reissueToken()
    if isLogined {
        if onboardingRepository.isOnboardingDone() {
            window?.rootViewController = TabBarView()
        } else {
            guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
                fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
            }
            let onboardingView = OnboardingView(viewModel: onboardingViewModel, onboarding: .time)
            let navigationController = UINavigationController(rootViewController: onboardingView)
            window?.rootViewController = navigationController
        }
    } else {
        let introView = IntroView()
        let navigationController = UINavigationController(rootViewController: introView)
        window?.rootViewController = navigationController
    }
}

3. 온보딩 여부 함수 OnboardingRepository에만 존재

현재 약간 뒤죽박죽하지만 UseCase가 단순 Repository를 passthrough하는 경우 Repository만 사용하기로 결정되었습니다 !!
그래서 온보딩 여부 확인하는 함수는 OnboardingRepository에만 존재합니다.

그러다 보니 onboardingRepository 인스턴스가 이곳 저곳 여러번 생성되어요 .. (온보딩 분기처리 해야 하는 뷰에 전부)

  • SceneDelegate
  • LoginView
  • 더 있을 수 있음 .......

이것의 해결법은 약간 리뷰 노트 4번과 이어져요 ...


4. 온보딩을 수행하지 않은 유저에 대한 케이스

테스트 케이스 1번을 보면 "로그인 후 온보딩 하지 않고 앱을 날린 후 다시 앱 접속 > (온보딩을 진행하지 않았으므로) 온보딩 화면 ⭕️" 이렇습니다.
하지만 온보딩 화면에는 첫번째 부터 백버튼이 존재하여, 앱 처음 진입점이 온보딩 화면이지만 백버튼이 존재하는 이상한 현상 발생 ...
백버튼을 눌러도 아무 현상이 일어나지 않는 이슈 발생 ...

이는 어찌 해결해야 할까요??

  1. 온보딩 여부를 아예 체크하지 말자
  2. 로그인 이후 온보딩 여부만 체크하고, 앱 진입점에서는 온보딩 여부를 체크하지 말기
  3. 백버튼 임의로 없애자 (디자이너 팀에게 문의해야 함)

Summary by CodeRabbit

  • 신규 기능

    • 온보딩 완료 여부를 확인하여, 완료 시 메인 화면(TabBarView)로, 미완료 시 온보딩 화면으로 이동하는 흐름이 추가되었습니다.
    • 로그인 성공 시 온보딩 진행 여부에 따라 다음 화면이 결정됩니다.
    • 네비게이션 바에 백 버튼 없는 진행 바 스타일이 새로 추가되었습니다.
  • 버그 수정

    • 네비게이션 바의 배경색 및 그림자 설정이 일관되게 적용되어 화면 전환 시 UI가 개선되었습니다.
    • 마이페이지 화면 진입 시 네비게이션 바 배경색이 흰색으로 설정되어 UI 일관성이 향상되었습니다.
  • 공개 범위 변경

    • 여러 View, ViewModel, Protocol의 접근 제한자가 public으로 변경되어 외부 모듈에서 접근이 가능해졌습니다.
  • 기타

    • 온보딩 관련 UserDefaults 저장 및 오류 처리 방식이 개선되었습니다.
    • 온보딩 완료 여부 확인 메서드가 추가되어 상태 관리가 강화되었습니다.

- 온보딩 여부 저장, 불러오기, 삭제 로직 구현
- 필요없는 profileImageUrl 저장, 불러오기, 삭제 로직 제거
- 온보딩 여부에 따른 진입 뷰 분기 처리
- 그에 따른 ViewController, ViewModel 접근제어자 변경 (internal -> public)
@choijungp choijungp requested a review from taipaise August 6, 2025 13:01
@choijungp choijungp self-assigned this Aug 6, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 6, 2025

Walkthrough

온보딩 완료 여부를 UserDefaults에 저장하고 확인하는 로직이 도입되었습니다. DI 컨테이너에서 OnboardingRepositoryProtocol을 주입받아 온보딩 상태를 판단해 루트 뷰 전환을 제어합니다. 관련 enum, 에러, 저장소, 뷰모델, 뷰 등의 접근 제어자가 public으로 확장되었습니다. 로그인 및 온보딩 플로우와 네비게이션 바 스타일링도 일부 수정되었습니다.

Changes

Cohort / File(s) Change Summary
온보딩 상태 저장 및 조회 로직
Projects/DataSource/Sources/Repository/OnboardingRepository.swift, Projects/Domain/Sources/Protocol/Repository/OnboardingRepositoryProtocol.swift, Projects/DataSource/Sources/Common/Enum/UserDefaultsKey.swift, Projects/DataSource/Sources/Common/Error/UserError.swift, Projects/DataSource/Sources/Repository/AuthRepository.swift
온보딩 완료 여부를 UserDefaults에 저장/조회하는 메서드 추가, 관련 에러 및 키 enum 변경, 프로필 이미지 URL 관련 코드 제거, 프로토콜에 isOnboardingDone() 추가 및 구현
앱 런칭 및 로그인 플로우 제어
Projects/App/Sources/SceneDelegate.swift, Projects/Presentation/Sources/Login/View/IntroView.swift, Projects/Presentation/Sources/Login/View/LoginView.swift
DI 컨테이너에서 온보딩 저장소 주입 및 온보딩 완료 여부에 따라 루트 뷰/네비게이션 분기, LoginView 생성자에 온보딩 저장소 추가, 로그인 후 온보딩 완료 여부에 따른 분기 로직 추가
접근 제어자(public) 확장
Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift, Projects/Presentation/Sources/Common/Protocol/ViewModel.swift, Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift, Projects/Presentation/Sources/Onboarding/ViewModel/OnboardingViewModel.swift
BaseViewController, ViewModel, OnboardingView, OnboardingViewModel 및 주요 메서드/이니셜라이저 public으로 변경
네비게이션 바 스타일링 개선
Projects/Presentation/Sources/Common/Extension/UIViewController+.swift, Projects/Presentation/Sources/MyPage/View/MypageView.swift, Projects/Presentation/Sources/Home/View/HomeView.swift
네비게이션 바 배경색/스타일을 일관성 있게 설정하는 메서드 추가 및 적용, MyPageView에서 viewWillAppear로 appearance 적용, HomeView에서 viewWillAppear에 네비게이션 바 숨김 설정 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App(SceneDelegate)
    participant AuthRepository
    participant OnboardingRepository
    participant DIContainer
    participant UI

    User->>App(SceneDelegate): 앱 실행
    App->>DIContainer: AuthRepository, OnboardingRepository resolve
    App->>AuthRepository: 토큰 재발급
    AuthRepository-->>App: 로그인 상태 확인
    App->>OnboardingRepository: isOnboardingDone()
    OnboardingRepository-->>App: 온보딩 완료 여부 반환
    alt 온보딩 완료
        App->>UI: TabBarView로 루트 뷰 설정
    else 미완료
        App->>DIContainer: OnboardingViewModel resolve
        App->>UI: OnboardingView (UINavigationController)로 루트 뷰 설정
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
온보딩 끝? 아직이야!
저장소에 살짝 메모해두고
로그인 후엔 다시 체크,
루트 뷰도 똑똑하게 분기해요.
public으로 문 활짝 열고
네비게이션 바도 하얗게 단장!
비트나길, 오늘도 한 뼘 성장!

Note

⚡️ Unit Test Generation is now available in beta!

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


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 03f40e2 and 54e35cd.

📒 Files selected for processing (2)
  • Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (5 hunks)
  • Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift
  • Projects/Presentation/Sources/Common/Extension/UIViewController+.swift
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/save-onboarding

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • 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.

Support

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

Documentation and Community

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

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

42-52: 네비게이션 바 외관 설정을 일관성 있게 처리해주세요.

온보딩 뷰에서 돌아올 때 네비게이션 바 색상을 초기화하려는 목적은 적절합니다. 하지만 UIViewController+.swift에 추가된 changeNavigationBackground(color:) 메서드와 동일한 로직을 중복 구현하고 있습니다.

일관성을 위해 extension 메서드를 활용해주세요:

 override func viewWillAppear(_ animated: Bool) {
     super.viewWillAppear(animated)
-    let appearance = UINavigationBarAppearance()
-    appearance.configureWithOpaqueBackground()
-    appearance.backgroundColor = .white
-    appearance.shadowColor = .clear
-
-    navigationController?.navigationBar.standardAppearance = appearance
-    navigationController?.navigationBar.scrollEdgeAppearance = appearance
-    navigationController?.navigationBar.compactAppearance = appearance
+    changeNavigationBackground(color: .white)
 }

단, changeNavigationBackground 메서드가 private이므로 접근 제어자를 수정하거나, 별도의 public 메서드를 제공하는 것을 고려해주세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7726a5a and 44d59bf.

📒 Files selected for processing (14)
  • Projects/App/Sources/SceneDelegate.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Enum/UserDefaultsKey.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Error/UserError.swift (2 hunks)
  • Projects/DataSource/Sources/Repository/AuthRepository.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/OnboardingRepository.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/OnboardingRepositoryProtocol.swift (1 hunks)
  • Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (3 hunks)
  • Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift (2 hunks)
  • Projects/Presentation/Sources/Common/Protocol/ViewModel.swift (1 hunks)
  • Projects/Presentation/Sources/Login/View/IntroView.swift (2 hunks)
  • Projects/Presentation/Sources/Login/View/LoginView.swift (3 hunks)
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift (1 hunks)
  • Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift (4 hunks)
  • Projects/Presentation/Sources/Onboarding/ViewModel/OnboardingViewModel.swift (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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/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#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/Presentation/Sources/Login/View/IntroView.swift
  • Projects/DataSource/Sources/Repository/OnboardingRepository.swift
  • Projects/Domain/Sources/Protocol/Repository/OnboardingRepositoryProtocol.swift
  • Projects/Presentation/Sources/Login/View/LoginView.swift
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift
  • Projects/Presentation/Sources/Onboarding/ViewModel/OnboardingViewModel.swift
  • Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift
  • Projects/App/Sources/SceneDelegate.swift
  • Projects/Presentation/Sources/Common/Extension/UIViewController+.swift
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.

Applied to files:

  • Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift
  • Projects/Presentation/Sources/MyPage/View/MypageView.swift
  • Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift
🧬 Code Graph Analysis (10)
Projects/DataSource/Sources/Repository/AuthRepository.swift (1)
Projects/DataSource/Sources/Persistence/UserDefaultsStorage.swift (1)
  • remove (25-28)
Projects/Presentation/Sources/Login/View/IntroView.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/DataSource/Sources/Repository/OnboardingRepository.swift (1)
Projects/DataSource/Sources/Persistence/UserDefaultsStorage.swift (2)
  • save (16-19)
  • load (21-23)
Projects/Domain/Sources/Protocol/Repository/OnboardingRepositoryProtocol.swift (1)
Projects/DataSource/Sources/Repository/OnboardingRepository.swift (1)
  • isOnboardingDone (26-31)
Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift (3)
Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift (1)
  • viewDidLoad (56-58)
Projects/Presentation/Sources/Common/Component/CustomBottomSheet.swift (1)
  • viewDidLoad (44-49)
Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineView.swift (1)
  • viewDidLoad (63-66)
Projects/Presentation/Sources/Login/View/LoginView.swift (2)
Projects/DataSource/Sources/Repository/OnboardingRepository.swift (1)
  • isOnboardingDone (26-31)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/Presentation/Sources/MyPage/View/MypageView.swift (7)
Projects/Presentation/Sources/Login/View/IntroView.swift (1)
  • viewWillAppear (37-40)
Projects/Presentation/Sources/Login/View/LoginView.swift (1)
  • viewWillAppear (51-54)
Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift (1)
  • viewWillAppear (60-67)
Projects/Presentation/Sources/Setting/View/SettingView.swift (1)
  • viewWillAppear (109-112)
Projects/Presentation/Sources/EmotionRegister/View/EmotionRegisterView.swift (1)
  • viewWillAppear (58-61)
Projects/Presentation/Sources/Onboarding/View/OnboardingResultView.swift (1)
  • viewWillAppear (68-73)
Projects/Presentation/Sources/ResultRecommendedRoutine/View/ResultRecommendedRoutineView.swift (1)
  • viewWillAppear (114-117)
Projects/Presentation/Sources/Onboarding/ViewModel/OnboardingViewModel.swift (4)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
  • action (65-88)
Projects/Presentation/Sources/EmotionRegister/ViewModel/EmotionRegisterViewModel.swift (1)
  • action (32-37)
Projects/Presentation/Sources/ResultRecommendedRoutine/ViewModel/ResultRecommendedRoutineViewModel.swift (1)
  • action (51-62)
Projects/Presentation/Sources/Login/ViewModel/LoginViewModel.swift (1)
  • action (43-89)
Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift (4)
Projects/Presentation/Sources/Login/View/IntroView.swift (2)
  • viewDidLoad (31-35)
  • viewWillAppear (37-40)
Projects/Presentation/Sources/Login/View/LoginView.swift (2)
  • viewDidLoad (47-49)
  • viewWillAppear (51-54)
Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift (1)
  • viewDidLoad (22-29)
Projects/Presentation/Sources/Onboarding/View/OnboardingResultView.swift (3)
  • viewDidLoad (53-56)
  • viewWillAppear (68-73)
  • viewDidLayoutSubviews (75-82)
Projects/App/Sources/SceneDelegate.swift (3)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)
  • reissueToken (25-44)
Projects/DataSource/Sources/Repository/OnboardingRepository.swift (1)
  • isOnboardingDone (26-31)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift

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

(unneeded_override)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (14)
Projects/DataSource/Sources/Common/Enum/UserDefaultsKey.swift (1)

11-11: 온보딩 키 추가가 적절합니다.

profileImageUrl 케이스를 onboarding 케이스로 교체한 것이 PR의 온보딩 상태 저장 목적과 일치합니다.

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

17-20: 온보딩 상태 확인 메서드 추가가 적절합니다.

동기적인 isOnboardingDone() 메서드 추가로 온보딩 완료 여부를 간단하게 확인할 수 있게 되었습니다. UserDefaults 접근에는 동기 메서드가 적합합니다.

Projects/Presentation/Sources/Common/Protocol/ViewModel.swift (1)

8-8: 프로토콜 접근 수준 변경이 적절합니다.

ViewModel 프로토콜을 public으로 변경하여 모듈 간 의존성 주입과 상호 운용성을 지원합니다.

Projects/Presentation/Sources/Login/View/IntroView.swift (2)

8-8: Domain 모듈 import 추가가 적절합니다.

OnboardingRepositoryProtocol 사용을 위한 필요한 import입니다.


52-60: 의존성 주입 패턴이 올바르게 적용되었습니다.

DIContainer에서 필요한 의존성들을 해결하고 LoginView에 전달하는 방식이 적절합니다. fatalError 사용은 의존성 설정 오류를 조기에 발견하기 위한 일반적인 패턴입니다.

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

151-154: 온보딩 상태 삭제로 변경이 적절합니다.

탈퇴 시 온보딩 상태를 삭제하는 것은 사용자가 재가입할 때 온보딩을 다시 진행하도록 하는 의도적인 동작으로 보입니다. 프로필 이미지 URL 삭제에서 온보딩 상태 삭제로 변경한 것이 PR의 목적과 일치합니다.

Projects/Presentation/Sources/Common/Protocol/BaseViewController.swift (1)

10-10: 접근 제어자 변경이 적절함

SceneDelegate에서 온보딩 뷰를 인스턴스화하기 위해 BaseViewController를 public으로 변경한 것이 적절합니다. 최소한의 변경으로 필요한 접근성을 확보했습니다.

Also applies to: 22-22

Projects/DataSource/Sources/Repository/OnboardingRepository.swift (3)

12-12: UserDefaultsStorage 인스턴스 추가 적절함

온보딩 상태를 UserDefaults에 저장하기 위한 의존성 추가가 적절합니다.


19-21: 온보딩 완료 상태 저장 로직 검토

UserDefaults 저장 실패 시 에러를 던지는 것이 적절합니다. 저장 성공 여부를 확인하고 실패 시 명확한 에러를 제공합니다.


26-31: 온보딩 완료 여부 확인 로직 검토

기본값으로 false를 반환하는 것이 안전한 접근법입니다. 온보딩이 완료되지 않은 경우를 기본 상태로 처리하여 사용자 경험을 보장합니다.

Projects/Presentation/Sources/Onboarding/ViewModel/OnboardingViewModel.swift (1)

12-13: OnboardingViewModel 접근 제어자 변경 적절함

SceneDelegate에서 OnboardingViewModel을 사용하기 위해 클래스와 관련 타입들을 public으로 변경한 것이 적절합니다. 필요한 부분만 노출하여 캡슐화를 유지했습니다.

Also applies to: 20-20, 51-51

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

15-17: 온보딩 관련 에러 케이스 추가 적절함

온보딩 상태 저장/로드/삭제에 대한 에러 케이스가 적절하게 추가되었습니다.


34-39: 에러 메시지 현지화 적절함

온보딩 관련 에러 메시지가 한국어로 적절하게 작성되어 사용자에게 명확한 정보를 제공합니다.

Projects/App/Sources/SceneDelegate.swift (1)

57-58: OnboardingRepository 의존성 주입 적절함

온보딩 완료 여부 확인을 위한 의존성 주입이 적절하게 구현되었습니다.

Comment on lines +63 to +72
if onboardingRepository.isOnboardingDone() {
window?.rootViewController = TabBarView()
} else {
guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
}
let onboardingView = OnboardingView(viewModel: onboardingViewModel, onboarding: .time)
let navigationController = UINavigationController(rootViewController: onboardingView)
window?.rootViewController = navigationController
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 6, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

온보딩 분기 처리 로직 검토

로그인 후 온보딩 완료 여부에 따른 분기 처리가 명확하게 구현되었습니다. 온보딩 미완료 시 OnboardingView로, 완료 시 TabBarView로 적절히 분기됩니다.

그런데 PR 목표에서 언급된 "여러 뷰에서 OnboardingRepository 인스턴스가 여러 개 생성되는 문제"를 고려해볼 필요가 있습니다. DI 컨테이너에서 싱글톤으로 관리하는지 확인해보시기 바랍니다.

다음 스크립트로 OnboardingRepository의 생성 패턴을 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# OnboardingRepository 사용 패턴 확인
echo "=== OnboardingRepository 의존성 주입 패턴 확인 ==="
rg -A 3 -B 1 "OnboardingRepositoryProtocol\.self"

echo -e "\n=== DIContainer에서 OnboardingRepository 등록 확인 ==="
rg -A 5 -B 2 "OnboardingRepository"

Length of output: 11414


🏁 Script executed:

#!/bin/bash
# DIContainer 구현부 확인
echo "=== DIContainer 클래스 정의 검색 ==="
rg -A 5 -B 5 "class DIContainer" -n .

echo -e "\n=== DIContainer.register 메서드 서명 검색 ==="
rg -n "func register" -n .

Length of output: 2879


OnboardingRepository 싱글톤 등록 필요성 검토

현재 DataSourceDependencyAssembler에서 아래와 같이 factory 방식으로 OnboardingRepository를 등록하고 있어, DIContainer.resolve 호출 시마다 새로운 인스턴스가 생성됩니다.
여러 뷰에서 resolve를 통해 OnboardingRepository가 반복 생성되는 현상을 방지하려면 싱글톤으로 공유되도록 등록 방식을 개선해야 합니다.

  • 수정 위치
    Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift

  • 제안 변경 예시

    - DIContainer.shared.register(type: OnboardingRepositoryProtocol.self) { _ in
    -     return OnboardingRepository()
    - }
    + // 싱글톤 인스턴스 미리 생성
    + private let sharedOnboardingRepository = OnboardingRepository()
    + DIContainer.shared.register(type: OnboardingRepositoryProtocol.self) { _ in
    +     return sharedOnboardingRepository
    + }

또는 DIContainer 자체에 최초 생성된 인스턴스를 캐싱하는 로직을 추가하는 방법도 고려해 보세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift around
the registration of OnboardingRepository, the current factory registration
causes a new instance to be created on each resolve call, leading to multiple
instances across views. To fix this, change the registration of
OnboardingRepository to a singleton scope so that the same instance is shared
throughout the app. This can be done by registering it once and returning the
cached instance on subsequent resolves, or by adding caching logic inside
DIContainer to store and reuse the first created instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

이거슨 .. 9월에 DIContainer에 scope라는 개념을 도입할지 상의를 해볼게요 .. !!! ㅜㅜ

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@choijungp 네, 맞습니다! DIContainer에 scope 개념을 도입하는 것은 전체 아키텍처에 영향을 주는 큰 변경사항이니까 팀과 충분히 상의해서 결정하는 게 좋겠어요 👍

지금은 현재 방식대로 사용하시고, 9월에 scope 개념 도입을 검토해보시면 될 것 같습니다. 그때 singleton, transient 등의 lifecycle 관리를 체계적으로 정리하실 수 있을 거예요!

현재 구현으로도 기능상 문제는 없으니 우선 이대로 진행하시면 됩니다 😊


🧠 Learnings used
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/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#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.

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로 해결하도록 수정할 계획을 가지고 있음.

Comment on lines +131 to +133
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

OnboardingViewModel을 의존성으로 주입받도록 리팩토링 필요

현재 DIContainer에서 직접 resolve하는 방식은 의존성 주입 원칙에 위배됩니다. OnboardingRepositoryProtocol처럼 생성자를 통해 주입받는 것이 더 나은 설계입니다.

다음과 같이 리팩토링하는 것을 제안합니다:

 private let onboardingRepository: OnboardingRepositoryProtocol
+private let onboardingViewModel: OnboardingViewModel

-init(onboardingRepository: OnboardingRepositoryProtocol, viewModel: LoginViewModel) {
+init(onboardingRepository: OnboardingRepositoryProtocol, 
+     onboardingViewModel: OnboardingViewModel,
+     viewModel: LoginViewModel) {
     self.onboardingRepository = onboardingRepository
+    self.onboardingViewModel = onboardingViewModel
     cancellables = []
     super.init(viewModel: viewModel)
 }

그리고 사용 부분을:

-guard let onboardingViewModel = DIContainer.shared.resolve(type: OnboardingViewModel.self) else {
-    fatalError("onboardingViewModel 의존성이 등록되지 않았습니다.")
-}
-let onboardingView = OnboardingView(viewModel: onboardingViewModel, onboarding: .time)
+let onboardingView = OnboardingView(viewModel: self.onboardingViewModel, onboarding: .time)
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Login/View/LoginView.swift around lines 131 to
133, the OnboardingViewModel is currently obtained by directly resolving it from
DIContainer, which violates dependency injection principles. Refactor the code
to inject OnboardingViewModel via the initializer of LoginView or its ViewModel,
similar to how OnboardingRepositoryProtocol is injected. Remove the direct
DIContainer.resolve call and pass the dependency through constructor injection
to improve testability and design.

Comment thread Projects/Presentation/Sources/Onboarding/View/OnboardingView.swift Outdated
- 중복된 네비게이션 바 Appearance 설정 제거
- OnboardingView viewDidLoad 제거
Copy link
Copy Markdown
Collaborator

@taipaise taipaise left a comment

Choose a reason for hiding this comment

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

  1. 네비게이션 뷰 확인했습니다!
  2. 온보딩 여부로 로직이 복잡해진다면 다음 방법은 어떨까요? 로그인 시점에 온보딩 여부는 판단하지 않고, 홈 화면에서 온보딩 여부를 체크하는 방법입니다. 만약에 온보딩 기록이 없다면 툴팁 알림이나, 팝업 메시지 등등 UI Component를 적절하게 활용해서 온보딩을 할 수 있도록 유도하는 방법입니다.
    다만 이 부분은 기획/서버 분들과의 회의가 필요할것 같습니다. 혹시 제가 이해를 잘못한 부분이 있다면 말씀해주세요.!~!
  • 미흡한 부분과 제가 아직 반영하지 못한 부분 임의 구현하시느라 고생하셨습니다.. 오늘 내로 빠르게 반영할게요! 사실 Mypage쪽 네비게이션 appearance도 제가 수정한 부분인데 괜히 일을 두 번 하시게 만든것 같습니다 ㅠ

@choijungp choijungp merged commit 08e37dc into develop Aug 7, 2025
2 checks passed
@choijungp choijungp deleted the feat/save-onboarding branch August 7, 2025 15:19
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