Skip to content

[feature] ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화#1830

Open
suhyun113 wants to merge 37 commits into
feature/#1826-scroll-floating-buttons-MOA-1038from
feature/#1829-club-apply-button-unify-mobile-button-MOA-1039
Open

[feature] ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화#1830
suhyun113 wants to merge 37 commits into
feature/#1826-scroll-floating-buttons-MOA-1038from
feature/#1829-club-apply-button-unify-mobile-button-MOA-1039

Conversation

@suhyun113

@suhyun113 suhyun113 commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

작업 내용

  • MobileSaveButtonArea(어드민 전용)와 ClubDetailFooter(동아리 상세 전용)를 제거하고 FixedBottomButtonArea 공통 컴포넌트로 통합
  • ShareButtonClubApplyButton에서 제거하고 FloatingButtonGroup의 공유 버튼으로 통합
  • deadlineText 계산을 ClubDetailFooterClubApplyButton 내부로 이동
  • FloatingButtonGroup의 공유 버튼에 동아리 상세 페이지 전용 공유 로직 추가 (useMatch 기반)

FixedBottomButtonArea 브레이크포인/트별 동작

환경 position 너비
데스크탑 (>700px) sticky; bottom: 0 버튼 517px, 중앙 정렬
태블릿 (≤700px) fixed; bottom: 0 max-width 500px, 중앙 정렬
모바일 (≤500px) fixed; bottom: 0 전체 너비

스크린샷

Before After
Mobile image image
Desktop image image

스토리북

👉스토리북

변경 범위

  • src/components/common/FixedBottomButtonArea/ — 신규 공통 컴포넌트
  • src/components/common/FloatingButtonGroup/ — 공유 로직 추가
  • src/pages/AdminPage/MobileSaveButtonAreaFixedBottomButtonArea 교체 (4곳)
  • src/pages/ClubDetailPage/ClubDetailFooter, ShareButton 제거 및 ClubApplyButton 직접 사용
  • src/pages/ClubDetailPage/components/ClubApplyButton/deadlineText 계산 내부화

Summary by CodeRabbit

  • 새로운 기능

    • 관리자 편집 화면과 동아리 상세 페이지에 반응형 하단 고정 버튼 영역을 적용했습니다.
    • 동아리 상세 페이지의 지원하기 버튼에서 모집 상태, 지원서 선택, 외부 지원서 이동을 지원합니다.
    • 위로 가기 및 공유 기능을 제공하는 부동 버튼 그룹을 추가했습니다.
    • 관련 UI를 Storybook에서 확인할 수 있습니다.
  • 스타일 개선

    • 부동 버튼 위치와 크기, 호버·클릭 효과를 개선했습니다.
    • 푸터 배경 및 텍스트 색상을 조정했습니다.
  • 문서

    • 하단 고정 버튼 영역과 지원하기 버튼의 동작 및 사용처를 문서화했습니다.

suhyun113 added 11 commits July 15, 2026 00:35
- 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 유지
@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, Comment Jul 19, 2026 3:03pm

@github-actions github-actions Bot added ✨ Feature 기능 개발 💻 FE Frontend labels Jul 14, 2026
@reviewer-harry

reviewer-harry Bot commented Jul 14, 2026

Copy link
Copy Markdown

Claude finished @suhyun113's task in 3m 11s —— View job


🍒 harry 리뷰

  • 리뷰 기준(.github/harry-review-rules.md) 확인
  • PR 전체 diff 분석
  • 주요 변경 파일 상세 확인
  • 인라인 코멘트 + 총평 작성

버튼 영역을 FixedBottomButtonArea로 통합하고 공유 로직을 useShare 훅으로 추출한 방향은 깔끔해요. useGetClubDetail(enabled: !!clubParam) 같은 기존 쿼리 훅을 그대로 재사용하고, deadlineText 계산도 기존 getDeadlineText/recruitmentDateParser 유틸을 재활용한 점 좋았어요. 다만 몇 가지만 확인 부탁드려요.

인라인으로 남긴 지적

  1. ClubApplyButton.tsx — 쿼리 데이터를 clubDetail?.id ?? ''clubDetail.id로 논-널 직접 접근으로 바꾼 부분이 있어요. 가드가 있어도 React Compiler 전제상 렌더 경로는 옵셔널 체이닝을 권장해요.
  2. FixedBottomButtonArea.styles.ts — 어드민 저장 버튼과 상세 지원 버튼에 공통으로 쓰이는데, 기본(데스크탑) 스타일이 상세 페이지 기준(sticky·517px)이에요. 어드민 쪽 데스크탑 분기가 의도된 동작인지 확인 부탁드려요.

총평에서만 짚는 부분 — 테스트 누락

  • 신규 useShare 훅은 RN 웹뷰 / 모바일 navigator.share / 클립보드 폴백으로 분기가 나뉘는 로직이 있는데 대응 테스트가 없어요. 분기 로직이라 회귀가 나기 쉬운 곳이니 최소한의 케이스라도 추가해 주시면 좋아요.
  • useScrollTriggerisScrollingUp 파생 상태가 추가됐는데, 스크롤 방향 판정 로직도 테스트가 있으면 안정적이에요.

전반적으로 통합 방향과 재사용은 좋아요. 위 항목만 확인해 주세요! 👍
· feature/#1829-club-apply-button-unify-mobile-button-MOA-1039

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

관리자 모바일 저장 버튼을 FixedBottomButtonArea로 통합하고, 동아리 상세 지원 버튼과 공유·스크롤 액션을 공통 하단 및 플로팅 컴포넌트로 재구성했다. 관련 문서, Storybook 스토리, Footer 색상 스타일도 갱신했다.

Changes

관리자 하단 저장 버튼 공통화

Layer / File(s) Summary
FixedBottomButtonArea 컴포넌트와 스타일
frontend/src/components/common/FixedBottomButtonArea/*
공통 버튼 컴포넌트와 데스크톱·태블릿·모바일 반응형 스타일, 비활성 상태, Storybook 스토리를 추가했다.
관리자 편집 화면 저장 버튼 교체
frontend/src/pages/AdminPage/tabs/..., frontend/docs/features/admin/*
세 모바일 편집 화면의 저장 버튼을 FixedBottomButtonArea로 변경하고 관련 문서의 컴포넌트명과 경로를 갱신했다.

동아리 상세 하단 액션 개편

Layer / File(s) Summary
지원 버튼과 하단 푸터 통합
frontend/src/pages/ClubDetailPage/..., frontend/docs/features/club-detail/apply-button.md
ClubApplyButton이 지원 상태·지원서 분기와 FixedBottomButtonArea 렌더링을 직접 담당하도록 변경하고, 상세 페이지의 ClubDetailFooter와 기존 공유 버튼 구현을 제거했다.
플로팅 버튼과 공유 흐름
frontend/src/components/common/FloatingButtonGroup/*
스크롤 기반 위로 이동 버튼과 클럽 상세 공유 버튼을 추가하고, 공유 URL 생성 및 Mixpanel 이벤트 추적을 연결했다.

공통 푸터 스타일 갱신

Layer / File(s) Summary
Footer 색상 스타일 변경
frontend/src/components/common/Footer/Footer.styles.ts
Footer 배경과 정책·이메일 링크 색상을 변경했다.

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: 지원서 선택 모달 렌더링
Loading

Possibly related issues

Possibly related PRs

  • Moadong/moadong#605ClubApplyButton의 props 제거와 지원 흐름 변경이 같은 컴포넌트에 연결된다.
  • Moadong/moadong#1306ClubApplyButtonclubDetail.id 기반 지원서 조회·라우팅 변경과 맞물린다.
  • Moadong/moadong#1828FloatingButtonGroup의 공유 및 스크롤 동작을 직접 수정한다.

Suggested labels: 🔨 Refactor

Suggested reviewers: seongwon030, lepitaaar, oesnuj

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed ShareButton 제거와 MobileSaveButtonArea 공통화라는 주요 변경을 잘 요약해 PR 내용과 대체로 일치합니다.
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.
✨ 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 feature/#1829-club-apply-button-unify-mobile-button-MOA-1039

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.

@github-actions

Copy link
Copy Markdown

✅ UI 변경사항 없음

구분 링크
📖 Storybook https://67904e61c16daa99a63b44a7-qdlbrihqny.chromatic.com/

전체 78개 스토리 · 30개 컴포넌트

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 ?? '');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

🐢 성능 이슈: 모든 페이지에서 불필요한 API 요청 발생

FloatingButtonGroupApp.tsx (36라인)에서 전역으로 렌더링되고 있습니다. 즉, 메인 페이지, 어드민 페이지, 목록 페이지 등 애플리케이션의 모든 페이지에서 이 컴포넌트가 마운트됩니다.

하지만 clubIdentifier는 동아리 상세 페이지에서만 정의되므로, 그 외의 모든 페이지에서는 clubIdentifierundefined가 되어 페이지 이동 시마다 useGetClubDetail('')이 빈 문자열로 호출되는 불필요한 API 요청이 발생합니다.

이를 방지하기 위해 clubIdentifier가 존재할 때만 쿼리가 활성화되도록 enabled 옵션을 추가하는 것을 권장합니다.

  const { data: clubDetail } = useGetClubDetail(clubIdentifier ?? '', {
    enabled: !!clubIdentifier,
  });

return shouldShowButton;
});

setIsScrollingUp(scrollingUp && scrollY > threshold);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

⚡ 성능 최적화: 스크롤 시 불필요한 상태 업데이트 방지

스크롤 이벤트는 매우 빈번하게 발생합니다. 스크롤할 때마다 setIsScrollingUp을 매번 호출하면 isScrollingUp 값이 변경되지 않았을 때도(예: 계속 아래로 스크롤하여 계속 false인 상태) 상태 업데이트가 예약되어 성능에 영향을 줄 수 있습니다.

setIsTriggered와 유사하게, 이전 값과 비교하여 실제로 값이 변경되었을 때만 상태를 업데이트하도록 함수형 업데이트 패턴을 적용하여 최적화하는 것이 좋습니다.

    const nextScrollingUp = scrollingUp && scrollY > threshold;
    setIsScrollingUp((prev) => (prev === nextScrollingUp ? prev : nextScrollingUp));

};

const recruitmentStatus = clubDetail?.recruitmentStatus;
const recruitmentStatus = clubDetail.recruitmentStatus;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이번 변경에서 쿼리 데이터 접근이 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)는 옵셔널 체이닝으로 바꿔 주시는 게 좋아요.

Comment on lines +7 to +16
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 컴포넌트가 어드민 저장 버튼(ClubInfoEditTabMobile, LinkEditPage 등)과 동아리 상세 지원 버튼 양쪽에 공통으로 쓰이는데, 기본(=데스크탑 >700px) 스타일이 position: sticky; button width 517px; height 60px로 동아리 상세 데스크탑 기준이에요. 기존 MobileSaveButtonArea는 모든 폭에서 position: fixed; max-width: 500px; height: 50px였고요.

어드민 모바일 편집 탭이 웹뷰/모바일에서만 렌더된다면 이 데스크탑 분기는 안 타서 문제없지만, 혹시 700px 초과 폭에서도 노출될 수 있다면 저장 버튼이 517px sticky로 바뀌어요. 어드민 쪽 데스크탑 분기까지 의도한 동작인지 한 번 확인해 주시면 좋아요. (컴포넌트 이름은 Fixed인데 기본값이 sticky라 살짝 헷갈리는 점도 같이 봐주세요.)

@coderabbitai coderabbitai Bot left a comment

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.

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에서 clubIdundefined가 됩니다. 결과적으로 !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 호출 분리 고려

FloatingButtonGroupApp.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

📥 Commits

Reviewing files that changed from the base of the PR and between 51cac31 and 7c1b74f.

⛔ Files ignored due to path filters (5)
  • frontend/src/assets/images/icons/scroll_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/scroll_to_top_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/share_floating_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/share_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/share_icon_mobile.svg is excluded by !**/*.svg
📒 Files selected for processing (27)
  • frontend/docs/features/admin/info/mobile.md
  • frontend/docs/features/admin/intro/mobile.md
  • frontend/docs/features/club-detail/apply-button.md
  • frontend/docs/features/components/FixedBottomButtonArea.md
  • frontend/src/App.tsx
  • frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.stories.tsx
  • frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts
  • frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.tsx
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
  • frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
  • frontend/src/hooks/Scroll/useScrollTrigger.ts
  • frontend/src/hooks/useShare.ts
  • frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts
  • frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/FreeTagEditPage/FreeTagEditPage.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/LinkEditPage/LinkEditPage.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/LegacyClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
  • frontend/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

Comment on lines +5 to +12
`ClubApplyButton`이 지원하기 흐름 전체를 담당한다. 별도 래퍼 없이 `ClubDetailPage`, `LegacyClubDetailPage`에서 직접 호출.

```
ClubDetailPage
└── <ClubApplyButton />
├── useGetClubDetail — clubDetail fetch (recruitmentStatus, deadlineText 계산 포함)
├── FixedBottomButtonArea — 반응형 하단 고정 영역
└── ApplicationSelectModal — 지원서 선택 모달

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

문서의 컴포넌트 책임과 상태 문구를 실제 구현에 맞춰주세요.

  • Line [8]-[12]의 트리에는 LegacyClubDetailPage가 누락되어 있지만, Line [5]에서는 두 페이지가 직접 호출한다고 설명합니다.
  • Line [10]의 useGetClubDetaildeadlineText까지 계산한다는 설명은 부정확합니다. 실제 계산은 ClubApplyButton 내부의 recruitmentDateParsergetDeadlineText 호출에서 수행됩니다.
  • Line [18]의 OPEN → "지원하기 | 마감일"도 실제 동작과 다릅니다. 일반 모집 상태에서는 D-n/D-Day가 표시되고, ALWAYS에서는 지원하기만 표시됩니다.

관련 구현인 frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx:32-134frontend/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`에서 직접 호출.

```

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.

📐 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

Comment on lines +22 to +26
const shareData = { text };

if (isMobile && navigator.share && !isRNWebView) {
try {
await navigator.share(shareData);

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

navigator.sharetitleurl 포함하기

shareDatatext 외에도 titleurl을 포함시키면, 모바일 기기의 운영체제 기본 공유 인터페이스에서 리치 링크 프리뷰(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.

Suggested change
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.

@suhyun113 suhyun113 changed the title feat(club-detail): 지원하기 버튼 영역 통합 및 FixedBottomButtonArea 공통 컴포넌트 도입 ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화 Jul 14, 2026
@suhyun113
suhyun113 changed the base branch from develop-fe to feature/#1826-scroll-floating-buttons-MOA-1038 July 14, 2026 17:41
@suhyun113 suhyun113 changed the title ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화 [feature] ClubApplyButton ShareButton 제거 및 MobileButtonArea 공통화 Jul 15, 2026
- 위로 버튼: bounceUp keyframe (커진 상태로 위아래 무한 반복)
- 공유 버튼: wiggle keyframe (커진 상태로 좌우 흔들림 1회)
- 클릭 시 scale(1.08) 눌림 피드백
- $variant prop으로 버튼별 애니메이션 분기
- 위로 버튼: bounceUp keyframe (커진 상태로 위아래 무한 반복)
- 공유 버튼: wiggle keyframe (커진 상태로 좌우 흔들림 1회)
- 클릭 시 scale(1.08) 눌림 피드백
- $variant prop으로 버튼별 애니메이션 분기

@seongwon030 seongwon030 left a comment

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.

혹시 ~~Group으로 컴포넌트명을 설정한 의도가 있나요?

suhyun113 added 16 commits July 17, 2026 18:39
- 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: '공유 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이번 커밋에서 실제 컴포넌트의 공유 버튼은 $isVisible={true}로 바뀌어 항상 표시되는데, 이 설명은 여전히 "위로 스크롤할 때 표시"로 남아 있어 실제 동작과 어긋나요. 새로 추가한 스토리가 오해를 주지 않도록 설명을 실제 동작에 맞춰 주는 게 좋아요.

Suggested change
description: '공유 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)',
description: '공유 버튼 표시 여부 (실제 컴포넌트에서는 항상 표시됩니다.)',

argTypes: {
showScrollToTop: {
control: 'boolean',
description: '위로 이동 버튼 표시 여부 (스크롤을 내렸을 때 표시됩니다.)',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

실제 컴포넌트에서 위로 이동 버튼은 $isVisible={isScrollingUp}, 즉 위로 스크롤할 때 표시돼요. 그런데 이 설명은 "스크롤을 내렸을 때 표시"로 반대로 적혀 있어요. 새 스토리 문서가 실제 동작과 반대로 안내되지 않도록 방향을 맞춰 주세요.

Suggested change
description: '위로 이동 버튼 표시 여부 (스크롤을 내렸을 때 표시됩니다.)',
description: '위로 이동 버튼 표시 여부 (위로 스크롤할 때 표시됩니다.)',


export const FloatingButtonGroup = () => {
const { isTriggered: isScrolledDown, isScrollingUp } = useScrollTrigger();
const { isTriggered: isScrollingUp } = useScrollTrigger();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

isTriggeredisScrollingUp으로 alias 했는데, 이 값은 useScrollTrigger의 기본값(direction='down') 기준으로 "임계값 이상 스크롤 내려간 상태"를 의미해요. 이름은 "위로 스크롤 중"인데 실제 의미는 반대라 헷갈리기 쉬워요. 게다가 훅에는 진짜 isScrollingUp 값이 따로 있어서 더 오해를 부를 수 있어요.

의도가 "스크롤을 내리면 위로 가기 버튼을 노출"하는 거라면, 이전처럼 isScrolledDown 같은 이름으로 alias 하는 게 의미가 더 명확해요.

Suggested change
const { isTriggered: isScrollingUp } = useScrollTrigger();
const { isTriggered: isScrolledDown } = useScrollTrigger();

(이 경우 아래 $isVisible={isScrollingUp}isScrolledDown으로 함께 바꿔 주세요.)

@coderabbitai coderabbitai Bot left a comment

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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7c1b74f and 148d163.

📒 Files selected for processing (5)
  • frontend/src/components/common/FixedBottomButtonArea/FixedBottomButtonArea.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
  • frontend/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

@suhyun113

Copy link
Copy Markdown
Collaborator Author

혹시 ~~Group으로 컴포넌트명을 설정한 의도가 있나요?

버튼 2개를 함께 담는 컴포넌트라 그렇게 설정했는데 다른 이름이 더 좋을까요?

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

Labels

💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants