Skip to content

[fix] 웹뷰 네비게이션 뒤로가기 버그 수정 및 useNavigator 개선#1507

Merged
oesnuj merged 5 commits into
develop-fefrom
fix/webview-navigation
May 3, 2026
Merged

[fix] 웹뷰 네비게이션 뒤로가기 버그 수정 및 useNavigator 개선#1507
oesnuj merged 5 commits into
develop-fefrom
fix/webview-navigation

Conversation

@oesnuj
Copy link
Copy Markdown
Member

@oesnuj oesnuj commented May 3, 2026

Summary

  • 웹뷰에서 홍보 게시판 카드 클릭 시 React Router 내부 이동 → requestNavigateWebview로 앱 위임
  • isFestival 카드도 동일하게 처리
  • itms-apps:// 링크가 웹뷰에서 무시되던 버그 수정
  • useNavigator에 내부 경로 웹뷰 분기 통합 (Banner, PromotionCard 중복 코드 제거)

Root Cause

웹뷰 내부에서 React Router로 이동하면 앱 네이티브 스택에 화면이 쌓이지 않아
스와이프 뒤로가기 및 TopBar 뒤로가기 불가

앱 측 작업 필요

ui/home/home-webview-screen.tsx handleMessage 함수 내 NAVIGATE_WEBVIEW에 아래 케이스 추가필요

} else if (payload.slug?.startsWith('promotions/')) {
  router.push({ pathname: '/webview/[slug]', params: { slug: 'promotions', path: `/${payload.slug}` } });
}

Test plan

  • 웹뷰에서 홍보 카드 클릭 → 상세 페이지 이동 확인 (앱 배포 후)
  • 상세 페이지에서 스와이프 뒤로가기 → 목록 복귀 확인
  • 일반 웹에서 카드 클릭 정상 동작 확인
  • 앱스토어 링크(itms-apps://) 클릭 정상 동작 확인

Summary by CodeRabbit

릴리스 노트

  • 버그 수정

    • 악의적인 프로토콜 URL에 대한 검증 강화로 보안 개선
    • 웹뷰 환경에서 링크 처리 로직 최적화
  • 테스트

    • 네비게이션 기능의 테스트 커버리지 확대 및 개선

oesnuj and others added 4 commits May 3, 2026 18:13
React Router 내부 이동 대신 requestNavigateWebview로 앱에 위임해
스와이프 뒤로가기가 동작하지 않던 문제 수정.
isFestival 카드도 동일하게 처리.
useNavigator에서 내부 경로(/로 시작)도 웹뷰 여부에 따라
requestNavigateWebview로 자동 위임하도록 확장.
Banner, PromotionCard에서 isInAppWebView + requestNavigateWebview
직접 호출 제거.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
isInAppWebView 중복 호출 제거, early return으로 분기 평탄화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 웹뷰에서 외부 링크 → requestOpenExternalUrl 위임
- 웹뷰에서 itms-apps:// → window.open 폴백
- 웹뷰에서 내부 경로 → requestNavigateWebview slug 변환

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@oesnuj oesnuj requested review from seongwon030 and suhyun113 May 3, 2026 09:31
@oesnuj oesnuj self-assigned this May 3, 2026
@oesnuj oesnuj added 💻 FE Frontend 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정 labels May 3, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, Comment May 3, 2026 9:37am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 3, 2026

Warning

Rate limit exceeded

@oesnuj has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 54 minutes and 8 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2de38885-088b-4cff-bd89-bf0f50416cd9

📥 Commits

Reviewing files that changed from the base of the PR and between 74d9542 and dc76248.

📒 Files selected for processing (2)
  • frontend/src/hooks/useNavigator.test.ts
  • frontend/src/hooks/useNavigator.ts

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

useNavigator 훅은 링크 처리 로직을 중앙화하여 웹뷰와 일반 웹 환경을 구분하고, 프로토콜 검증 및 URL 라우팅을 통합합니다. 관련 컴포넌트들은 직접 useNavigate 대신 새로운 훅을 사용하도록 업데이트되었습니다.

Changes

네비게이션 로직 통합 및 웹뷰 위임

Layer / File(s) Summary
유틸리티 및 가드 로직
frontend/src/hooks/useNavigator.ts
toSlug 헬퍼 함수 추가. javascript:, data:, vbscript: 프로토콜 조기 반환 가드 구현. inWebview 계산 중앙화.
훅 코어 로직
frontend/src/hooks/useNavigator.ts
외부 URL(http, https, itms-apps)은 웹뷰 환경에서 requestOpenExternalUrl 위임, 실패 시 window.open 폴백. 일반 웹에서는 window.location.href 할당. 내부 경로는 웹뷰에서 requestNavigateWebview로 위임, 일반 웹에서는 navigate 호출.
컴포넌트 통합
frontend/src/pages/MainPage/components/Banner/Banner.tsx, frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx, frontend/src/pages/PromotionPage/components/list/PromotionCard/PromotionCard.tsx
useNavigate 제거하고 useNavigator 훅으로 교체. Banner는 isWebview prop으로 페이지네이션 렌더링 조건 변경. 컴포넌트들은 handleLink를 통해 통일된 네비게이션 호출.
테스트 확장
frontend/src/hooks/useNavigator.test.ts
웹뷰 모드(mockIsInAppWebView 설정)와 일반 웹 모드 분리. 악성 프로토콜 파라미터화된 테스트 추가. 외부/내부 URL 및 웹뷰 위임 동작 검증.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

🔨 Refactor, ✅ Test

Suggested reviewers

  • seongwon030
  • suhyun113
  • lepitaaar
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 요약하고 있습니다. 웹뷰 네비게이션 버그 수정 및 useNavigator 개선이라는 핵심 목표와 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/webview-navigation

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
Review rate limit: 0/1 reviews remaining, refill in 54 minutes and 8 seconds.

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

@oesnuj oesnuj changed the title fix(PromotionCard): 웹뷰에서 카드 클릭 시 앱 네이티브 스택으로 이동 [fix] 웹뷰 네비게이션 뒤로가기 버그 수정 및 useNavigator 개선 May 3, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

🎨 UI 변경사항을 확인해주세요

변경된 스토리를 Chromatic에서 확인해주세요.

구분 링크
🔍 변경사항 리뷰 https://www.chromatic.com/build?appId=67904e61c16daa99a63b44a7&number=284
📖 Storybook https://67904e61c16daa99a63b44a7-zyyeaymplb.chromatic.com/

9개 스토리 변경 · 전체 56개 스토리 · 22개 컴포넌트

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/hooks/useNavigator.ts`:
- Around line 20-25: The isExternal detection in useNavigator.ts uses
/^(https?|itms-apps):\/\// which is case-sensitive and misclassifies schemes
like "HTTPS://" or "ITMS-APPS://"; update the isExternal check (the regex or the
input) to be case-insensitive (e.g., add the i flag to the regex or normalize
trimmedUrl to lowercase before testing) so variables inWebview and isExternal
behave correctly for all-cased schemes and navigation branches (window.open /
window.location.href / requestOpenExternalUrl).
- Around line 29-30: The webview bridge call
requestNavigateWebview(toSlug(trimmedUrl)) can return false on failure but its
result is ignored; update the logic in the useNavigator hook so that you capture
the return value and, if it is false (or otherwise indicates a failure), call
navigate(trimmedUrl) as a fallback; ensure this change targets the branch that
currently does if (inWebview) requestNavigateWebview(...) else navigate(...),
replacing the simple call with a conditional that falls back to navigate when
requestNavigateWebview fails.

In
`@frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx`:
- Around line 24-28: The CTA currently calls
requestNavigateWebview(`club/${clubId}`) without checking its result, so if the
webview bridge call fails the click does nothing; update the click handling
around isInAppWebView() to await/inspect requestNavigateWebview's return (or
catch its rejection) and on failure fall back to calling
handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`); locate this logic in
PromotionClubCTA (symbols: isInAppWebView, requestNavigateWebview, handleLink,
clubId, clubName) and ensure the fallback runs when the bridge returns a falsy
value or throws.
🪄 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: 8e174ba8-5ff0-441c-ae2b-3e98f0df15af

📥 Commits

Reviewing files that changed from the base of the PR and between e61bce3 and 74d9542.

📒 Files selected for processing (5)
  • frontend/src/hooks/useNavigator.test.ts
  • frontend/src/hooks/useNavigator.ts
  • frontend/src/pages/MainPage/components/Banner/Banner.tsx
  • frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx
  • frontend/src/pages/PromotionPage/components/list/PromotionCard/PromotionCard.tsx

Comment on lines +20 to +25
const inWebview = isInAppWebView();
const isExternal = /^(https?|itms-apps):\/\//.test(trimmedUrl);

const isDangerousProtocol = /^(javascript|data|vbscript):/i.test(
trimmedUrl,
);
if (isDangerousProtocol) return;

const isExternalUrl = /^(https?|itms-apps):\/\//.test(trimmedUrl);

if (isExternalUrl) {
// 웹뷰에서 window.location.href로 외부 URL을 열면 WebView 자체가 이동해버리므로 앱에 위임.
// requestOpenExternalUrl은 http/https만 허용하므로, itms-apps:// 등 비표준 스킴은 false를
// 반환 → window.open으로 폴백해 OS가 처리하도록 위임
if (isInAppWebView()) {
if (!requestOpenExternalUrl(trimmedUrl)) {
window.open(trimmedUrl);
}
} else {
window.location.href = trimmedUrl;
}
} else {
navigate(trimmedUrl);
if (isExternal) {
if (inWebview && !requestOpenExternalUrl(trimmedUrl)) window.open(trimmedUrl);
else if (!inWebview) window.location.href = trimmedUrl;
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

외부 URL 스킴 판별은 대소문자를 무시해야 합니다.

지금 정규식은 HTTPS://...ITMS-APPS://...를 내부 경로로 분류합니다. 그러면 일반 웹에서는 React Router로, 웹뷰에서는 NAVIGATE_WEBVIEW로 잘못 보내지게 됩니다.

수정 예시
-      const isExternal = /^(https?|itms-apps):\/\//.test(trimmedUrl);
+      const isExternal = /^(https?|itms-apps):\/\//i.test(trimmedUrl);
📝 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
const inWebview = isInAppWebView();
const isExternal = /^(https?|itms-apps):\/\//.test(trimmedUrl);
const isDangerousProtocol = /^(javascript|data|vbscript):/i.test(
trimmedUrl,
);
if (isDangerousProtocol) return;
const isExternalUrl = /^(https?|itms-apps):\/\//.test(trimmedUrl);
if (isExternalUrl) {
// 웹뷰에서 window.location.href로 외부 URL을 열면 WebView 자체가 이동해버리므로 앱에 위임.
// requestOpenExternalUrl은 http/https만 허용하므로, itms-apps:// 등 비표준 스킴은 false를
// 반환 → window.open으로 폴백해 OS가 처리하도록 위임
if (isInAppWebView()) {
if (!requestOpenExternalUrl(trimmedUrl)) {
window.open(trimmedUrl);
}
} else {
window.location.href = trimmedUrl;
}
} else {
navigate(trimmedUrl);
if (isExternal) {
if (inWebview && !requestOpenExternalUrl(trimmedUrl)) window.open(trimmedUrl);
else if (!inWebview) window.location.href = trimmedUrl;
const inWebview = isInAppWebView();
const isExternal = /^(https?|itms-apps):\/\//i.test(trimmedUrl);
if (isExternal) {
if (inWebview && !requestOpenExternalUrl(trimmedUrl)) window.open(trimmedUrl);
else if (!inWebview) window.location.href = trimmedUrl;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useNavigator.ts` around lines 20 - 25, The isExternal
detection in useNavigator.ts uses /^(https?|itms-apps):\/\// which is
case-sensitive and misclassifies schemes like "HTTPS://" or "ITMS-APPS://";
update the isExternal check (the regex or the input) to be case-insensitive
(e.g., add the i flag to the regex or normalize trimmedUrl to lowercase before
testing) so variables inWebview and isExternal behave correctly for all-cased
schemes and navigation branches (window.open / window.location.href /
requestOpenExternalUrl).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

타당하지 않습니다. 적용 불필요합니다.

URL 스킴은 RFC 3986 기준으로 대소문자를 구분하지 않지만, 실제로 HTTPS:// 같은 대문자 URL을 입력하는 경우는 없습니다. 브라우저, 앱, 백엔드 모두 소문자 스킴으로 URL을 생성하고, 사용자가 직접 입력하는 경우도 거의 없습니다.

이미 위의 위험 프로토콜 차단 정규식(line 18)에는 i 플래그가 있는 것도 실제 XSS 공격 벡터를 막기 위한 것이고, 외부 URL 판별은 그런 위협이 없습니다. 과잉 방어입니다.

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.

@oesnuj, 말씀하신 내용 이해했습니다. 실제 환경에서 대문자 스킴이 사용되는 경우가 없고, 위험 프로토콜 차단과는 위협 모델이 다르다는 점에서 타당한 판단입니다. 제안을 철회하겠습니다.

Comment on lines +29 to +30
if (inWebview) requestNavigateWebview(toSlug(trimmedUrl));
else navigate(trimmedUrl);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 3, 2026

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

웹뷰 브리지 실패 시 내부 링크가 무반응이 됩니다.

requestNavigateWebview()는 브리지 전송 실패 시 false를 돌려주는데, 현재는 그 값을 무시하고 있습니다. postMessage가 실패하면 프로모션/페스티벌 이동이 그대로 죽으니 웹 라우팅으로라도 폴백하는 편이 안전합니다.

수정 예시
-      if (inWebview) requestNavigateWebview(toSlug(trimmedUrl));
-      else navigate(trimmedUrl);
+      if (inWebview) {
+        const delegated = requestNavigateWebview(toSlug(trimmedUrl));
+        if (!delegated) navigate(trimmedUrl);
+        return;
+      }
+
+      navigate(trimmedUrl);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useNavigator.ts` around lines 29 - 30, The webview bridge
call requestNavigateWebview(toSlug(trimmedUrl)) can return false on failure but
its result is ignored; update the logic in the useNavigator hook so that you
capture the return value and, if it is false (or otherwise indicates a failure),
call navigate(trimmedUrl) as a fallback; ensure this change targets the branch
that currently does if (inWebview) requestNavigateWebview(...) else
navigate(...), replacing the simple call with a conditional that falls back to
navigate when requestNavigateWebview fails.

Copy link
Copy Markdown
Member Author

@oesnuj oesnuj May 3, 2026

Choose a reason for hiding this comment

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

requestNavigateWebview는 이미 inWebview === true인 브랜치 안에서 호출됩니다.

이 함수가 false를 반환하는 유일한 경우는 isInAppWebView()가 false일 때인데, 해당 브랜치에 진입했다는 것 자체가 그 조건을 이미 통과했음을 의미합니다.
브릿지 전송 실패 시 navigate(trimmedUrl)로 폴백해도 웹뷰 환경에서 React Router 이동은 앱 네이티브 스택에 화면이 쌓이지 않는 근본 문제를 그대로 가집니다.

의미 없는 폴백을 추가하는 것은 오히려 잘못된 동작을 정상처럼 보이게 만듭니다.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and display the webviewBridge utility
fd -i "webviewBridge" --type f

Length of output: 36


🏁 Script executed:

#!/bin/bash
cat frontend/src/utils/webviewBridge.ts

Length of output: 4110


@oesnuj, 피드백 감사합니다. 코드를 확인했습니다.

첫 번째 주장에 대해 한 가지 짚고 싶은 점이 있습니다. postMessageToApptry/catch 블록을 보면, window.ReactNativeWebView?.postMessage() 호출 자체가 예외를 던지는 경우에도 false를 반환하는 경로가 존재합니다. 즉, isInAppWebView() === true인 브랜치 안에서도 이론상 false 반환은 가능합니다.

그러나 두 번째 주장이 결정적입니다. postMessage가 실패한 상황에서 navigate(trimmedUrl)로 폴백하더라도, 웹뷰 환경에서 React Router 이동은 앱 네이티브 스택에 화면을 쌓지 못한다는 근본 문제가 그대로 남습니다. 결과적으로 해당 폴백은 오류를 정상 동작처럼 보이게 만드는 오해의 소지가 있으며, 실질적인 UX 개선도 없습니다.

지적하신 내용이 맞습니다. 리뷰 코멘트를 철회하겠습니다.


✏️ Learnings added
Learnt from: oesnuj
Repo: Moadong/moadong PR: 1507
File: frontend/src/hooks/useNavigator.ts:30-31
Timestamp: 2026-05-03T09:41:11.455Z
Learning: In `frontend/src/hooks/useNavigator.ts`, the `requestNavigateWebview` call is intentionally inside the `if (inWebview)` guard. A React Router `navigate()` fallback on bridge failure is not meaningful because React Router navigation inside an in-app webview does not push screens onto the native app stack — that is the exact problem the webview bridge is designed to solve. Adding such a fallback would make broken behavior appear normal.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +24 to +28
// 웹뷰는 club/id 기반 slug, 일반 웹은 clubName 기반 경로 사용
if (isInAppWebView()) {
requestNavigateWebview(`club/${clubId}`);
} else {
navigate(`/clubDetail/@${encodeURIComponent(clubName)}`);
handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 3, 2026

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

CTA도 웹뷰 브리지 실패 시 그대로 죽습니다.

여기서는 requestNavigateWebview('club/...')의 반환값을 확인하지 않아서, 브리지 전송이 실패하면 버튼 클릭이 무반응이 됩니다. 실패 시에는 웹 경로로 폴백해 두는 게 안전합니다.

수정 예시
-    if (isInAppWebView()) {
-      requestNavigateWebview(`club/${clubId}`);
-    } else {
-      handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`);
-    }
+    if (isInAppWebView() && requestNavigateWebview(`club/${clubId}`)) {
+      return;
+    }
+
+    handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`);
📝 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
// 웹뷰는 club/id 기반 slug, 일반 웹은 clubName 기반 경로 사용
if (isInAppWebView()) {
requestNavigateWebview(`club/${clubId}`);
} else {
navigate(`/clubDetail/@${encodeURIComponent(clubName)}`);
handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`);
// 웹뷰는 club/id 기반 slug, 일반 웹은 clubName 기반 경로 사용
if (isInAppWebView() && requestNavigateWebview(`club/${clubId}`)) {
return;
}
handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx`
around lines 24 - 28, The CTA currently calls
requestNavigateWebview(`club/${clubId}`) without checking its result, so if the
webview bridge call fails the click does nothing; update the click handling
around isInAppWebView() to await/inspect requestNavigateWebview's return (or
catch its rejection) and on failure fall back to calling
handleLink(`/clubDetail/@${encodeURIComponent(clubName)}`); locate this logic in
PromotionClubCTA (symbols: isInAppWebView, requestNavigateWebview, handleLink,
clubId, clubName) and ensure the fallback runs when the bridge returns a falsy
value or throws.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

실패 시 handleLink('/clubDetail/@clubname')으로 폴백하면 useNavigator 내부에서 다시 inWebview === true 브랜치로 진입해 requestNavigateWebview를 재호출하는 무한루프가 발생합니다.
브릿지가 실패한 환경에서 동일한 브릿지 경로를 다시 타는 폴백은 성립하지 않습니다.

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.

@oesnuj, 지적이 정확합니다. /clubDetail/@clubName은 외부 URL(http/https/itms-apps)이 아니라 내부 경로이기 때문에, handleLink 내부에서 isInAppWebView() === true 브랜치로 다시 진입해 requestNavigateWebview를 재호출하는 순환이 발생합니다. 제 제안이 잘못되었습니다.

현재 구조(isInAppWebView() → requestNavigateWebview, 그 외 → handleLink)는 올바른 패턴이며, 웹뷰 브리지 실패 시 동일 브리지 경로로의 폴백은 의미가 없습니다. 제 코멘트는 철회합니다.

(∩`-´)⊃━☆゚.*・。゚


✏️ Learnings added
Learnt from: oesnuj
Repo: Moadong/moadong PR: 1507
File: frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx:24-28
Timestamp: 2026-05-03T09:40:26.989Z
Learning: In `frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx`, the `PromotionClubCTA` component intentionally keeps a manual `isInAppWebView()` guard rather than delegating to `handleLink` (from `useNavigator`) for the webview branch. This is because `handleLink` internally re-checks `isInAppWebView()` and would re-enter the webview branch for internal paths, causing an infinite loop if used as a fallback after a bridge failure. The pattern `if (isInAppWebView()) { requestNavigateWebview(...) } else { handleLink(...) }` is correct and intentional here.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

React router는 스택이 쌓이지 않군요 빠른 수정 좋습니다!

@oesnuj oesnuj merged commit 9cf9669 into develop-fe May 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants