Skip to content

[feature] 어드민 소개 정보 수정 모바일 공통 컴포넌트 구현#1784

Merged
suhyun113 merged 14 commits into
develop-fefrom
feature/#1782-intro-section-components-MOA-1016
Jun 30, 2026
Merged

[feature] 어드민 소개 정보 수정 모바일 공통 컴포넌트 구현#1784
suhyun113 merged 14 commits into
develop-fefrom
feature/#1782-intro-section-components-MOA-1016

Conversation

@suhyun113

@suhyun113 suhyun113 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

#️⃣연관된 이슈

#1782

📝작업 내용

어드민 동아리 소개 편집 화면의 모바일 UI에서 사용하는 섹션 컴포넌트를 구현했습니다.

구현 컴포넌트

컴포넌트 역할
InfoSection 라벨 + 글자수 카운터 + 카드를 묶는 공통 섹션 래퍼. children으로 입력 내용을 유연하게 조합
AwardSection 동아리 성과(수상 이력)를 학기별로 표시. 클릭 시 편집 화면으로 이동하는 NavButton 포함
FAQSection 자주 묻는 질문 목록 편집. FAQ 추가 버튼으로 카드를 동적으로 추가/삭제

컴포넌트 설계 결정

InfoSection을 공통으로, AwardSection·FAQSection을 별도 구현한 이유

처음에는 InfoSection을 공통 래퍼로 두고 AwardSection, FAQSection 모두 이를 상속하는 구조를 계획했습니다.

구현 과정에서 각 섹션이 공통 구조에서 벗어나야 하는 이유가 생겼습니다.

  • FAQSection: FAQ 추가 버튼으로 카드를 동적으로 추가하는 구조라, 카드 하나로 children을 감싸는 InfoSection 형태와 맞지 않아 별도 구현이 필요했습니다.

  • AwardSection: 글자수 카운터가 없고, 카드 내부에 NavButton이 포함된 구조입니다. 이 필드 하나만을 위해 InfoSection에 optional prop을 추가하는 것보다 별도 구현이 더 적절하다고 판단했습니다.

결과적으로 InfoSection글자수 카운터가 있는 텍스트 입력 섹션 전용 공통 컴포넌트로 확정했고, AwardSection·FAQSection은 각자 Wrapper/Header/Label/Card 스타일을 독립으로 가집니다.

사용 방법

InfoSection — 글자수 제한이 있는 텍스트 입력 섹션

<InfoSection label="동아리를 소개할게요" maxLength={200} currentLength={value.length}>
  <textarea value={value} onChange={...} />
</InfoSection>

AwardSection — 수상 이력 표시 및 편집 진입

<AwardSection awards={awards} onNavigate={(award) => navigateToEditor(award)} />

FAQSection — FAQ 목록 편집

<FAQSection faqs={faqs} onChange={setFaqs} />

Storybook

구현한 컴포넌트를 Storybook에서 확인할 수 있습니다.
👉 스토리북

경로: `Pages/AdminPage/tabs/ClubIntroEditTab/components/mobile
-> 이전에 구현한 ApplicationInfoEditTab의 경우 처음에는 AdminPage의 전체 공통 컴포넌트로 구현했으니, 각 페이지별 공통 컴포넌트로 구현하는 것이 맞다는 판단이었습니다. 이후 EditFields 컴포넌트는 ClubInfoEditTab으로 이동시킬 예정입니다. 또한 각 탭의 컴포넌트의 경우 모바일과 데스크탑의 사용이 많이 다르기 때문에 폴더로 분리했습니다.

🫡 참고사항

  • useAutoGrow 훅을 별도 파일로 분리하여 FAQSection의 답변 textarea 자동 높이 조절에 활용했습니다.

Summary by CodeRabbit

  • New Features
    • 모바일 소개 편집 화면에 정보, FAQ, 수상 내역 섹션이 추가되어 더 직관적으로 수정할 수 있게 되었습니다.
    • 입력 영역이 내용에 맞춰 자동으로 늘어나며, 글자 수와 최대 길이를 함께 확인할 수 있습니다.
  • Bug Fixes
    • 비활성화된 입력 상태에서 불필요한 값이 남지 않도록 동작을 개선했습니다.
    • 수상 내역은 최신 순으로 정렬되어 표시됩니다.

@vercel

vercel Bot commented Jun 24, 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 Jun 29, 2026 6:55pm

@github-actions github-actions Bot added the 💻 FE Frontend label Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@suhyun113, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 714ea74c-f463-4b66-8719-059b6953ceed

📥 Commits

Reviewing files that changed from the base of the PR and between b3fd425 and 1f59902.

📒 Files selected for processing (3)
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.tsx

Walkthrough

textarea 자동 높이 조절을 useAutoGrow 훅으로 분리하고 CustomTextArea에 적용했습니다. 모바일 클럽 소개 편집 탭에 InfoSection, AwardSection, FAQSection과 관련 스타일, 스토리를 추가했으며, ClubIntroEditTab의 에디터 import 경로도 조정했습니다.

Changes

모바일 섹션 및 공유 훅 추가

Layer / File(s) Summary
useAutoGrow 훅과 CustomTextArea 적용
frontend/src/hooks/useAutoGrow.ts, frontend/src/components/common/CustomTextArea/CustomTextArea.tsx
textarea 높이를 scrollHeight 기준으로 갱신하는 useAutoGrow 훅을 추가하고, CustomTextArea의 기존 useRef+useEffect 로직을 훅 호출로 교체했습니다. disabled 상태에서는 빈 문자열을 훅에 전달합니다.
InfoSection 컴포넌트, 스타일, 스토리
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.styles.ts, InfoSection.tsx, InfoSection.stories.tsx
label, maxLength, currentLength, children을 받아 헤더 카운터와 카드 영역을 렌더링하는 InfoSectionWrapper/Header/Label/Counter/Card 스타일을 추가했습니다. 스토리에서는 MobileTextareachildren으로 전달합니다.
AwardSection 컴포넌트, 스타일, 스토리
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.styles.ts, AwardSection.tsx, AwardSection.stories.tsx
학기/연도 기반 내림차순 정렬과 빈 상태/목록 상태 조건부 렌더링을 갖는 AwardSection을 추가하고, 관련 스타일과 Empty/Filled 스토리를 구성했습니다. 행 클릭 시 onNavigate에 선택 항목을 전달합니다.
FAQSection 컴포넌트, 스타일, 스토리
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.styles.ts, FAQSection.tsx, FAQSection.stories.tsx
개별 FAQ 편집과 전체 목록 관리를 수행하는 FAQSection을 추가했습니다. 답변 textarea에 useAutoGrow를 적용하고 글자수 제한 로직을 포함했으며, 관련 스타일과 Empty/Filled 스토리를 함께 추가했습니다.
데스크톱 에디터 import 경로 변경
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx
AwardEditorFAQEditor의 import 경로를 데스크톱 하위 디렉터리로 변경했습니다.

Sequence Diagram(s)

sequenceDiagram
  participant CustomTextArea
  participant useAutoGrow
  participant TextareaDOM
  CustomTextArea->>useAutoGrow: useAutoGrow(disabled ? '' : value)
  useAutoGrow->>TextareaDOM: height = 'auto'
  useAutoGrow->>TextareaDOM: height = scrollHeight + 'px'
  useAutoGrow-->>CustomTextArea: ref 반환
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

✨ Feature, 🎨 Design

Suggested reviewers

  • 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 모바일용 어드민 소개 정보 수정 공통 컴포넌트 구현이라는 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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#1782-intro-section-components-MOA-1016

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.

@suhyun113 suhyun113 changed the title [feature] 어드민 소개 편집 모바일 공통 컴포넌트 구현 [feature] 어드민 소개 정보 수정 모바일 공통 컴포넌트 구현 Jun 24, 2026
@suhyun113 suhyun113 changed the base branch from main to develop-fe June 24, 2026 17:55
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

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

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

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

1개 스토리 변경 · 전체 66개 스토리 · 26개 컴포넌트

@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 several new mobile components for the Admin Page, including AwardSection, FAQSection, and InfoSection, along with a custom useAutoGrow hook to handle auto-resizing text areas. Feedback on these changes highlights three key areas for improvement: ensuring useAutoGrow receives the actual value even when disabled to prevent text truncation during programmatic updates, utilizing the native maxLength attribute on the FAQ answer textarea for a smoother user experience, and replacing array indices with unique keys in the dynamic FAQ list to avoid React rendering and state bugs.

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.

el.style.height = `${el.scrollHeight}px`;
}
}, [value]);
const textAreaRef = useAutoGrow(disabled ? '' : value);

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

disabled 상태일 때 useAutoGrow에 빈 문자열('')을 전달하도록 구현되어 있습니다. 이 경우, 컴포넌트가 비활성화(disabled={true})된 상태에서 부모 컴포넌트로부터 value가 프로그램적으로 업데이트되면, useAutoGrow 훅의 의존성 배열에 있는 값이 ''로 유지되므로 높이 조절 useEffect가 실행되지 않습니다. 이로 인해 텍스트가 변경되었음에도 높이가 늘어나지 않아 텍스트가 잘리는 현상이 발생할 수 있습니다.

비활성화 상태이더라도 텍스트 내용에 맞게 높이가 자동으로 조절되는 것이 올바른 UX이므로, disabled 여부와 관계없이 value를 직접 전달하는 것이 안전합니다.

Suggested change
const textAreaRef = useAutoGrow(disabled ? '' : value);
const textAreaRef = useAutoGrow(value);

Comment on lines +42 to +52
<Styled.AnswerTextarea
ref={answerRef}
value={faq.answer}
onChange={(e) => {
if (e.target.value.length <= ANSWER_MAX_LENGTH) {
onChange(index, 'answer', e.target.value);
}
}}
placeholder='답변을 입력해주세요'
rows={1}
/>

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

AnswerTextarea에서 글자 수 제한을 구현하기 위해 onChange 이벤트 핸들러 내에서 직접 길이를 체크하고 있습니다. 이 방식은 사용자가 최대 글자 수를 초과하는 텍스트를 붙여넣거나 입력할 때 입력 자체가 완전히 무시되어 사용자 경험(UX) 측면에서 어색할 수 있습니다.

또한, QuestionInput 컴포넌트(34라인)처럼 HTML 표준 속성인 maxLength를 활용하면 브라우저가 기본적으로 글자 수 제한을 처리해 주므로 코드가 더 간결해지고 일관성을 유지할 수 있습니다.

Suggested change
<Styled.AnswerTextarea
ref={answerRef}
value={faq.answer}
onChange={(e) => {
if (e.target.value.length <= ANSWER_MAX_LENGTH) {
onChange(index, 'answer', e.target.value);
}
}}
placeholder='답변을 입력해주세요'
rows={1}
/>
<Styled.AnswerTextarea
ref={answerRef}
value={faq.answer}
onChange={(e) => onChange(index, 'answer', e.target.value)}
placeholder='답변을 입력해주세요'
rows={1}
maxLength={ANSWER_MAX_LENGTH}
/>

Comment on lines +104 to +112
{faqs.map((faq, index) => (
<FAQItemEditor
key={index}
faq={faq}
index={index}
onChange={handleFieldChange}
onDelete={handleDelete}
/>
))}

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

자주 묻는 질문(FAQ) 목록은 사용자가 동적으로 추가하거나 삭제할 수 있는 동적 리스트입니다. React에서 indexkey로 사용하면, 특정 항목을 삭제했을 때 React가 DOM 엘리먼트와 컴포넌트 상태(예: 포커스, 입력값, useAutoGrow 내부 상태 등)를 잘못 매핑하여 엉뚱한 컴포넌트가 업데이트되거나 포커스를 잃어버리는 버그가 발생할 수 있습니다.

따라서 각 FAQ 항목에 고유한 id를 부여하고 이를 key로 사용하는 것이 안전합니다. 새로운 FAQ를 추가할 때 crypto.randomUUID() 등을 이용해 고유 ID를 생성해 주거나, 데이터 구조상 ID를 다루기 어렵다면 로컬 상태에서 고유한 키를 관리하도록 개선하는 것을 권장합니다.

Suggested change
{faqs.map((faq, index) => (
<FAQItemEditor
key={index}
faq={faq}
index={index}
onChange={handleFieldChange}
onDelete={handleDelete}
/>
))}
{faqs.map((faq, index) => (
<FAQItemEditor
key={faq.id || index}
faq={faq}
index={index}
onChange={handleFieldChange}
onDelete={handleDelete}
/>
))}

@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

🤖 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/CustomTextArea/CustomTextArea.tsx`:
- Line 38: `CustomTextArea`의 `useAutoGrow`에 전달하는 값이 `disabled ? '' : value`라서
disabled 상태에서는 입력값 변경이 훅에 반영되지 않습니다. `CustomTextArea`와 `useAutoGrow` 호출부를 수정해
disabled 여부와 무관하게 value 변경 시 auto-grow가 다시 실행되도록 하세요. 필요하면 `useAutoGrow` 내부에서
disabled일 때 높이 조정만 건너뛰고 의존값은 실제 value를 유지하도록 조정하세요.

In
`@frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.tsx`:
- Around line 36-47: `AwardSection`의 `Styled.AwardRow`가 클릭만 가능한 div로 동작해 키보드
접근성이 빠져 있습니다. `onClick`을 사용하는 두 분기(없음 상태와 `sortedAwards.map`)에서
`Styled.AwardRow`를 `button` 기반 인터랙티브 요소로 바꾸거나, 최소한 `role="button"`, `tabIndex`,
`onKeyDown`을 추가해 Enter/Space로도 `onNavigate`가 실행되도록 수정해 주세요.

In
`@frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.styles.ts`:
- Around line 84-95: The focus styles are missing for keyboard accessibility in
FAQSection.styles.ts: QuestionInput removes outline without any replacement, and
the related button components also lack visible focus feedback. Update the
styled components for QuestionInput and the button blocks in the FAQ section to
add clear :focus-visible styling so keyboard users can see the active element,
while preserving the current visual design for mouse interactions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e762a3ba-ace7-44bd-801e-29890ab8575d

📥 Commits

Reviewing files that changed from the base of the PR and between 7f6a8b5 and 4af36d1.

⛔ Files ignored due to path filters (2)
  • frontend/src/assets/images/icons/add_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/close_circle_icon.svg is excluded by !**/*.svg
📒 Files selected for processing (11)
  • frontend/src/components/common/CustomTextArea/CustomTextArea.tsx
  • frontend/src/hooks/useAutoGrow.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.styles.ts
  • 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/components/mobile/InfoSection/InfoSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.tsx

el.style.height = `${el.scrollHeight}px`;
}
}, [value]);
const textAreaRef = useAutoGrow(disabled ? '' : value);

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 | 🟠 Major | ⚡ Quick win

비활성화 상태에서 값이 바뀌면 auto-grow가 다시 돌지 않습니다.

disabled ? '' : value 때문에 disabled 동안에는 훅 의존값이 항상 동일합니다. 그래서 API 응답처럼 value가 나중에 채워지는 경우에도 높이가 갱신되지 않아 긴 텍스트가 한 줄 높이에 갇힐 수 있습니다.

제안 수정
-  const textAreaRef = useAutoGrow(disabled ? '' : value);
+  const textAreaRef = useAutoGrow(value);
📝 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 textAreaRef = useAutoGrow(disabled ? '' : value);
const textAreaRef = useAutoGrow(value);
🤖 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/CustomTextArea/CustomTextArea.tsx` at line 38,
`CustomTextArea`의 `useAutoGrow`에 전달하는 값이 `disabled ? '' : value`라서 disabled
상태에서는 입력값 변경이 훅에 반영되지 않습니다. `CustomTextArea`와 `useAutoGrow` 호출부를 수정해 disabled
여부와 무관하게 value 변경 시 auto-grow가 다시 실행되도록 하세요. 필요하면 `useAutoGrow` 내부에서 disabled일 때
높이 조정만 건너뛰고 의존값은 실제 value를 유지하도록 조정하세요.

Comment on lines +36 to +47
<Styled.AwardRow onClick={() => onNavigate?.()}>
<Styled.EmptyText>없음</Styled.EmptyText>
<Styled.NavButton>
<img src={rightArrowIcon} alt='편집' />
</Styled.NavButton>
</Styled.AwardRow>
) : (
sortedAwards.map((award) => (
<Styled.AwardRow
key={`${award.year}-${award.semesterTerm}`}
onClick={() => onNavigate?.(award)}
>

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 | 🟠 Major | ⚡ Quick win

클릭 가능한 div 대신 키보드 접근 가능한 인터랙티브 요소를 사용해 주세요.

Line 36, Line 44의 onClickdiv에 걸려 있어 키보드 탐색/활성화가 보장되지 않습니다. button(또는 role="button" + tabIndex + onKeyDown)으로 바꿔 접근성을 확보해 주세요.

🤖 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/components/mobile/AwardSection/AwardSection.tsx`
around lines 36 - 47, `AwardSection`의 `Styled.AwardRow`가 클릭만 가능한 div로 동작해 키보드
접근성이 빠져 있습니다. `onClick`을 사용하는 두 분기(없음 상태와 `sortedAwards.map`)에서
`Styled.AwardRow`를 `button` 기반 인터랙티브 요소로 바꾸거나, 최소한 `role="button"`, `tabIndex`,
`onKeyDown`을 추가해 Enter/Space로도 `onNavigate`가 실행되도록 수정해 주세요.

Comment on lines +84 to +95
export const QuestionInput = styled.input`
border: none;
outline: none;
background: transparent;
${setTypography(typography.paragraph.p6)}
color: ${colors.base.black};
width: 100%;

&::placeholder {
color: ${colors.gray[600]};
}
`;

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 | 🟠 Major | ⚡ Quick win

포커스 표시가 제거되어 키보드 사용자 접근성이 깨집니다.

Line 85-86, Line 123-124에서 outline을 제거했지만 대체 :focus-visible 스타일이 없습니다. 버튼(Line 97-108, Line 144-162)도 동일하게 포커스 표시를 추가해 주세요.

Also applies to: 97-108, 122-136, 144-162

🤖 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/components/mobile/FAQSection/FAQSection.styles.ts`
around lines 84 - 95, The focus styles are missing for keyboard accessibility in
FAQSection.styles.ts: QuestionInput removes outline without any replacement, and
the related button components also lack visible focus feedback. Update the
styled components for QuestionInput and the button blocks in the FAQ section to
add clear :focus-visible styling so keyboard users can see the active element,
while preserving the current visual design for mouse interactions.

@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/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx (1)

13-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

@/* alias import로 통일해주세요.

여기서만 상대 경로를 쓰면 파일 이동 시 경로가 다시 깨지기 쉽고, 현재 프론트엔드 import 규칙과도 어긋납니다.

권장 수정안
-import AwardEditor from './components/desktop/AwardEditor/AwardEditor';
-import FAQEditor from './components/desktop/FAQEditor/FAQEditor';
+import AwardEditor from '`@/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/AwardEditor/AwardEditor`';
+import FAQEditor from '`@/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/FAQEditor/FAQEditor`';

As per coding guidelines, "Use @/* path alias to map to src/* in imports".

🤖 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/ClubIntroEditTab.tsx`
around lines 13 - 14, The imports in ClubIntroEditTab should use the `@/`* alias
instead of relative paths, since the current two component imports break the
frontend import convention and are fragile when files move. Update the
AwardEditor and FAQEditor imports in ClubIntroEditTab to use the same
`@/src-style` aliasing pattern used elsewhere in the codebase.

Source: Coding guidelines

🤖 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/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx`:
- Around line 13-14: The imports in ClubIntroEditTab should use the `@/`* alias
instead of relative paths, since the current two component imports break the
frontend import convention and are fragile when files move. Update the
AwardEditor and FAQEditor imports in ClubIntroEditTab to use the same
`@/src-style` aliasing pattern used elsewhere in the codebase.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db17774f-5c6c-4572-9ae4-99e346627ac3

📥 Commits

Reviewing files that changed from the base of the PR and between 4af36d1 and b3fd425.

📒 Files selected for processing (8)
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/AwardEditor/AwardEditor.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/AwardEditor/AwardEditor.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/desktop/FAQEditor/FAQEditor.styles.ts
  • 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/FAQSection/FAQSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/InfoSection/InfoSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardSection/AwardSection.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/FAQSection/FAQSection.stories.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 +3 to +4
const useAutoGrow = (value: string) => {
const ref = useRef<HTMLTextAreaElement>(null);

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 +45 to +48
onChange={(e) => {
if (e.target.value.length <= ANSWER_MAX_LENGTH) {
onChange(index, 'answer', e.target.value);
}

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.

보통 저는 핸들러로 빼는 편인데 이 정도면 괜찮을까요? 어떻게 생각하시나요

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

말씀 주신 것처럼 핸들러 분리가 더 깔끔할 것 같아서 handleAnswerChange로 분리했습니다 1f59902

import { Award, SemesterTerm } from '@/types/club';
import * as Styled from './AwardSection.styles';

interface Props {

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.

이거 이름 변경해주세요

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다! a07f33f

@reviewer-harry

reviewer-harry Bot commented Jun 29, 2026

Copy link
Copy Markdown

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


🤖 harry 리뷰 (incremental)

이번 푸시(b3fd4251f59902)에서 새로 추가된 변경분만 확인했어요.

  • 리뷰 기준·변경분 확인
  • 변경 파일 리뷰

변경 요약

파일 변경 내용
AwardSection.tsx PropsAwardSectionProps 인터페이스명 명시화
FAQSection.tsx handleAnswerChange 핸들러 분리 + PropsFAQSectionProps
InfoSection.tsx PropsInfoSectionProps + 구조분해 포맷팅

총평

이번 변경분은 모두 이전 리뷰 피드백(인터페이스명 명시화, 답변 onChange 핸들러 분리)을 반영한 깔끔한 리팩터링이에요. Props 인터페이스명을 컴포넌트별로 명시해 가독성이 좋아졌고, handleAnswerChange 분리도 JSX가 한결 읽기 쉬워졌어요. 새로 추가된 줄 기준으로 룰 위반은 없어 통과예요. 수고하셨어요! 👍

ℹ️ 참고: 저장소에서 .github/harry-review-rules.md 파일을 찾지 못해 일반 리뷰 기준으로 확인했어요. (이전 리뷰들에서 지적된 maxLength 네이티브 속성·접근성 항목은 이번 변경분에 새로 추가된 내용이 아니라 반복하지 않았어요.)

@suhyun113 suhyun113 merged commit ac22ec6 into develop-fe Jun 30, 2026
8 checks passed
@suhyun113 suhyun113 deleted the feature/#1782-intro-section-components-MOA-1016 branch June 30, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants