Skip to content

[feature] 위로가기 아이콘 교체 및 스크롤 업 시 플로팅 버튼 노출 - #1828

Merged
suhyun113 merged 15 commits into
develop-fefrom
feature/#1826-scroll-floating-buttons-MOA-1038
Jul 21, 2026
Merged

[feature] 위로가기 아이콘 교체 및 스크롤 업 시 플로팅 버튼 노출#1828
suhyun113 merged 15 commits into
develop-fefrom
feature/#1826-scroll-floating-buttons-MOA-1038

Conversation

@suhyun113

@suhyun113 suhyun113 commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

작업 내용

스크롤 위치에 따라 동적으로 표시되는 FloatingButtonGroup을 구현하고, 기존 ScrollToTopButton을 대체했습니다.
공유하기 기능을 useShare 훅으로 추출하여 FloatingButtonGroup에 통합했습니다.

주요 변경사항

  • ScrollToTopButton 제거 → FloatingButtonGroup으로 대체
  • useScrollTriggerisScrollingUp 감지 추가
  • useShare 훅으로 공유 로직 추출 (Kakao / Web Share API / 클립보드 복사 fallback)
  • FloatingButtonGroup 하단 위치를 80px → 100px으로 조정하여 하단 고정 버튼과 자연스럽게 분리

스크린샷

Before After
image image

스토리북

👉스토리북


고민 사항

관리자 페이지의 공유 버튼 필요 여부

현재 FloatingButtonGroup에는 공유하기 버튼이 포함되어 있습니다.
관리자 페이지는 로그인이 필요한 페이지이므로, 외부 공유가 실질적으로 의미 없을 수 있습니다.
관리자 페이지에서 공유 버튼을 노출할 필요가 있는지 추가 논의가 필요합니다.


다음 작업 예정

동아리 상세페이지의 지원하기 버튼 영역에 현재 공유하기 버튼이 함께 표시되고 있습니다.
이를 제거하고, 관리자 페이지의 저장하기 버튼과 동일한 FixedBottomButton 스타일로 통일할 예정입니다.

  • ClubApplyButtonShareButton 제거
  • FixedBottomButton 공통 컴포넌트 기반으로 지원하기 버튼 통일

Summary by CodeRabbit

  • 새로운 기능
    • 화면 우측 하단에 ‘맨 위로 이동’ 및 ‘현재 페이지 공유’ 플로팅 버튼을 도입했습니다.
    • Storybook에서 플로팅 버튼 가시성 상태를 시각적으로 확인할 수 있습니다.
  • 개선 사항
    • ‘맨 위로 이동’ 버튼은 스크롤 방향(상향)에 따라 표시/숨김이 달라집니다.
    • ‘현재 페이지 공유’는 웹/모바일/앱 환경에 맞춰 공유를 처리하며, 불가한 경우 링크를 자동 복사하고 안내합니다.
  • 문서화
    • 플로팅 버튼 동작 시나리오(둘 다/하나만/둘 다 숨김)를 Story로 제공합니다.

- ScrollToTopButton을 FloatingButtonGroup으로 교체
- 스크롤 다운 시 위로가기 버튼, 스크롤 업 시 공유 버튼 함께 노출
- 두 버튼을 wrapper로 묶어 우측 12px 고정 위치에 배치
- useShare 훅으로 플랫폼별 공유 로직 (RN WebView / Web Share API / 클립보드) 추출
- ShareButton에서 useShare 재사용
- FloatingButtonGroup 공유 버튼 클릭 시 현재 페이지 URL 공유
@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 21, 2026 1:43pm

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

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

공유 로직을 useShare 훅으로 통합하고, 스크롤 방향에 따라 상단 이동·페이지 공유 버튼을 표시하는 FloatingButtonGroup을 추가했다. 앱 루트는 새 그룹을 렌더링하며 Storybook 상태별 스토리도 제공한다.

Changes

공유 처리 흐름

Layer / File(s) Summary
공유 처리 훅 및 기존 버튼 연동
frontend/src/hooks/useShare.ts, frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
RN WebView, Web Share API, 클립보드 공유 분기를 useShare로 추출하고 기존 ShareButton이 이를 호출하도록 변경했다.

스크롤 기반 플로팅 버튼

Layer / File(s) Summary
스크롤 상태와 플로팅 버튼 동작
frontend/src/hooks/Scroll/useScrollTrigger.ts, frontend/src/components/common/FloatingButtonGroup/*
스크롤 방향 상태를 추가하고, 상단 이동·페이지 공유 버튼의 표시 조건과 클릭 동작 및 고정·호버 스타일을 구현했다.

앱 연결 및 Storybook

Layer / File(s) Summary
앱 루트 교체와 상태별 스토리
frontend/src/App.tsx, frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx
앱 루트에서 FloatingButtonGroup을 렌더링하고 세 가지 버튼 가시성 시나리오를 추가했다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant FloatingButtonGroup
  participant useScrollTrigger
  participant useScrollTo
  participant useShare
  participant ShareTarget
  App->>FloatingButtonGroup: 플로팅 버튼 그룹 렌더링
  FloatingButtonGroup->>useScrollTrigger: 스크롤 상태 조회
  FloatingButtonGroup->>useScrollTo: 상단 이동 요청
  FloatingButtonGroup->>useShare: 현재 제목과 URL 전달
  useShare->>ShareTarget: WebView·Web Share·클립보드 공유
Loading

Possibly related PRs

Suggested labels: 🔨 Refactor

Suggested reviewers: oesnuj, seongwon030, lepitaaar

🚥 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 제목이 기존 위로가기 버튼을 플로팅 버튼 그룹으로 교체하고 스크롤 업 시 노출되도록 한 핵심 변경을 잘 요약합니다.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#1826-scroll-floating-buttons-MOA-1038

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

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

✅ UI 변경사항 없음

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

전체 91개 스토리 · 33개 컴포넌트

@suhyun113 suhyun113 changed the title feat(scroll): FloatingButtonGroup 구현 및 공유 기능 통합 [feature] 위로가기 아이콘 교체 및 스크롤 업 시 플로팅 버튼 노출 Jul 14, 2026
@suhyun113
suhyun113 changed the base branch from main to develop-fe July 14, 2026 15:12

@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 mobile-responsive layout for the club introduction edit tab, refactors application form grouping from semester-based to active/inactive status, and replaces the scroll-to-top button with a unified floating button group (scroll and share). Feedback focuses on fixing a bug in the useClubIntroEdit hook where isDirty incorrectly evaluates to true on mount due to auto-generated FAQ IDs, resolving a layout spacing issue in the floating button group when buttons are hidden, and simplifying character limit checks by passing maxLength directly to the textareas.

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.

I am having trouble creating individual review comments. Click here to see my feedback.

frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/hooks/useClubIntroEdit.ts (35-55)

high

useClubIntroEdit 훅에서 isDirty를 판단할 때 JSON.stringify(faqs)와 JSON.stringify(initialState.faqs)를 비교하고 있습니다. 하지만 FAQEditor 컴포넌트 마운트 시 id가 없는 FAQ 항목에 대해 자동으로 id를 생성하여 onChange를 호출하게 됩니다.

만약 서버에서 처음 받아온 FAQ 데이터에 id가 없다면, 마운트 직후 faqs에는 id가 추가되고 initialState.faqs에는 id가 없어 isDirty가 사용자의 조작 없이도 즉시 true로 변경되는 버그가 발생합니다.

이를 방지하기 위해, 데이터를 처음 불러와 상태를 초기화하는 useClubIntroEdit 훅의 useEffect 내부에서 미리 id를 생성하여 faqs와 initialState.faqs가 동일한 id를 공유하도록 수정하는 것을 권장합니다.

  useEffect(() => {
    if (clubDetail?.description && clubDetail.id !== loadedClubId) {
      const desc = clubDetail.description;
      const initialFaqs = (desc.faqs || []).map((faq) => ({
        ...faq,
        id: faq.id || `faq-${Date.now()}-${Math.random()}`,
      }));
      const initial = {
        introDescription: desc.introDescription || '',
        activityDescription: desc.activityDescription || '',
        awards: desc.awards || [],
        idealCandidate: desc.idealCandidate || { tags: [], content: '' },
        benefits: desc.benefits || '',
        faqs: initialFaqs,
      };
      setIntroDescription(initial.introDescription);
      setActivityDescription(initial.activityDescription);
      setAwards(initial.awards);
      setIdealCandidate(initial.idealCandidate);
      setBenefits(initial.benefits);
      setFaqs(initialFaqs);
      setInitialState(initial);
      setLoadedClubId(clubDetail.id);
    }
  }, [clubDetail, loadedClubId]);

frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts (16-27)

medium

FloatingButton 컴포넌트에서 $isVisible이 false일 때 visibility: hidden을 사용하고 있습니다. 하지만 이 버튼들은 flex-direction: column과 gap: 8px이 적용된 GroupContainer 내부의 자식 요소입니다. visibility: hidden은 화면에서 보이지 않지만 레이아웃 공간은 여전히 차지하므로, 버튼이 숨겨졌을 때 불필요한 빈 공간(여백)이 생기게 됩니다.

이를 해결하기 위해 $isVisible에 따라 height와 opacity를 함께 트랜지션하고 overflow: hidden을 적용하면, 숨겨졌을 때 레이아웃 공간을 차지하지 않으면서 자연스러운 애니메이션 효과를 유지할 수 있습니다.

export const FloatingButton = styled.button<{ $isVisible: boolean }>`
  width: 33px;
  height: ${({ $isVisible }) => ($isVisible ? '33px' : '0')};
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  opacity: ${({ $isVisible }) => ($isVisible ? 1 : 0)};
  visibility: ${({ $isVisible }) => ($isVisible ? 'visible' : 'hidden')};
  overflow: hidden;
  transition:
    height 0.3s,
    opacity 0.3s,
    visibility 0.3s;

frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx (104-109)

medium

TextArea 컴포넌트에 maxLength 속성을 직접 전달하지 않아, 글자 수 제한을 위해 handleIntroChange 등의 커스텀 핸들러에서 매번 길이를 수동으로 체크하고 있습니다.

Styled.TextArea에 maxLength 속성을 직접 전달하면 브라우저가 네이티브하게 글자 수 입력을 제한하므로, 복잡한 수동 길이 체크 핸들러들을 모두 제거하고 인라인 onChange로 단순화할 수 있어 코드가 훨씬 깔끔해집니다. (동일한 패턴을 activityDescription, idealCandidate, benefits 입력 영역에도 적용하고 관련 커스텀 핸들러들을 제거할 수 있습니다.)

            <Styled.TextArea
              ref={introRef}
              value={introDescription}
              onChange={(e) => setIntroDescription(e.target.value)}
              placeholder={INTRO_DESCRIPTION_PLACEHOLDER}
              maxLength={INTRO_DESCRIPTION_MAX}
            />

frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.tsx (51-57)

medium

Styled.AnswerTextarea에 maxLength 속성을 직접 전달하지 않아 handleAnswerChange 핸들러에서 수동으로 길이를 체크하고 있습니다. maxLength={FAQ_ANSWER_MAX}를 직접 전달하면 브라우저가 네이티브하게 입력을 제한하므로, 수동 체크 핸들러를 제거하고 인라인 onChange로 단순화할 수 있습니다.

          <Styled.AnswerTextarea
            ref={answerRef}
            value={faq.answer}
            onChange={(e) => onChange(index, 'answer', e.target.value)}
            placeholder={FAQ_ANSWER_PLACEHOLDER}
            maxLength={FAQ_ANSWER_MAX}
            rows={1}
          />

@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: 2

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/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts (1)

6-14: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

배경색 제거로 하단 고정 버튼 영역이 투명해질 수 있습니다.

SaveButtonArea는 화면 하단에 고정된 오버레이인데, 이전에 있던 흰색 background 선언이 제거되어 버튼을 감싸는 패딩 영역(상 10px, 좌우 20px)으로 스크롤 중인 페이지 콘텐츠가 비쳐 보일 수 있습니다. 의도된 변경이 아니라면 배경을 다시 추가해주세요.

🎨 제안하는 수정
 export const SaveButtonArea = styled.div`
   position: fixed;
   bottom: 0;
   left: 50%;
   transform: translateX(-50%);
   width: 100%;
   max-width: 500px;
   padding: 10px 20px calc(20px + env(safe-area-inset-bottom));
+  background: ${colors.base.white};
   z-index: ${Z_INDEX.clubDetailFooter};
🤖 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/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts`
around lines 6 - 14, Restore an opaque white background on the SaveButtonArea
styled component so the fixed footer’s padding area does not reveal scrolling
content behind it.
🧹 Nitpick comments (2)
frontend/src/App.tsx (1)

4-4: 🎯 Functional Correctness | 🔵 Trivial

FloatingButtonGroup이 관리자 라우트에도 전역 노출됩니다.

AppRoutes 바깥, 라우터 최상위에 마운트되어 있어 /admin/* 등 관리자 페이지에서도 공개용 공유/스크롤 버튼이 그대로 노출됩니다. PR 설명에서 이미 후속 논의 대상으로 언급된 사항이라 차단 이슈는 아니지만, 관리자 라우트에서는 조건부로 숨기거나 PrivateRoute 경계에 따라 렌더링을 제한하는 것을 검토해 주세요.

Also applies to: 36-36

🤖 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/App.tsx` at line 4, App.tsx의 최상위에 전역 마운트된 FloatingButtonGroup이
/admin/* 관리자 라우트에서도 표시되지 않도록 제한하세요. AppRoutes 또는 PrivateRoute 경계를 기준으로 공개 라우트에서만
렌더링되게 조건부 처리하고, 관리자 페이지의 기존 라우팅 동작은 유지하세요.
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx (1)

60-82: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

최대 길이를 초과하는 입력(붙여넣기 등)에 대한 처리 방식을 개선해 보세요.

현재 onChange 핸들러는 입력된 값이 최대 길이를 초과하면 상태 업데이트를 완전히 무시합니다. 이로 인해 사용자가 긴 텍스트를 한 번에 붙여넣을 때 화면에 아무런 반응이 없어 불편을 겪을 수 있습니다.

입력된 텍스트를 최대 길이에 맞춰 잘라내어 상태를 업데이트하도록 slice를 사용하는 것을 권장합니다.

💡 제안하는 수정안 (텍스트 잘라내기)
  const handleIntroChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= INTRO_DESCRIPTION_MAX) {
-     setIntroDescription(e.target.value);
-   }
+   setIntroDescription(e.target.value.slice(0, INTRO_DESCRIPTION_MAX));
  };

  const handleActivityChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= ACTIVITY_DESCRIPTION_MAX) {
-     setActivityDescription(e.target.value);
-   }
+   setActivityDescription(e.target.value.slice(0, ACTIVITY_DESCRIPTION_MAX));
  };

  const handleIdealChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= IDEAL_CANDIDATE_MAX) {
-     setIdealCandidate({ ...idealCandidate, content: e.target.value });
-   }
+   setIdealCandidate({
+     ...idealCandidate,
+     content: e.target.value.slice(0, IDEAL_CANDIDATE_MAX),
+   });
  };

  const handleBenefitsChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= BENEFITS_MAX) {
-     setBenefits(e.target.value);
-   }
+   setBenefits(e.target.value.slice(0, BENEFITS_MAX));
  };
🤖 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/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx`
around lines 60 - 82, Update handleIntroChange, handleActivityChange,
handleIdealChange, and handleBenefitsChange to always update their respective
state using the input value truncated with slice(0, corresponding maximum).
Preserve the existing idealCandidate object update while ensuring pasted or
oversized text is visibly capped at its configured limit.
🤖 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/src/hooks/useShare.ts`:
- Around line 17-20: Wrap the requestShare call in the isRNWebView branch with
try/catch so synchronous bridge exceptions are handled, then continue to the
existing clipboard fallback when it throws or does not report success. Preserve
the current early return only when requestShare completes successfully.

In
`@frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx`:
- Around line 29-31: In the TextField height-adjustment logic, update the
lineHeight parsing to fall back to a safe numeric default when
getComputedStyle(textarea).lineHeight parses as NaN, then use that validated
value for the two-line height calculation and CSS assignment.

---

Outside diff comments:
In
`@frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts`:
- Around line 6-14: Restore an opaque white background on the SaveButtonArea
styled component so the fixed footer’s padding area does not reveal scrolling
content behind it.

---

Nitpick comments:
In `@frontend/src/App.tsx`:
- Line 4: App.tsx의 최상위에 전역 마운트된 FloatingButtonGroup이 /admin/* 관리자 라우트에서도 표시되지
않도록 제한하세요. AppRoutes 또는 PrivateRoute 경계를 기준으로 공개 라우트에서만 렌더링되게 조건부 처리하고, 관리자 페이지의
기존 라우팅 동작은 유지하세요.

In
`@frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx`:
- Around line 60-82: Update handleIntroChange, handleActivityChange,
handleIdealChange, and handleBenefitsChange to always update their respective
state using the input value truncated with slice(0, corresponding maximum).
Preserve the existing idealCandidate object update while ensuring pasted or
oversized text is visibly capped at its configured limit.
🪄 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: ed928c0a-61cc-4b7b-ba08-1ab16e8f7357

📥 Commits

Reviewing files that changed from the base of the PR and between 3d177fb and 02db0e6.

⛔ Files ignored due to path filters (3)
  • 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
📒 Files selected for processing (53)
  • frontend/docs/features/admin/info/field-components.md
  • frontend/docs/features/admin/info/mobile.md
  • frontend/docs/features/admin/intro/mobile.md
  • frontend/docs/features/components/header.md
  • frontend/src/App.tsx
  • frontend/src/apis/application.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
  • frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
  • frontend/src/components/common/WebviewTopBar/WebviewTopBar.styles.ts
  • frontend/src/constants/CLAUDE.md
  • frontend/src/constants/adminFieldLimits.ts
  • frontend/src/constants/adminFieldPlaceholders.ts
  • frontend/src/constants/initialFormData.ts
  • frontend/src/hooks/Scroll/useScrollTrigger.ts
  • frontend/src/hooks/useShare.ts
  • frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/desktop/MakeTags/MakeTags.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/FreeTagEditPage/FreeTagEditPage.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/LinkEditPage/LinkField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/FAQEditor/FAQEditor.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/hooks/useClubIntroEdit.ts
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
  • frontend/src/types/application.ts
  • frontend/src/utils/CLAUDE.md
  • frontend/src/utils/applicationFormGroup.ts
  • frontend/src/utils/semester.test.ts
  • frontend/src/utils/semester.ts
💤 Files with no reviewable changes (4)
  • frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
  • frontend/src/utils/semester.test.ts
  • frontend/src/utils/semester.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx

@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

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

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/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts (1)

6-14: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

배경색 제거로 하단 고정 버튼 영역이 투명해질 수 있습니다.

SaveButtonArea는 화면 하단에 고정된 오버레이인데, 이전에 있던 흰색 background 선언이 제거되어 버튼을 감싸는 패딩 영역(상 10px, 좌우 20px)으로 스크롤 중인 페이지 콘텐츠가 비쳐 보일 수 있습니다. 의도된 변경이 아니라면 배경을 다시 추가해주세요.

🎨 제안하는 수정
 export const SaveButtonArea = styled.div`
   position: fixed;
   bottom: 0;
   left: 50%;
   transform: translateX(-50%);
   width: 100%;
   max-width: 500px;
   padding: 10px 20px calc(20px + env(safe-area-inset-bottom));
+  background: ${colors.base.white};
   z-index: ${Z_INDEX.clubDetailFooter};
🤖 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/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts`
around lines 6 - 14, Restore an opaque white background on the SaveButtonArea
styled component so the fixed footer’s padding area does not reveal scrolling
content behind it.
🧹 Nitpick comments (2)
frontend/src/App.tsx (1)

4-4: 🎯 Functional Correctness | 🔵 Trivial

FloatingButtonGroup이 관리자 라우트에도 전역 노출됩니다.

AppRoutes 바깥, 라우터 최상위에 마운트되어 있어 /admin/* 등 관리자 페이지에서도 공개용 공유/스크롤 버튼이 그대로 노출됩니다. PR 설명에서 이미 후속 논의 대상으로 언급된 사항이라 차단 이슈는 아니지만, 관리자 라우트에서는 조건부로 숨기거나 PrivateRoute 경계에 따라 렌더링을 제한하는 것을 검토해 주세요.

Also applies to: 36-36

🤖 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/App.tsx` at line 4, App.tsx의 최상위에 전역 마운트된 FloatingButtonGroup이
/admin/* 관리자 라우트에서도 표시되지 않도록 제한하세요. AppRoutes 또는 PrivateRoute 경계를 기준으로 공개 라우트에서만
렌더링되게 조건부 처리하고, 관리자 페이지의 기존 라우팅 동작은 유지하세요.
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx (1)

60-82: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

최대 길이를 초과하는 입력(붙여넣기 등)에 대한 처리 방식을 개선해 보세요.

현재 onChange 핸들러는 입력된 값이 최대 길이를 초과하면 상태 업데이트를 완전히 무시합니다. 이로 인해 사용자가 긴 텍스트를 한 번에 붙여넣을 때 화면에 아무런 반응이 없어 불편을 겪을 수 있습니다.

입력된 텍스트를 최대 길이에 맞춰 잘라내어 상태를 업데이트하도록 slice를 사용하는 것을 권장합니다.

💡 제안하는 수정안 (텍스트 잘라내기)
  const handleIntroChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= INTRO_DESCRIPTION_MAX) {
-     setIntroDescription(e.target.value);
-   }
+   setIntroDescription(e.target.value.slice(0, INTRO_DESCRIPTION_MAX));
  };

  const handleActivityChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= ACTIVITY_DESCRIPTION_MAX) {
-     setActivityDescription(e.target.value);
-   }
+   setActivityDescription(e.target.value.slice(0, ACTIVITY_DESCRIPTION_MAX));
  };

  const handleIdealChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= IDEAL_CANDIDATE_MAX) {
-     setIdealCandidate({ ...idealCandidate, content: e.target.value });
-   }
+   setIdealCandidate({
+     ...idealCandidate,
+     content: e.target.value.slice(0, IDEAL_CANDIDATE_MAX),
+   });
  };

  const handleBenefitsChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-   if (e.target.value.length <= BENEFITS_MAX) {
-     setBenefits(e.target.value);
-   }
+   setBenefits(e.target.value.slice(0, BENEFITS_MAX));
  };
🤖 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/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx`
around lines 60 - 82, Update handleIntroChange, handleActivityChange,
handleIdealChange, and handleBenefitsChange to always update their respective
state using the input value truncated with slice(0, corresponding maximum).
Preserve the existing idealCandidate object update while ensuring pasted or
oversized text is visibly capped at its configured limit.
🤖 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/src/hooks/useShare.ts`:
- Around line 17-20: Wrap the requestShare call in the isRNWebView branch with
try/catch so synchronous bridge exceptions are handled, then continue to the
existing clipboard fallback when it throws or does not report success. Preserve
the current early return only when requestShare completes successfully.

In
`@frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx`:
- Around line 29-31: In the TextField height-adjustment logic, update the
lineHeight parsing to fall back to a safe numeric default when
getComputedStyle(textarea).lineHeight parses as NaN, then use that validated
value for the two-line height calculation and CSS assignment.

---

Outside diff comments:
In
`@frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts`:
- Around line 6-14: Restore an opaque white background on the SaveButtonArea
styled component so the fixed footer’s padding area does not reveal scrolling
content behind it.

---

Nitpick comments:
In `@frontend/src/App.tsx`:
- Line 4: App.tsx의 최상위에 전역 마운트된 FloatingButtonGroup이 /admin/* 관리자 라우트에서도 표시되지
않도록 제한하세요. AppRoutes 또는 PrivateRoute 경계를 기준으로 공개 라우트에서만 렌더링되게 조건부 처리하고, 관리자 페이지의
기존 라우팅 동작은 유지하세요.

In
`@frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx`:
- Around line 60-82: Update handleIntroChange, handleActivityChange,
handleIdealChange, and handleBenefitsChange to always update their respective
state using the input value truncated with slice(0, corresponding maximum).
Preserve the existing idealCandidate object update while ensuring pasted or
oversized text is visibly capped at its configured limit.
🪄 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: ed928c0a-61cc-4b7b-ba08-1ab16e8f7357

📥 Commits

Reviewing files that changed from the base of the PR and between 3d177fb and 02db0e6.

⛔ Files ignored due to path filters (3)
  • 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
📒 Files selected for processing (53)
  • frontend/docs/features/admin/info/field-components.md
  • frontend/docs/features/admin/info/mobile.md
  • frontend/docs/features/admin/intro/mobile.md
  • frontend/docs/features/components/header.md
  • frontend/src/App.tsx
  • frontend/src/apis/application.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
  • frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
  • frontend/src/components/common/WebviewTopBar/WebviewTopBar.styles.ts
  • frontend/src/constants/CLAUDE.md
  • frontend/src/constants/adminFieldLimits.ts
  • frontend/src/constants/adminFieldPlaceholders.ts
  • frontend/src/constants/initialFormData.ts
  • frontend/src/hooks/Scroll/useScrollTrigger.ts
  • frontend/src/hooks/useShare.ts
  • frontend/src/pages/AdminPage/components/MobileSaveButtonArea/MobileSaveButtonArea.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/desktop/MakeTags/MakeTags.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/EditField/EditField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/FreeTagEditPage/FreeTagEditPage.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/LinkEditPage/LinkField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/NavField/NavField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/FAQEditor/FAQEditor.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/hooks/useClubIntroEdit.ts
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
  • frontend/src/types/application.ts
  • frontend/src/utils/CLAUDE.md
  • frontend/src/utils/applicationFormGroup.ts
  • frontend/src/utils/semester.test.ts
  • frontend/src/utils/semester.ts
💤 Files with no reviewable changes (4)
  • frontend/src/components/common/ScrollToTopButton/ScrollToTopButton.tsx
  • frontend/src/utils/semester.test.ts
  • frontend/src/utils/semester.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🛑 Comments failed to post (2)
frontend/src/hooks/useShare.ts (1)

17-20: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

requestShare 호출에 예외 처리가 없습니다.

인앱 웹뷰 브릿지(requestShare)가 동기적으로 예외를 던지면 catch되지 않아 unhandled promise rejection이 발생하고, 아래 clipboard fallback으로도 넘어가지 않습니다. 다른 두 경로(navigator.share, clipboard)는 모두 try/catch로 보호되어 있는 것과 대비됩니다.

🔧 제안 수정
-    if (isRNWebView) {
-      const isSent = requestShare({ title, text, url });
-      if (isSent) return;
-    }
+    if (isRNWebView) {
+      try {
+        const isSent = requestShare({ title, text, url });
+        if (isSent) return;
+      } catch {
+        // fall through to web fallback
+      }
+    }
📝 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.

    if (isRNWebView) {
      try {
        const isSent = requestShare({ title, text, url });
        if (isSent) return;
      } catch {
        // fall through to web fallback
      }
    }
🤖 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 17 - 20, Wrap the requestShare
call in the isRNWebView branch with try/catch so synchronous bridge exceptions
are handled, then continue to the existing clipboard fallback when it throws or
does not report success. Preserve the current early return only when
requestShare completes successfully.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx (1)

29-31: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

lineHeight 파싱 시 NaN을 방지하기 위한 Fallback을 추가해 주세요.

브라우저에 따라 getComputedStyle(textarea).lineHeight"normal"을 반환할 수 있으며, 이 경우 parseFloatNaN을 반환합니다. NaN이 포함된 연산 결과가 CSS 속성값으로 들어가면 높이 자동 조절이 깨질 수 있으므로 안전한 기본값을 설정하는 것이 좋습니다.

💻 제안하는 수정안
-    const lineHeight = parseFloat(getComputedStyle(textarea).lineHeight);
+    const computedLineHeight = getComputedStyle(textarea).lineHeight;
+    const lineHeight = computedLineHeight === 'normal' ? 20 : parseFloat(computedLineHeight) || 20;
     textarea.style.height = 'auto';
     textarea.style.height = `${Math.min(textarea.scrollHeight, lineHeight * 2)}px`;
📝 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 computedLineHeight = getComputedStyle(textarea).lineHeight;
    const lineHeight = computedLineHeight === 'normal' ? 20 : parseFloat(computedLineHeight) || 20;
    textarea.style.height = 'auto';
    textarea.style.height = `${Math.min(textarea.scrollHeight, lineHeight * 2)}px`;
🤖 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/AdminPage/tabs/ClubInfoEditTab/components/mobile/TextField/TextField.tsx`
around lines 29 - 31, In the TextField height-adjustment logic, update the
lineHeight parsing to fall back to a safe numeric default when
getComputedStyle(textarea).lineHeight parses as NaN, then use that validated
value for the two-line height calculation and CSS assignment.

- 위로 버튼: bounceUp keyframe (커진 상태로 위아래 무한 반복)
- 공유 버튼: wiggle keyframe (커진 상태로 좌우 흔들림 1회)
- 클릭 시 scale(1.08) 눌림 피드백
- $variant prop으로 버튼별 애니메이션 분기

@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: 1

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

Inline comments:
In
`@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx`:
- Around line 16-31: Update both Styled.FloatingButton instances in the
FloatingButtonGroup story to provide the required $variant prop: use the scroll
variant for the scroll-to-top button and the share variant for the share button,
preserving their existing visibility and click behavior.
🪄 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: c5784f93-6bf0-48f0-8e2d-5d7932faec42

📥 Commits

Reviewing files that changed from the base of the PR and between 02db0e6 and 4c848e0.

📒 Files selected for processing (3)
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx

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

수고하셨습니다!

Comment on lines +13 to +20
const handlePageShare = async () => {
const url = window.location.href;
await handleShare({
title: document.title,
text: url,
url,
});
};

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.

트래킹 이벤트만 추가해주실 수 있을까요? 전역에서 쓰이기에 prop은 path가 나을 것 같아요

import * as Styled from './FloatingButtonGroup.styles';

export const FloatingButtonGroup = () => {
const { isTriggered: isScrolledDown, 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.

공유 버튼의 $isVisibletrue로 바뀌면서 isScrolledDown(isTriggered)이 더 이상 쓰이지 않게 됐어요. 사용하지 않는 값은 destructuring에서 빼 주시는 게 좋아요. 이대로면 useScrollTrigger가 반환하는 isTriggered 자체가 필요 없는지도 함께 확인해 주세요.

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

import * as Styled from './FloatingButtonGroup.styles';

export const FloatingButtonGroup = () => {
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 했는데, 훅을 보면 isTriggeredisScrollingUp은 서로 다른 값이에요. isTriggered는 (direction: 'down' 기준) scrollY > threshold, 즉 "일정 이상 스크롤 내려간 상태"를 뜻하고, isScrollingUp은 "실제로 위로 스크롤 중"을 뜻해요.

이번 변경으로 위로 이동 버튼이 "스크롤 올릴 때만" → "스크롤 내려가면 항상 노출"로 동작이 바뀐 건데, 만약 의도한 동작이라면 변수명이 실제 값(isTriggered)과 반대 의미라 헷갈려요. isScrolledDown처럼 실제 의미에 맞게 네이밍해 주는 게 좋아요.

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

(이 경우 아래 $isVisible={isScrollingUp}isScrolledDown으로 함께 바꿔 주세요.) 혹시 원래대로 "위로 스크롤 시에만 노출"이 의도였다면 isScrollingUp 값을 그대로 구조분해해서 쓰는 게 맞아요.

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

🧹 Nitpick comments (1)
frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts (1)

43-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

모바일 기기에서 Hover 상태 고정(Sticky hover) 방지 권장

모바일 터치 기기에서는 버튼을 탭한 후 :hover 상태가 계속 남아있어 아이콘이 커진 상태로 고정되는 어색한 사용성을 보일 수 있습니다. 이를 방지하기 위해 마우스(포인터)를 지원하는 기기에서만 hover 애니메이션이 동작하도록 미디어 쿼리를 추가하는 것을 제안합니다.

💡 제안하는 수정안
-  &:hover img {
-    transform: scale(1.12);
-  }
+  `@media` (hover: hover) and (pointer: fine) {
+    &:hover img {
+      transform: scale(1.12);
+    }
+  }

   &:active img {
     transform: scale(1.04);
   }
🤖 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.styles.ts`
around lines 43 - 49, Update the hover styling in FloatingButtonGroup.styles.ts
so the &:hover img scale animation applies only on devices with a hover-capable
mouse pointer, using an appropriate pointer/hover media query; keep the &:active
img behavior unchanged.
🤖 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.

Nitpick comments:
In
`@frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts`:
- Around line 43-49: Update the hover styling in FloatingButtonGroup.styles.ts
so the &:hover img scale animation applies only on devices with a hover-capable
mouse pointer, using an appropriate pointer/hover media query; keep the &:active
img behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5405804-d12e-446b-a714-0dab7b8fcb8b

📥 Commits

Reviewing files that changed from the base of the PR and between e4070a9 and 630e6a9.

📒 Files selected for processing (3)
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.styles.ts
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx
💤 Files with no reviewable changes (1)
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/common/FloatingButtonGroup/FloatingButtonGroup.tsx

@suhyun113
suhyun113 merged commit e2bca03 into develop-fe Jul 21, 2026
6 checks passed
@suhyun113
suhyun113 deleted the feature/#1826-scroll-floating-buttons-MOA-1038 branch July 21, 2026 13:45
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