-
Notifications
You must be signed in to change notification settings - Fork 17
[Feat]#731 - 딥링크 로직 구현 #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
Summary by CodeRabbit릴리스 노트
Walkthrough솝탬프 기능을 위한 새로운 딥링크 구조를 구현합니다. 부분 랭킹, 미션 목록, 미션 상세 조회 등의 딥링크 핸들러를 추가하고, 스탬프 코디네이터에 공개 래퍼 메서드를 추가하며, 홈 화면의 조사 버튼 동작을 테스트 딥링크로 변경합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant HomeCoordinator
participant RootCoordinator
participant StampCoordinator
participant DeepLinkHandler
App->>HomeCoordinator: onSurveyButtonTapped()
HomeCoordinator->>RootCoordinator: navigate(deepLink: test-url)
RootCoordinator->>DeepLinkHandler: execute(handler)
alt Config.coordinatorFlag == .new
DeepLinkHandler->>StampCoordinator: runRankingFlow(rankingViewType)
StampCoordinator->>App: Present Ranking View
else Config.coordinatorFlag == .legacy
DeepLinkHandler->>StampCoordinator: runRankingFlow(rankingViewType)
StampCoordinator->>App: Present Legacy Ranking View
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 새로운 딥링크 핸들러 4개가 유사한 구조를 따르지만, 각각 서로 다른 쿼리 파라미터 처리 로직과 코디네이터 메서드 호출을 포함합니다. Config.coordinatorFlag 분기 패턴이 반복되므로 리뷰 효율이 향상되지만, 각 핸들러의 파라미터 파싱 및 에러 처리 방식을 검증해야 합니다. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
SOPT-iOS/Projects/Features/HomeFeature/Sources/HomeScene/Coordinator/HomeCoordinator.swift(2 hunks)SOPT-iOS/Projects/Features/RootFeature/Sources/DeepLinks/SoptampDeepLink.swift(1 hunks)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampEntirePartRankingDeepLink.swift(1 hunks)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionDetailDeepLink.swift(1 hunks)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift(1 hunks)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampPartRankingDeepLink.swift(1 hunks)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/StampCoordinator.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift (4)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionDetailDeepLink.swift (1)
execute(20-41)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampPartRankingDeepLink.swift (1)
execute(20-43)SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Array+.swift (1)
getQueryValue(18-20)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/StampCoordinator.swift (1)
runOtherMissionList(102-104)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/StampCoordinator.swift (1)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/LegacyRankingCoordinator.swift (1)
showOtherMissionList(67-82)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionDetailDeepLink.swift (4)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift (1)
execute(20-36)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampPartRankingDeepLink.swift (1)
execute(20-43)SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Array+.swift (1)
getQueryValue(18-20)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/StampCoordinator.swift (1)
runMissionDetailById(106-114)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampPartRankingDeepLink.swift (4)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionDetailDeepLink.swift (1)
execute(20-41)SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift (1)
execute(20-36)SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Array+.swift (1)
getQueryValue(18-20)SOPT-iOS/Projects/Features/StampFeature/Interface/Sources/Part.swift (1)
uppercasedName(19-28)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampEntirePartRankingDeepLink.swift (1)
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampEntireRankingDeepLink.swift (1)
SoptampEntireRankingDeepLink(15-34)
SOPT-iOS/Projects/Features/HomeFeature/Sources/HomeScene/Coordinator/HomeCoordinator.swift (1)
SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator+Delegate.swift (1)
homeCoordinator(52-77)
🔇 Additional comments (7)
SOPT-iOS/Projects/Features/HomeFeature/Sources/HomeScene/Coordinator/HomeCoordinator.swift (1)
123-128: 테스트 코드 제거 필요TODO 주석에 명시된 대로, 딥링크 테스트 완료 후 이 코드를 제거해야 합니다. 프로덕션 배포 전에 원래의
onSurveyButtonTapped동작을 복원하거나 해당 기능을 완전히 제거해 주세요.SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/StampCoordinator.swift (1)
102-104: LGTM!기존 내부 메서드를 외부에 노출하는 깔끔한 래퍼 구현입니다.
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionDetailDeepLink.swift (1)
28-39: Legacy 경로에서 네비게이션 미구현
Config.coordinatorFlag가.legacy인 경우, coordinator 타입 검증만 수행하고 실제 네비게이션 메서드를 호출하지 않습니다. 반면.new경로는runMissionDetailById를 호출합니다.이것이 의도된 동작인지 확인해 주세요. Legacy 구현이 아직 필요하지 않다면 명시적인 주석을 추가하거나, 구현이 필요하다면 적절한 메서드 호출을 추가해야 합니다.
switch Config.coordinatorFlag { case .legacy: guard let coordinator = coordinator as? LegacyStampCoordinator else { return nil } + // TODO: Legacy 미션 디테일 네비게이션 구현 필요 + // coordinator.runMissionDetailById(missionId: missionId, username: nickname) case .new: guard let coordinator = coordinator as? StampCoordinator else { return nil } coordinator.runMissionDetailById(missionId: missionId, username: nickname) }SOPT-iOS/Projects/Features/RootFeature/Sources/DeepLinks/SoptampDeepLink.swift (1)
15-15: LGTM!새로운 파트 랭킹 딥링크를 children 배열에 적절히 추가했습니다.
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift (1)
28-34: Legacy 경로에서 네비게이션 미구현
SoptampMissionDetailDeepLink와 동일한 패턴으로,.legacy경로에서 coordinator 타입 검증만 수행하고runOtherMissionList를 호출하지 않습니다..new경로와 달리 실제 네비게이션이 발생하지 않습니다.Legacy 구현 계획을 확인하고, 필요시 메서드 호출을 추가하거나 TODO 주석을 추가해 주세요.
switch Config.coordinatorFlag { case .legacy: guard let coordinator = coordinator as? LegacyStampCoordinator else { return nil } + // TODO: Legacy 미션 리스트 네비게이션 구현 필요 + // coordinator.runOtherMissionList(username: username, sentence: sentence) case .new: guard let coordinator = coordinator as? StampCoordinator else { return nil } coordinator.runOtherMissionList(username: username, sentence: sentence) }SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampEntirePartRankingDeepLink.swift (1)
14-31: LGTM!Legacy와 new 경로 모두에서 적절히
runRankingFlow를 호출하고 있으며, 자식 딥링크 구조도 올바르게 설정되어 있습니다.SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampPartRankingDeepLink.swift (1)
20-43: LGTM!파트 파라미터 검증 및 에러 로깅이 잘 구현되어 있으며, legacy/new 경로 모두에서 일관되게 네비게이션을 수행합니다.
uppercasedName()을 사용한 대소문자 무시 비교도 적절합니다.
| // | ||
| // SoptampPartMissionListDeepLink.swift | ||
| // StampFeature | ||
| // | ||
| // Created by 성현주 on 10/21/25. | ||
| // Copyright © 2025 SOPT-iOS. All rights reserved. | ||
| // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파일명 불일치
헤더 주석의 파일명이 실제 파일명과 다릅니다.
- 주석:
SoptampPartMissionListDeepLink.swift - 실제:
SoptampMissionListDeepLink.swift
//
-// SoptampPartMissionListDeepLink.swift
+// SoptampMissionListDeepLink.swift
// StampFeature📝 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.
| // | |
| // SoptampPartMissionListDeepLink.swift | |
| // StampFeature | |
| // | |
| // Created by 성현주 on 10/21/25. | |
| // Copyright © 2025 SOPT-iOS. All rights reserved. | |
| // | |
| // | |
| // SoptampMissionListDeepLink.swift | |
| // StampFeature | |
| // | |
| // Created by 성현주 on 10/21/25. | |
| // Copyright © 2025 SOPT-iOS. All rights reserved. | |
| // |
🤖 Prompt for AI Agents
In
SOPT-iOS/Projects/Features/StampFeature/Sources/Coordinator/DeepLinks/SoptampMissionListDeepLink.swift
around lines 1 to 7, the header comment's filename
(SoptampPartMissionListDeepLink.swift) does not match the actual file name
(SoptampMissionListDeepLink.swift); update the header comment to use the correct
filename so the top-of-file comment matches the real file name exactly.
| public func runMissionDetailById(missionId: Int, username: String?) { | ||
| let model = MissionListModel( | ||
| id: missionId, | ||
| title: "", | ||
| level: 1, | ||
| isCompleted: true | ||
| ) | ||
| showMissionDetail(model, username) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
더미 데이터로 인한 잠재적 문제
MissionListModel을 하드코딩된 더미 값으로 생성하고 있습니다:
title: ""- UI에서 빈 제목이 표시될 수 있음level: 1- 실제 미션 레벨과 불일치할 수 있음 (Line 117에서 StarViewLevel 초기화에 사용됨)isCompleted: true- 다른 사용자의 미션인 경우 부정확할 수 있음
PR 설명에서 언급하신 대로, 미션 레벨 정보 누락 문제에 대한 다음 해결 방안을 검토해 주세요:
- 서버 응답에 레벨 정보 포함하도록 API 확장
- 딥링크 파라미터로 레벨 전달 (예:
level쿼리 파라미터 추가) - 미션 디테일 뷰에서 ID로 전체 정보를 다시 fetch
public func runMissionDetailById(missionId: Int, username: String?) {
+ // 옵션 1: 쿼리 파라미터에서 레벨 정보를 추가로 받기
+ // public func runMissionDetailById(missionId: Int, username: String?, level: Int?)
+
+ // 옵션 2: 미션 디테일 뷰에서 ID로 전체 정보 fetch 후 업데이트
let model = MissionListModel(
id: missionId,
- title: "",
- level: 1,
+ title: "", // TODO: Fetch from server or pass via query
+ level: 1, // TODO: Get actual level from query parameter
isCompleted: true
)
showMissionDetail(model, username)
}Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 적어주신 내용 확인했는데, 2로 가는 게 깔끔할 듯 ! 안드랑 논의하고 결정해주세요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dlwogus0128 넵, 알겠습니다! 딥링크 파라미터로 level 정보를 전달하는 방식(옵션 2)으로 안드로이드 팀과 협의하여 결정하시면 좋을 것 같습니다. 크로스 플랫폼 간 일관성 있는 딥링크 스펙을 맞추는 것이 중요하니 논의 후 구현 방향이 정해지면 더미 데이터 부분을 실제 값으로 대체해 주시면 되겠습니다! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
굿! 수고햇어요
| //TODO: - DeepLink 테스트이후 제거 | ||
| homeForMember.vm.onSurveyButtonTapped = { [weak self] _ in | ||
| guard let self else { return } | ||
| let testLink = "home/soptamp/entire-part-ranking/part-ranking/missions/missionDetail?part=ANDROID&nickname=안드박찬미&id=292&isMine=true" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
길긴 길다 ㅋㅋㅋㅋ
| } | ||
|
|
||
| guard let part = Part.allCases.first(where: { | ||
| $0.uppercasedName() == partString.uppercased() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 디테일하네요
| coordinator.runRankingFlow(rankingViewType: .individualRankingInPart(part: part)) | ||
| case .new: | ||
| guard let coordinator = coordinator as? StampCoordinator else { return nil } | ||
| coordinator.runRankingFlow(rankingViewType: .individualRankingInPart(part: part)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
분기까지 굿! 근데 이제 coordinatorFlag 들어가보면 알겠지만 legacy 쪽은 안 쓰긴 해요
| public func runMissionDetailById(missionId: Int, username: String?) { | ||
| let model = MissionListModel( | ||
| id: missionId, | ||
| title: "", | ||
| level: 1, | ||
| isCompleted: true | ||
| ) | ||
| showMissionDetail(model, username) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 적어주신 내용 확인했는데, 2로 가는 게 깔끔할 듯 ! 안드랑 논의하고 결정해주세요
|
별건 아니지만.. 담번엔 pr 제목 컨벤션 맞춰주세요! 진짜 사소하긴함 |
🌴 PR 요약
🌱 작업한 브랜치
🌱 PR Point
새롭게 추가된 노드
기존에는 인덱스가 깊은 케이스가 없어서 하나의 deeplink로 처리가 가능했지만, 더 깊은 뎁스의 요구사항때문에 필요한 노드를 추가했습니다.
SoptampEntirePartRankingDeepLink : 전채 파트 랭킹을 담당하는 노드입니다, 내부적으로 homeCoordinator의 runRankingFlow(rankingViewType: .partRanking) 메소드를 사용합니다
SoptampPartRankingDeepLink: 파트 내의 랭킹을 담당하는 노드입니다. 마찬가지로 내부적으로 홈코디네이터의 .runRankingFlow(rankingViewType: .individualRankingInPart(part: part)) 위 메소드를 사용합니다. 여기서 part는 deeplink의 파라미터를 사용합니다.
SoptampMissionListDeepLink: 사용자가 완료한 미션 리스트를 담당하는 노드입니다. 기존의 homecoordinator에서는 missionlist용 public 함수가 없어, 아래와 같이 구현해 사용중입니다.
📌 참고 사항
missionDetail뷰에서 isMine여부에 따라 하나의 뷰에서 분기처리 하고 있는걸로 알고 있습니다. 그러나 아직 구체적인 인터페이스가 없어서 뷰작업 완료이후 필요한 데이터를 전달 드리는 방식으로 구현하기 위해 아직 isMine은 별도 처리 안해뒀습니다. 뷰작업이후 뷰에 맞춰서 구현해두겠습니다!
현재 missionDetail뷰로 이동하는 경우에는 원래 함수의 시그니쳐인 func showMissionDetail(_ model: MissionListModel, _ username: String?)에 대해서 딥링크에서는 Model에 대한 정보를 얻을수 없다는 문제점이 있어, missionID만 전달받고, MissionListModel에 missionID를 제외한 나머지 필드에 빈값을 넣어주고 해당 mission ID로 뷰를 업데이트하도록해 구현해뒀습니다. 최대한 기존 함수 시그니처들을 유지하고 싶어 수정하지 않고 구현했는데 혹시 더 좋은 방법이 있다면 공유해주시면 감사드리겠습니당 ㅠ 🙇♂️
레거시 분기코드는 레거시가 전부 사라진뒤에 한번에 정리하기위해 똑같이 구현해두었습니다!
💡논의하고 싶은 이슈
이렇게 있을거 같아요. 개인적으로는 최대한 사이드 이펙트를 줄이고 깔끔하게 구현이 가능한 방식은 2번인거 같은데 이부분은 서버측, 안드측과도 논의가 필요할거 같아 의견이 궁금합니다.
//TODO: - DeepLink 테스트이후 제거 homeForMember.vm.onSurveyButtonTapped = { [weak self] _ in guard let self else { return } let testLink = "home/soptamp/entire-part-ranking/part-ranking/missions/missionDetail?part=ANDROID&nickname=안드박찬미&id=292&isMine=true" self.delegate?.homeCoordinator(self, to: .deepLink(url: testLink)) }📸 스크린샷
📮 관련 이슈