[feature] ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화#1830
Conversation
- src/components/common/MobileButtonArea로 이동 및 이름 변경 - children prop으로 일반화하여 AdminPage, ClubDetailPage 모두 사용 가능 - AdminPage 4곳 import 경로 및 컴포넌트명 업데이트 - 기존 MobileSaveButtonArea 삭제
…tonArea 적용 - ShareButton 및 hideShareButtonOnMobile 관련 로직 제거 - 모바일/태블릿에서 MobileButtonArea 기반 지원하기 버튼 렌더링 - 데스크탑은 기존 ApplyButton 스타일 유지
- 모바일/태블릿에서 ClubDetailFooterContainer 제거 (MobileButtonArea가 fixed로 처리) - 데스크탑에서만 sticky 컨테이너(흰 배경+border-top) 유지 - LegacyClubDetailPage의 불필요한 hideShareButtonOnMobile prop 제거
…및 데스크탑 버튼 크기 통일 - MobileButtonArea → FixedBottomButtonArea로 rename (투명 배경 영역 + 공통 Button 사용) - 데스크탑 기본: 517px × 60px (기존 ApplyButton과 동일) - 태블릿 이하: 100% × 50px, typography.paragraph.p2 적용 - ClubApplyButton 모바일/데스크탑 분기 제거 — FixedBottomButtonArea 단일 사용 - ClubDetailFooter 컨테이너(흰 배경+border-top) 완전 제거 및 styles 파일 삭제 - AdminPage 4곳 import 경로 업데이트
데스크탑에서 position fixed로 Footer와 겹치는 문제 수정 - 데스크탑: normal flow, Footer 아래에 자연스럽게 위치 - 태블릿 이하: position fixed, 화면 하단 고정
기존 ClubDetailFooterContainer와 동일하게 sticky 처리 - 데스크탑: position sticky, bottom 0 — Footer 아래 자연스럽게 위치하며 스크롤 시 하단 고정 - 태블릿 이하: position fixed 유지
- 태블릿(≤700px): max-width 500px 센터 정렬, 콘텐츠 영역 이탈 방지 - 모바일(≤500px): 전체 너비로 확장
- deadlineText 계산을 ClubApplyButton 내부로 이동 - ClubDetailFooter 컴포넌트 삭제 - ClubDetailPage, LegacyClubDetailPage에서 ClubApplyButton 직접 렌더링
- 미사용 ShareButton 컴포넌트 및 share_icon SVG 파일 삭제 - FloatingButtonGroup에서 클럽 상세 페이지 감지 후 클럽 특화 공유 로직 적용 - 클럽 외 페이지는 기존 window.location.href fallback 유지
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @suhyun113's task in 3m 11s —— View job 🍒 harry 리뷰
버튼 영역을 인라인으로 남긴 지적
총평에서만 짚는 부분 — 테스트 누락
전반적으로 통합 방향과 재사용은 좋아요. 위 항목만 확인해 주세요! 👍 |
Walkthrough관리자 모바일 저장 버튼을 Changes관리자 하단 저장 버튼 공통화
동아리 상세 하단 액션 개편
공통 푸터 스타일 갱신
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ClubDetailPage
participant ClubApplyButton
participant FixedBottomButtonArea
participant ApplicationSelectModal
ClubDetailPage->>ClubApplyButton: 지원 UI 렌더링
ClubApplyButton->>FixedBottomButtonArea: 버튼과 클릭 동작 전달
ClubApplyButton->>ApplicationSelectModal: 지원서 선택 모달 렌더링
Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
✅ UI 변경사항 없음
전체 78개 스토리 · 30개 컴포넌트 |
There was a problem hiding this comment.
Code Review
This pull request introduces a unified FixedBottomButtonArea component to standardize bottom-aligned buttons across admin and club detail pages, replaces the scroll-to-top button with a more versatile FloatingButtonGroup that includes page sharing, and extracts sharing logic into a reusable useShare hook. While these changes greatly improve code reuse and clean up obsolete components, there are two performance concerns: first, the globally rendered FloatingButtonGroup triggers unnecessary API requests on non-detail pages, which can be resolved by conditionally enabling the query; second, the scroll listener in useScrollTrigger causes redundant state updates on every scroll event, which should be optimized by only updating the state when the scroll direction actually changes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const clubIdentifier = | ||
| matchByName?.params.clubName ?? matchById?.params.clubId; | ||
|
|
||
| const { data: clubDetail } = useGetClubDetail(clubIdentifier ?? ''); |
There was a problem hiding this comment.
🐢 성능 이슈: 모든 페이지에서 불필요한 API 요청 발생
FloatingButtonGroup은 App.tsx (36라인)에서 전역으로 렌더링되고 있습니다. 즉, 메인 페이지, 어드민 페이지, 목록 페이지 등 애플리케이션의 모든 페이지에서 이 컴포넌트가 마운트됩니다.
하지만 clubIdentifier는 동아리 상세 페이지에서만 정의되므로, 그 외의 모든 페이지에서는 clubIdentifier가 undefined가 되어 페이지 이동 시마다 useGetClubDetail('')이 빈 문자열로 호출되는 불필요한 API 요청이 발생합니다.
이를 방지하기 위해 clubIdentifier가 존재할 때만 쿼리가 활성화되도록 enabled 옵션을 추가하는 것을 권장합니다.
const { data: clubDetail } = useGetClubDetail(clubIdentifier ?? '', {
enabled: !!clubIdentifier,
});
| return shouldShowButton; | ||
| }); | ||
|
|
||
| setIsScrollingUp(scrollingUp && scrollY > threshold); |
There was a problem hiding this comment.
⚡ 성능 최적화: 스크롤 시 불필요한 상태 업데이트 방지
스크롤 이벤트는 매우 빈번하게 발생합니다. 스크롤할 때마다 setIsScrollingUp을 매번 호출하면 isScrollingUp 값이 변경되지 않았을 때도(예: 계속 아래로 스크롤하여 계속 false인 상태) 상태 업데이트가 예약되어 성능에 영향을 줄 수 있습니다.
setIsTriggered와 유사하게, 이전 값과 비교하여 실제로 값이 변경되었을 때만 상태를 업데이트하도록 함수형 업데이트 패턴을 적용하여 최적화하는 것이 좋습니다.
const nextScrollingUp = scrollingUp && scrollY > threshold;
setIsScrollingUp((prev) => (prev === nextScrollingUp ? prev : nextScrollingUp));| }; | ||
|
|
||
| const recruitmentStatus = clubDetail?.recruitmentStatus; | ||
| const recruitmentStatus = clubDetail.recruitmentStatus; |
There was a problem hiding this comment.
이번 변경에서 쿼리 데이터 접근이 clubDetail?.id ?? '' → clubDetail.id / clubDetail.recruitmentStatus / clubDetail.recruitmentStart 처럼 논-널 직접 접근으로 바뀌었어요. 30번째 줄에 if (!clubId || !clubDetail) return null; 가드가 있어서 타입상으로는 문제가 없지만, 저희는 React Compiler 전제를 두고 있어서 렌더 경로에서 읽는 쿼리 상태는 옵셔널 체이닝(clubDetail?.recruitmentStatus)으로 접근하는 걸 규칙으로 삼고 있어요. 컴파일러가 의존성 읽기를 가드 위로 끌어올려 로딩 중 크래시가 날 여지가 있어서예요.
이벤트 핸들러 내부(handleApplyButtonClick 등)의 접근은 가드 이후에만 실행되니 괜찮지만, 90~98번째 줄의 렌더 경로 읽기(recruitmentStatus, deadlineText 계산의 recruitmentStart/End)는 옵셔널 체이닝으로 바꿔 주시는 게 좋아요.
| export const ButtonArea = styled.div` | ||
| position: sticky; | ||
| bottom: 0; | ||
| display: flex; | ||
| justify-content: center; | ||
| padding: 10px 20px 20px; | ||
| z-index: ${Z_INDEX.clubDetailFooter}; | ||
|
|
||
| button { | ||
| width: 517px; |
There was a problem hiding this comment.
이 컴포넌트가 어드민 저장 버튼(ClubInfoEditTabMobile, LinkEditPage 등)과 동아리 상세 지원 버튼 양쪽에 공통으로 쓰이는데, 기본(=데스크탑 >700px) 스타일이 position: sticky; button width 517px; height 60px로 동아리 상세 데스크탑 기준이에요. 기존 MobileSaveButtonArea는 모든 폭에서 position: fixed; max-width: 500px; height: 50px였고요.
어드민 모바일 편집 탭이 웹뷰/모바일에서만 렌더된다면 이 데스크탑 분기는 안 타서 문제없지만, 혹시 700px 초과 폭에서도 노출될 수 있다면 저장 버튼이 517px sticky로 바뀌어요. 어드민 쪽 데스크탑 분기까지 의도한 동작인지 한 번 확인해 주시면 좋아요. (컴포넌트 이름은 Fixed인데 기본값이 sticky라 살짝 헷갈리는 점도 같이 봐주세요.)
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)
30-30: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
@clubName기반 라우트에서 버튼 렌더링이 누락되는 크리티컬 버그 수정새로 추가된 이름 기반 라우트(
/clubDetail/@:clubName)를 통해 페이지에 접근할 경우,useParams에서clubId는undefined가 됩니다. 결과적으로!clubId조건이true로 평가되어 컴포넌트가null을 조기 반환하므로 지원하기 버튼이 아예 화면에 나타나지 않게 됩니다.
clubId또는clubName중 하나라도 존재할 때 정상 동작하도록 검증 로직을 수정해야 합니다.🐛 제안하는 수정안
- if (!clubId || !clubDetail) return null; + if (!(clubId || clubName) || !clubDetail) return null;🤖 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 `@frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx` at line 30, Update the early-return guard in ClubApplyButton so it returns null only when both clubId and clubName from useParams are absent, while still requiring clubDetail. This must allow the button to render for either ID-based or `@clubName-based` routes.
🧹 Nitpick comments (2)
frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts (1)
7-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
z-index상수명 변경 고려이번 PR을 통해
ClubDetailFooter가 제거되고 공통 컴포넌트인FixedBottomButtonArea로 통합 및 일반화되었습니다. 이에 맞춰Z_INDEX.clubDetailFooter라는 네이밍도Z_INDEX.fixedBottomButtonArea등 범용적인 이름으로 변경하는 것을 고려해 보세요.🤖 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 `@frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts` around lines 7 - 13, Rename the Z_INDEX.clubDetailFooter constant to a generic name such as fixedBottomButtonArea, and update ButtonArea plus all references to use the new symbol consistently.frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx (1)
20-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff전역 컴포넌트 내 페이지 종속적인 API 호출 분리 고려
FloatingButtonGroup은App.tsx최상단에 렌더링되어 모든 페이지에서 공통으로 사용되는 컴포넌트임에도 불구하고, 특정 페이지(동아리 상세)에 종속적인 라우트 파싱 로직(useMatch)과 데이터 페칭(useGetClubDetail)을 직접 호출하고 있습니다.이러한 구조는 동아리 상세 페이지가 아닌 일반 화면에서도 불필요한 의존성 평가를 발생시키며, 컴포넌트 간 결합도를 높입니다. 향후 다른 페이지(예: 공지사항, 행사 상세 등)에서도 공유 기능이 필요해질 경우 컴포넌트가 무거워질 수 있습니다.
단기적으로는 현재 코드도 동작하지만, 향후 공유에 필요한 메타데이터(
title,url등)를 Context나 전역 상태(Zustand 등)를 통해 주입받도록 개선하여 컴포넌트의 범용성을 높이는 것을 권장합니다.🤖 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 `@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx` around lines 20 - 43, Decouple FloatingButtonGroup from club-detail-specific routing and fetching by removing its direct useMatch and useGetClubDetail dependencies. Provide the share metadata (title, text, and url, including club details when applicable) through an existing context or global state mechanism, and update handlePageShare to consume that injected metadata while preserving the current generic-page fallback.
🤖 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 `@frontend/docs/features/club-detail/apply-button.md`:
- Around line 5-12: Update the ClubApplyButton documentation to include
LegacyClubDetailPage in the component tree, state that deadline text is
calculated inside ClubApplyButton via recruitmentDateParser and getDeadlineText
rather than useGetClubDetail, and correct the status text behavior: normal
recruitment displays D-n or D-Day, while ALWAYS displays only 지원하기.
- Line 7: 문서의 코드 펜스에 언어 식별자가 없어 MD040 경고가 발생합니다. 해당 Markdown 코드 펜스를 `text` 언어
식별자를 포함하도록 수정하세요.
In `@frontend/src/hooks/useShare.ts`:
- Around line 22-26: Update the shareData object in the useShare hook to include
title and url alongside text, using the existing share context values, so
navigator.share receives complete link metadata.
---
Outside diff comments:
In
`@frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx`:
- Line 30: Update the early-return guard in ClubApplyButton so it returns null
only when both clubId and clubName from useParams are absent, while still
requiring clubDetail. This must allow the button to render for either ID-based
or `@clubName-based` routes.
---
Nitpick comments:
In
`@frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts`:
- Around line 7-13: Rename the Z_INDEX.clubDetailFooter constant to a generic
name such as fixedBottomButtonArea, and update ButtonArea plus all references to
use the new symbol consistently.
In `@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx`:
- Around line 20-43: Decouple FloatingButtonGroup from club-detail-specific
routing and fetching by removing its direct useMatch and useGetClubDetail
dependencies. Provide the share metadata (title, text, and url, including club
details when applicable) through an existing context or global state mechanism,
and update handlePageShare to consume that injected metadata while preserving
the current generic-page fallback.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6f52dc8f-7778-4b4f-a677-1ca94ca939a0
⛔ Files ignored due to path filters (5)
frontend/src/assets/images/icons/scroll_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/scroll_to_top_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_floating_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_icon_mobile.svgis excluded by!**/*.svg
📒 Files selected for processing (27)
frontend/docs/features/admin/info/mobile.mdfrontend/docs/features/admin/intro/mobile.mdfrontend/docs/features/club-detail/apply-button.mdfrontend/docs/features/components/FixedBottomButtonArea.mdfrontend/src/App.tsxfrontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.stories.tsxfrontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.tsfrontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.tsxfrontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.tsfrontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsxfrontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsxfrontend/src/hooks/Scroll/useScrollTrigger.tsfrontend/src/hooks/useShare.tsfrontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.tsfrontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTabMobile.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/FreeTagEditPage/FreeTagEditPage.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/LinkEditPage/LinkEditPage.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/LegacyClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
💤 Files with no reviewable changes (8)
- frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.tsx
- frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts
- frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
- frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
- frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
- frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts
- frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
- frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
| `ClubApplyButton`이 지원하기 흐름 전체를 담당한다. 별도 래퍼 없이 `ClubDetailPage`, `LegacyClubDetailPage`에서 직접 호출. | ||
|
|
||
| ``` | ||
| ClubDetailPage | ||
| └── <ClubApplyButton /> | ||
| ├── useGetClubDetail — clubDetail fetch (recruitmentStatus, deadlineText 계산 포함) | ||
| ├── FixedBottomButtonArea — 반응형 하단 고정 영역 | ||
| └── ApplicationSelectModal — 지원서 선택 모달 |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
문서의 컴포넌트 책임과 상태 문구를 실제 구현에 맞춰주세요.
- Line [8]-[12]의 트리에는
LegacyClubDetailPage가 누락되어 있지만, Line [5]에서는 두 페이지가 직접 호출한다고 설명합니다. - Line [10]의
useGetClubDetail이deadlineText까지 계산한다는 설명은 부정확합니다. 실제 계산은ClubApplyButton내부의recruitmentDateParser와getDeadlineText호출에서 수행됩니다. - Line [18]의
OPEN → "지원하기 | 마감일"도 실제 동작과 다릅니다. 일반 모집 상태에서는D-n/D-Day가 표시되고,ALWAYS에서는 지원하기만 표시됩니다.
관련 구현인 frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx:32-134와 frontend/src/utils/getDeadLineText.ts:11-38에 맞춰 문서를 갱신해주세요.
Also applies to: 17-20
🧰 Tools
🪛 markdownlint-cli2 (0.23.0)
[warning] 7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@frontend/docs/features/club-detail/apply-button.md` around lines 5 - 12,
Update the ClubApplyButton documentation to include LegacyClubDetailPage in the
component tree, state that deadline text is calculated inside ClubApplyButton
via recruitmentDateParser and getDeadlineText rather than useGetClubDetail, and
correct the status text behavior: normal recruitment displays D-n or D-Day,
while ALWAYS displays only 지원하기.
|
|
||
| `ClubApplyButton`이 지원하기 흐름 전체를 담당한다. 별도 래퍼 없이 `ClubDetailPage`, `LegacyClubDetailPage`에서 직접 호출. | ||
|
|
||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
코드 펜스에 언어 식별자를 추가해주세요.
Markdown lint의 MD040 경고를 해소할 수 있도록 ```text처럼 언어를 명시해주세요.
수정 예시
-```
+```text🧰 Tools
🪛 markdownlint-cli2 (0.23.0)
[warning] 7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@frontend/docs/features/club-detail/apply-button.md` at line 7, 문서의 코드 펜스에 언어
식별자가 없어 MD040 경고가 발생합니다. 해당 Markdown 코드 펜스를 `text` 언어 식별자를 포함하도록 수정하세요.
Source: Linters/SAST tools
| const shareData = { text }; | ||
|
|
||
| if (isMobile && navigator.share && !isRNWebView) { | ||
| try { | ||
| await navigator.share(shareData); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
navigator.share에 title과 url 포함하기
shareData에 text 외에도 title과 url을 포함시키면, 모바일 기기의 운영체제 기본 공유 인터페이스에서 리치 링크 프리뷰(Rich Link Preview)를 생성하거나 공유 앱(예: 메일)에 제목을 자동으로 채우는 등 더 나은 사용자 경험을 제공할 수 있습니다.
💡 제안하는 수정안
- const shareData = { text };
+ const shareData = { title, text, 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.
| const shareData = { text }; | |
| if (isMobile && navigator.share && !isRNWebView) { | |
| try { | |
| await navigator.share(shareData); | |
| const shareData = { title, text, url }; | |
| if (isMobile && navigator.share && !isRNWebView) { | |
| try { | |
| await navigator.share(shareData); |
🤖 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 `@frontend/src/hooks/useShare.ts` around lines 22 - 26, Update the shareData
object in the useShare hook to include title and url alongside text, using the
existing share context values, so navigator.share receives complete link
metadata.
- 위로 버튼: bounceUp keyframe (커진 상태로 위아래 무한 반복) - 공유 버튼: wiggle keyframe (커진 상태로 좌우 흔들림 1회) - 클릭 시 scale(1.08) 눌림 피드백 - $variant prop으로 버튼별 애니메이션 분기
- 위로 버튼: bounceUp keyframe (커진 상태로 위아래 무한 반복) - 공유 버튼: wiggle keyframe (커진 상태로 좌우 흔들림 1회) - 클릭 시 scale(1.08) 눌림 피드백 - $variant prop으로 버튼별 애니메이션 분기
seongwon030
left a comment
There was a problem hiding this comment.
혹시 ~~Group으로 컴포넌트명을 설정한 의도가 있나요?
- src/components/common/MobileButtonArea로 이동 및 이름 변경 - children prop으로 일반화하여 AdminPage, ClubDetailPage 모두 사용 가능 - AdminPage 4곳 import 경로 및 컴포넌트명 업데이트 - 기존 MobileSaveButtonArea 삭제
…tonArea 적용 - ShareButton 및 hideShareButtonOnMobile 관련 로직 제거 - 모바일/태블릿에서 MobileButtonArea 기반 지원하기 버튼 렌더링 - 데스크탑은 기존 ApplyButton 스타일 유지
- 모바일/태블릿에서 ClubDetailFooterContainer 제거 (MobileButtonArea가 fixed로 처리) - 데스크탑에서만 sticky 컨테이너(흰 배경+border-top) 유지 - LegacyClubDetailPage의 불필요한 hideShareButtonOnMobile prop 제거
…및 데스크탑 버튼 크기 통일 - MobileButtonArea → FixedBottomButtonArea로 rename (투명 배경 영역 + 공통 Button 사용) - 데스크탑 기본: 517px × 60px (기존 ApplyButton과 동일) - 태블릿 이하: 100% × 50px, typography.paragraph.p2 적용 - ClubApplyButton 모바일/데스크탑 분기 제거 — FixedBottomButtonArea 단일 사용 - ClubDetailFooter 컨테이너(흰 배경+border-top) 완전 제거 및 styles 파일 삭제 - AdminPage 4곳 import 경로 업데이트
데스크탑에서 position fixed로 Footer와 겹치는 문제 수정 - 데스크탑: normal flow, Footer 아래에 자연스럽게 위치 - 태블릿 이하: position fixed, 화면 하단 고정
기존 ClubDetailFooterContainer와 동일하게 sticky 처리 - 데스크탑: position sticky, bottom 0 — Footer 아래 자연스럽게 위치하며 스크롤 시 하단 고정 - 태블릿 이하: position fixed 유지
- 태블릿(≤700px): max-width 500px 센터 정렬, 콘텐츠 영역 이탈 방지 - 모바일(≤500px): 전체 너비로 확장
- deadlineText 계산을 ClubApplyButton 내부로 이동 - ClubDetailFooter 컴포넌트 삭제 - ClubDetailPage, LegacyClubDetailPage에서 ClubApplyButton 직접 렌더링
- 미사용 ShareButton 컴포넌트 및 share_icon SVG 파일 삭제 - FloatingButtonGroup에서 클럽 상세 페이지 감지 후 클럽 특화 공유 로직 적용 - 클럽 외 페이지는 기존 window.location.href fallback 유지
- desktop: 흰 배경, box-shadow 0 0 14px rgba(0,0,0,0.16), padding 10px 0 24px, border-radius 14px - tablet/mobile: background transparent, box-shadow none 초기화
- FooterContainer 배경 #F5F5F5(gray[100]) 추가 - PolicyLink 색상 #787878 → #818181 - 이메일 링크 색상 #aaa → #818181
…-1039' of https://github.com/Moadong/moadong into feature/#1829-club-apply-button-unify-mobile-button-MOA-1039
| }, | ||
| showShare: { | ||
| control: 'boolean', | ||
| description: '공유 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)', |
There was a problem hiding this comment.
이번 커밋에서 실제 컴포넌트의 공유 버튼은 $isVisible={true}로 바뀌어 항상 표시되는데, 이 설명은 여전히 "위로 스크롤할 때 표시"로 남아 있어 실제 동작과 어긋나요. 새로 추가한 스토리가 오해를 주지 않도록 설명을 실제 동작에 맞춰 주는 게 좋아요.
| description: '공유 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)', | |
| description: '공유 버튼 표시 여부 (실제 컴포넌트에서는 항상 표시됩니다.)', |
| argTypes: { | ||
| showScrollToTop: { | ||
| control: 'boolean', | ||
| description: '위로 이동 버튼 표시 여부 (스크롤을 내렸을 때 표시됩니다.)', |
There was a problem hiding this comment.
실제 컴포넌트에서 위로 이동 버튼은 $isVisible={isScrollingUp}, 즉 위로 스크롤할 때 표시돼요. 그런데 이 설명은 "스크롤을 내렸을 때 표시"로 반대로 적혀 있어요. 새 스토리 문서가 실제 동작과 반대로 안내되지 않도록 방향을 맞춰 주세요.
| description: '위로 이동 버튼 표시 여부 (스크롤을 내렸을 때 표시됩니다.)', | |
| description: '위로 이동 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)', |
|
|
||
| export const FloatingButtonGroup = () => { | ||
| const { isTriggered: isScrolledDown, isScrollingUp } = useScrollTrigger(); | ||
| const { isTriggered: isScrollingUp } = useScrollTrigger(); |
There was a problem hiding this comment.
isTriggered를 isScrollingUp으로 alias 했는데, 이 값은 useScrollTrigger의 기본값(direction='down') 기준으로 "임계값 이상 스크롤 내려간 상태"를 의미해요. 이름은 "위로 스크롤 중"인데 실제 의미는 반대라 헷갈리기 쉬워요. 게다가 훅에는 진짜 isScrollingUp 값이 따로 있어서 더 오해를 부를 수 있어요.
의도가 "스크롤을 내리면 위로 가기 버튼을 노출"하는 거라면, 이전처럼 isScrolledDown 같은 이름으로 alias 하는 게 의미가 더 명확해요.
| const { isTriggered: isScrollingUp } = useScrollTrigger(); | |
| const { isTriggered: isScrolledDown } = useScrollTrigger(); |
(이 경우 아래 $isVisible={isScrollingUp}도 isScrolledDown으로 함께 바꿔 주세요.)
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx (1)
30-31: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win공유 URL 생성 시 동아리 이름 URL 인코딩 적용
동아리 이름(
clubDetail.name)에 공백이나 특수 문자가 포함될 경우 잘못된 형식의 URL이 생성되어 공유 링크가 정상적으로 작동하지 않을 수 있습니다. 안전한 딥링크/공유 링크 생성을 위해encodeURIComponent를 적용하는 것을 권장합니다.💡 제안하는 수정
- const url = `${MOADONG_BASE_URL}@${clubDetail.name}`; + const url = `${MOADONG_BASE_URL}@${encodeURIComponent(clubDetail.name)}`;🤖 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 `@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx` around lines 30 - 31, Update URL construction in the FloatingButtonGroup share flow to apply encodeURIComponent to clubDetail.name before interpolating it into the deep link. Keep the displayed sharing text using the original club name while ensuring the URL segment is safely encoded.
🤖 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.
Outside diff comments:
In `@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx`:
- Around line 30-31: Update URL construction in the FloatingButtonGroup share
flow to apply encodeURIComponent to clubDetail.name before interpolating it into
the deep link. Keep the displayed sharing text using the original club name
while ensuring the URL segment is safely encoded.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5a5f4ae3-855b-48d5-9f4a-d941f8e2d4f5
📒 Files selected for processing (5)
frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.tsfrontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsxfrontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.tsfrontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsxfrontend/src/components/common/Footer/Footer.styles.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts
버튼 2개를 함께 담는 컴포넌트라 그렇게 설정했는데 다른 이름이 더 좋을까요? |
작업 내용
MobileSaveButtonArea(어드민 전용)와ClubDetailFooter(동아리 상세 전용)를 제거하고FixedBottomButtonArea공통 컴포넌트로 통합ShareButton을ClubApplyButton에서 제거하고FloatingButtonGroup의 공유 버튼으로 통합deadlineText계산을ClubDetailFooter→ClubApplyButton내부로 이동FloatingButtonGroup의 공유 버튼에 동아리 상세 페이지 전용 공유 로직 추가 (useMatch기반)FixedBottomButtonArea 브레이크포인/트별 동작
sticky; bottom: 0fixed; bottom: 0fixed; bottom: 0스크린샷
스토리북
👉스토리북
변경 범위
src/components/common/FixedBottomButtonArea/— 신규 공통 컴포넌트src/components/common/FloatingButtonGroup/— 공유 로직 추가src/pages/AdminPage/—MobileSaveButtonArea→FixedBottomButtonArea교체 (4곳)src/pages/ClubDetailPage/—ClubDetailFooter,ShareButton제거 및ClubApplyButton직접 사용src/pages/ClubDetailPage/components/ClubApplyButton/—deadlineText계산 내부화Summary by CodeRabbit
새로운 기능
스타일 개선
문서