Skip to content

[Fix] #883 - 콕찌르기 알림탭 진입 방식 수정#885

Open
kwonseokki wants to merge 5 commits into
developfrom
fix/#883-deeplink-tabbar
Open

[Fix] #883 - 콕찌르기 알림탭 진입 방식 수정#885
kwonseokki wants to merge 5 commits into
developfrom
fix/#883-deeplink-tabbar

Conversation

@kwonseokki
Copy link
Copy Markdown
Contributor

🌴 PR 요약

알림탭에서 콕찌르기 진입 시 이중 BottomSheet로 인해서 로띠가 나타나지 않는 문제를 수정합니다.

🌱 작업한 브랜치

🌱 PR Point

isRouteFromTabBar 플래그를 제거하여 기존 BottomSheet로 알림을 띄우던 방식에서 탭화면으로 띄우는 방식으로 수정했습니다.

📌 참고 사항

📸 스크린샷

기능 스크린샷
기능이름 스크린샷 첨부

📮 관련 이슈

@kwonseokki kwonseokki self-assigned this May 19, 2026
@kwonseokki kwonseokki added Fix 문제 해결, 코드 수정 석기🍙 labels May 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Review Change Stack

Summary by CodeRabbit

  • UI 개선

    • 포크 메인 화면의 뒤로가기 버튼이 제거되어 화면 구성이 단순해졌습니다.
  • 리팩토링

    • 포크 기능의 네비게이션 흐름과 진입 로직이 간소화되어 동작이 단순화되었습니다.
  • 딥링크 / 알림

    • 딥링크 처리 시 대상 탭을 먼저 선택한 후 이동하며, 일부 딥링크 경로의 처리 방식이 단순화되었습니다.

Walkthrough

PR은 PokeFeature의 라우팅 파라미터 isRouteFromTabBar를 제거하고, Poke 코디네이터와 뷰 컨트롤러/뷰모델, 루트 딥링크/알림 처리 흐름을 탭 기반 단일 진입으로 단순화합니다.

Changes

Poke 라우팅 단순화

Layer / File(s) Summary
빌더 프로토콜 및 구현 시그니처 업데이트
SOPT-iOS/Projects/Features/PokeFeature/Interface/Sources/PokeFeatureBuildable.swift, SOPT-iOS/Projects/Features/PokeFeature/Sources/Coordinator/LegacyPokeBuilder.swift, SOPT-iOS/Projects/Features/PokeFeature/Sources/Coordinator/PokeBuilder.swift
PokeFeatureBuildable.makePokeMain에서 isRouteFromTabBar를 제거하고 makePokeMain(coordinator:)로 변경. 빌더 구현들은 PokeMainVC 생성 시 더 이상 isRouteFromTabBar 인자를 전달하지 않습니다.
PokeCoordinator 라우팅 흐름 단순화
SOPT-iOS/Projects/Features/PokeFeature/Sources/Coordinator/PokeCoordinator.swift, SOPT-iOS/Projects/Features/PokeFeature/Sources/Coordinator/LegacyPokeCoordinator.swift
start()/showPokeMain 파라미터가 제거되어 단일 경로로 PokeMain을 표시하도록 통합되며, LegacyPokeCoordinator에서 onNaviBackTap 바인딩 등록이 제거됩니다.
PokeMainVC UI 및 초기화 정리
SOPT-iOS/Projects/Features/PokeFeature/Sources/PokeMainScene/VC/PokeMainVC.swift
navigationView에서 backButton 제거, 공개 init에서 isRouteFromTabBar 파라미터 삭제, 관련 UI 로직 및 레이아웃 제약, 및 back-button 바인딩 제거.
PokeMainViewModel 입력 정리 및 인터페이스 업데이트
SOPT-iOS/Projects/Features/PokeFeature/Sources/PokeMainScene/ViewModel/PokeMainViewModel.swift, SOPT-iOS/Projects/Features/PokeFeature/Interface/Sources/PokeMainPresentable.swift
onNaviBackTap 퍼블릭 콜백과 Input.naviBackButtonTap 제거; PokeMainRoutingTrigger에서 onNaviBackTap 요구사항 삭제.
루트 코디네이터 및 딥링크/알림 컴포넌트 변경
SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift, SOPT-iOS/Projects/Features/RootFeature/Sources/DeepLinks/PokeDeepLink.swift, SOPT-iOS/Projects/Features/RootFeature/Sources/NotificationHelpers/*, SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/DeepLink/DeepLinkComponentsExecutable.swift
DeepLinkComponents에 targetTap 추가 및 관련 초기자 시그니처 변경, NotificationHandler가 deep-link에서 targetTap를 계산해 전달하도록 변경, ApplicationCoordinator의 runPokeFlow/PokeCoordinator.start 호출에서 파라미터 제거, PokeDeepLink.execute 분기 간소화.

Sequence Diagram

sequenceDiagram
  participant App as ApplicationCoordinator
  participant PokeC as PokeCoordinator
  participant Builder as PokeBuilder
  participant Nav as NavigationController
  App->>PokeC: start()
  PokeC->>PokeC: showPokeMain()
  PokeC->>Builder: makePokeMain(coordinator:)
  Builder->>Nav: setViewControllers([pokeMain.vc])
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size/M

Suggested reviewers

  • juri123123
  • yungu0010
  • dlwogus0128

"나는 토끼, 코드를 깎았지요,
탭 하나로 포케 흐름을 모아,
백 버튼은 사라졌지만 길은 명확해,
딥링크는 탭을 가리키고, 코디는 짧아졌네,
콩콩 웃으며 배포하러 가요! 🐰✨"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항인 isRouteFromTabBar 플래그 제거를 명확하게 설명하고 있으며, 이슈 #883과 연결되어 있습니다.
Description check ✅ Passed PR 설명이 알림 탭 진입 시 발생하는 문제 및 isRouteFromTabBar 플래그 제거를 통한 해결책을 관련 변경 사항과 함께 설명하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경 사항이 issue #883의 목적인 '딥링크 진입 시 탭바로 이동하는 방식으로 수정'을 충족합니다. isRouteFromTabBar 제거, DeepLinkComponents에 targetTap 추가, ApplicationCoordinator에서 탭 전환 로직 추가 등이 요구사항을 모두 구현했습니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 issue #883의 딥링크 탭바 진입 방식 수정이라는 범위 내에 있으며, 관련 없는 변경은 발견되지 않습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/#883-deeplink-tabbar

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member

@yungu0010 yungu0010 left a comment

Choose a reason for hiding this comment

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

리뷰 확인 부탁드립니다~!
또한 콕찌르기 알림에서 뒤로가기를 할 경우 계속 네트워크 에러가 발생하고 있어 이 부분도 확인이 필요해보입니다..!

질문) 솝탬프는 알림탭을 통해 진입했을 때 바텀시트 형태로 나타나게 되는데 콕찌르기와 통일하지 않아도 되나요? 같은 앱 서비스인데 진입 경로가 다른 점이 UX적으로 어색하게 느껴져 질문드려요!(아직 논의되지 않은 사안이라면 앱팀 회의 때 전달해주시면 감사하겠습니다!)

Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-05-20.at.07.17.54.mov

Comment thread SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift Outdated
case .deepLink(let url):
self.notificationHandler.receive(deepLink: url)
guard let deepLink = self.notificationHandler.deepLink.value else { return }
guard let deepLink = self.notificationHandler.deepLink.value else { return }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

의미없는 공백은 지워주세요!

Suggested change
guard let deepLink = self.notificationHandler.deepLink.value else { return }
guard let deepLink = self.notificationHandler.deepLink.value else { return }

Comment on lines +26 to +27
coordinator.runTabBarFlow(initSelectedTabType: .poke)
return coordinator.runPokeFlow()
Copy link
Copy Markdown
Member

@yungu0010 yungu0010 May 19, 2026

Choose a reason for hiding this comment

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

runTabBarFlow 내에서 이미 runPokeFlow를 호출하고 있기 때문에 return coordiantor.runPokeFlow()는 중복 호출로 보여집니다!

1. 알림탭 > 콕찌르기 탭 진입을 n회 했을 때

해당 플로우를 실행할 때 마다 HomeVCSoptlogVC가 재생성되어 중복 인스턴스가 생기고 있음을 확인했습니다.

Image Image
2026-05-20.7.00.12.mov

누수가 발생하고 있어 runTabBarFlow로 모든 VC를 재생성하기 보다 탭 바 인덱스만 교체해주는 것이 좋을 것 같아요.

솝트앱은 탭바가 자주 변경되는 환경인만큼 탭바 인덱스로 접근하는 대신 TabBarItemType를 사용해서 전환하는 것이 유지보수 측면에서 좋을 것 같습니다.

2. isDestination 분기처리 제거

isDestination/poke가 딥링크의 목적지인 경우를 나타내는데, 이제는 /poke가 딥링크의 목적지이든, 지금처럼 /poke/콕찌르기 상세가 딥링크의 목적지이든 상관없이 탭바로 띄워주기 때문에 isDestination을 통한 분기처리도 필요 없을 것 같아요.

PokeNotificationListDeepLink가 PokeCoordinator를 필요로 하는군요..! 그런데 runPokeFlow()를 호출해버리면 start 메소드가 불필요하게 호출되니까,, ApplicationCoordinatorPokeCoordinator를 저장하고 coordinator.pokeCoordinator를 반환해야할 것 같습니다..
혹시 더 좋은 방법이 있을까요?

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.

1. 알림탭 -> 콕찌르기 진입 시 메모리 누수 해결

기존 메모리 누수의 원인이었던runPokeFlow()호출하는 부분을 제거하고 딥링크를 받아오는 시점에 딥링크의 첫번째 요소를 검사하고 탭 정보를 객체에 주입하도록 코드를 수정했어요 .
현재는 모든 경우에 탭 인덱스를 바꾸는게 필요한 상황은 아닌지라 콕찌르기 부분만 반영을 했는데, 이 부분은 회의때 원래의 기획이 어땠는지 다시 물어봐야할 것 같아요.

deepLinkData 생성 시점에 진입이 필요한 탭 정보를 옵셔널로 주입합니다. 현재는 콕찌르기가 아니라면 기존과 동일하게 동작합니다.

    private func parseDeepLink(with deepLink: String?) {
        guard let deepLink else { return }
        
        do {
            let deepLinkData = try deepLinkParser.parse(with: deepLink)
            let targetTap = resolveTargetTap(deepLinkData: deepLinkData)
                        
            let deepLinkComponents = DeepLinkComponents(deepLinkData: deepLinkData, targetTap: targetTap)
            self.deepLink.send(deepLinkComponents)
        } catch {
            self.handleLinkError(error: error)
        }
    }

ApplicationCoordinator 에서 화면 전환을 수행하기 전에 딥링크 객체에서 전환이 필요한 탭 정보를 확인하고, 필요한 경우 tabBarController 의 인덱스를 변경

    private func handleNewDeepLink(deepLink: DeepLinkComponentsExecutable) {
        self.rootNavigationController.popToRootViewController(animated: false)
        if let targetTap = deepLink.targetTap {
            tabBarController?.selectedIndex = targetTap.getTabIndex(userType: UserDefaultKeyList.Auth.getUserType())
        }
        deepLink.execute(coordinator: self)
    }

실제 딥링크를 실행하고 화면 전환은 코디네이터에서 이뤄지기 때문에 탭 전환도 ApplicationCoordinator 에서 받아온 딥링크로 tabBarController 인덱스를 변경하도록 했어요. 혹시 방식이 이상하거나 좋은 방법이 있다면 추가적으로 코멘트 달아주세요.

2. isDestination 분기처리

/poke 가 목적지라는게 알림을 통해서 들어갔을떄 콕찌르기 화면이 바로 나타나는 경우를 말씀하시는 걸까요? 이 부분은 제가 자세하게 이해를 못했는데, 그렇다면 만약 다른 알림또한 탭을 통해서 진입을 한다면 ApplicationCoordinator 가 다른 코디네이터도 전부 소유를 하도록 하는걸까요?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

1. 탭 정보 주입

ApplicationCoordinator에서 딥링크에 따라 탭바 인덱스를 수정하려고 DeepLinkComponentsExecutable, DeepLinkComponents, NotificationHandler에도 탭바 인덱스에 대한 정보를 넘겨주는 것으로 보입니다.. !

그런데 왜 /poke/notification-list 등의 딥링크일 때 ApplicationCoordinator에서 탭바를 전환해야하는지 궁금해요.
지금 딥링크 구조는 /를 단위로 파싱하여서 OODeepLink.swift에서 단계별로 화면전환을 수행하고, 그 다음(/ 다음에 오는 딥링크 컴포넌트)에 필요한 코디네이터를 반환 -> 파싱된 다음 딥링크 컴포넌트에서 같은 과정을 수행하는 것으로 이해했습니다.

그러면 ApplicationCoordinator에서는 구체적인 동작을 알 필요 없이 deepLink.excute()만 호출하고
실질적인 탭바 인덱스 전환은 PokeDeepLink에서 수정해주면 훨씬 더 단순할 것 같아요.

import Core

public struct PokeDeepLink: DeepLinkExecutable {
    public let name = "poke"
    public let children: [DeepLinkExecutable] = [PokeNotificationListDeepLink()]
    public var isDestination: Bool = false

    public func execute(with coordinator: Coordinator, queryItems: [URLQueryItem]?) -> Coordinator? {
        guard let coordinator = coordinator as? ApplicationCoordinator else { return nil }
        
        let userType = UserDefaultKeyList.Auth.getUserType()
        coordinator.tabBarController?.selectedIndex = TabBarItemType.poke.getTabIndex(userType: userType)
        
        if self.isDestination == true {
            return coordinator
        }
        return coordinator.runPokeFlow()
    }
}

2. isDestination 분기처리

/poke는 딥링크의 최종 목적지가 /poke이기 때문에 콕찌르기 메인만 띄워주면 되는 상황입니다. 그 경우가 isDestination == true인 경우예요!

만약 다른 알림또한 탭을 통해서 진입을 한다면 ApplicationCoordinator 가 다른 코디네이터도 전부 소유를 하도록 하는걸까요?

이 말은 콕찌르기 외에 솝탬프나 솝트로그 등을 의미하는 걸까요? 만약 탭바에 있는 화면들이 딥링크로 도착하게 된다면 HomeCoordinator, StampCoordinator, PokeCoordinator, SoplotCoordinator 모두 ApplicationCoodinator가 소유하는 방향으로 수정되어야할 것 같습니다.
현재 구조는 탭바가 도입되기 전의 구조이고 딥링크에서는 탭바를 사용하지 않기 때문에 문제가 되지 않았지만, 콕찌르기와 같이 딥링크에서도 탭바를 통해 진입해야한다면 Coordinator.start()를 호출할 이유가 없다고 생각합니다!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@SOPT-iOS/Projects/Features/RootFeature/Sources/NotificationHelpers/NotificationHandler.swift`:
- Around line 94-97: The fallback deep link builder makeComponentsForEmptyLink
currently hardcodes targetTap: .poke while parsing "home/notification/detail",
which can misalign path vs tab; change it to derive the targetTap from the
parsed deepLinkData (or pass nil) when constructing DeepLinkComponents so the
tab matches the deepLinkParser.parse result (use deepLinkData's tab property if
present instead of always using .poke).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b30e4b0-e4ce-49dc-a1cb-cf41715ef2b6

📥 Commits

Reviewing files that changed from the base of the PR and between ef2af58 and c8ae69d.

📒 Files selected for processing (8)
  • SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/DeepLink/DeepLinkComponentsExecutable.swift
  • SOPT-iOS/Projects/Features/PokeFeature/Interface/Sources/PokeMainPresentable.swift
  • SOPT-iOS/Projects/Features/PokeFeature/Sources/Coordinator/LegacyPokeCoordinator.swift
  • SOPT-iOS/Projects/Features/PokeFeature/Sources/PokeMainScene/ViewModel/PokeMainViewModel.swift
  • SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift
  • SOPT-iOS/Projects/Features/RootFeature/Sources/DeepLinks/PokeDeepLink.swift
  • SOPT-iOS/Projects/Features/RootFeature/Sources/NotificationHelpers/DeepLinkComponents.swift
  • SOPT-iOS/Projects/Features/RootFeature/Sources/NotificationHelpers/NotificationHandler.swift
💤 Files with no reviewable changes (2)
  • SOPT-iOS/Projects/Features/PokeFeature/Interface/Sources/PokeMainPresentable.swift
  • SOPT-iOS/Projects/Features/PokeFeature/Sources/PokeMainScene/ViewModel/PokeMainViewModel.swift

Comment on lines 94 to 97
private func makeComponentsForEmptyLink(notificationId: String) -> DeepLinkComponents? {
let deepLinkData = try? deepLinkParser.parse(with: "home/notification/detail?id=\(notificationId)")
return DeepLinkComponents(deepLinkData: deepLinkData)
return DeepLinkComponents(deepLinkData: deepLinkData, targetTap: .poke)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

빈 링크 fallback의 탭 지정이 딥링크 경로와 불일치합니다.

Line 95에서 home/notification/detail을 만들고 Line 96에서 targetTap: .poke를 고정하면, 실행 전에 포크 탭으로 강제 전환되어 경로/탭이 어긋날 수 있습니다. deepLinkData에서 계산한 탭(또는 nil)을 쓰는 방식으로 맞춰주세요.

수정 제안
 private func makeComponentsForEmptyLink(notificationId: String) -> DeepLinkComponents? {
-    let deepLinkData = try? deepLinkParser.parse(with: "home/notification/detail?id=\(notificationId)")
-    return DeepLinkComponents(deepLinkData: deepLinkData, targetTap: .poke)
+    guard let deepLinkData = try? deepLinkParser.parse(with: "home/notification/detail?id=\(notificationId)") else {
+        return nil
+    }
+    let targetTap = resolveTargetTap(deepLinkData: deepLinkData)
+    return DeepLinkComponents(deepLinkData: deepLinkData, targetTap: targetTap)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@SOPT-iOS/Projects/Features/RootFeature/Sources/NotificationHelpers/NotificationHandler.swift`
around lines 94 - 97, The fallback deep link builder makeComponentsForEmptyLink
currently hardcodes targetTap: .poke while parsing "home/notification/detail",
which can misalign path vs tab; change it to derive the targetTap from the
parsed deepLinkData (or pass nil) when constructing DeepLinkComponents so the
tab matches the deepLinkParser.parse result (use deepLinkData's tab property if
present instead of always using .poke).


public struct PokeDeepLink: DeepLinkExecutable {
public let name = "poke"
public static let path = "poke"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

static let path로 변경하신 이유가 있나요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix 문제 해결, 코드 수정 석기🍙

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 알림 딥링크 진입 방식 수정

2 participants