Skip to content

[Feat] Modal 컴포넌트 제작#16

Merged
leemanjae02 merged 24 commits into
mainfrom
feat/modal-components-15
Jul 12, 2026
Merged

[Feat] Modal 컴포넌트 제작#16
leemanjae02 merged 24 commits into
mainfrom
feat/modal-components-15

Conversation

@leemanjae02

@leemanjae02 leemanjae02 commented Jul 11, 2026

Copy link
Copy Markdown
Member

#️⃣ 연관된 이슈

🚧 Work in Progress

  • 작업 진행 중

📌 주요 변경사항

  • Figma "Admin/Modal/Add" 컴포넌트셋 기준으로 기업/블로그/밋업/후기 등록 모달 4종 구현
  • 여러 모달에서 공통으로 쓰이는 Modal, SelectField, TextareaField, FormField 등 공통 컴포넌트 추가
  • 여러 도메인에서 재사용되는 Inputfield, ImageUploadBox를 introductions에서 common으로 이동
  • 아이콘 생성 파이프라인의 clipPath id 충돌 버그 수정(여러 아이콘 동시 렌더링 시 모양이 섞이던 문제)

📝 작업 내용

  • 모달 공통 컴포넌트 추가: Base UI Dialog/Select 기반 Modal, SelectField, TextareaField, FormField
  • 기업/블로그/밋업/후기 등록 모달 구현: Figma 스펙(색상, 타이포그래피, 간격) 기준 1:1 매칭
  • 재사용 컴포넌트 위치 정리: Inputfield, ImageUploadBox, useImageFileInput 훅을 common으로 이동
  • 밋업 모달 팀원 관리 플로우 반영: 입력행 작성 → 추가하기 → 확정된 행(읽기전용) 전환 + 삭제하기, 갱신된 Figma 플로우 기준으로 구현
  • 아이콘 생성 스크립트에 prefixIds 적용, add 아이콘 SVG의 불필요한 degenerate fill path 제거
  • 검증: pnpm lint, pnpm exec vitest run(104개 테스트 통과), pnpm build, pnpm gen:index:check, pnpm icons:check 모두 통과

📸 스크린샷 (선택)

스크린샷 2026-07-11 오후 2 33 29 스크린샷 2026-07-11 오후 2 33 02 스크린샷 2026-07-11 오후 2 32 52 스크린샷 2026-07-11 오후 3 44 05

💬 리뷰 요구사항(선택)

  • ImageUploadBox 빈 상태 높이가 공용 컴포넌트라 90px로 고정되어 있는데, 밋업 모달 "포스터 이미지"는 Figma 스펙상 94px입니다(4px 차이). 공용 높이를 유지할지 prop으로 분리할지 리뷰 부탁드립니다.

Summary by CodeRabbit

  • 새로운 기능
    • 블로그 후기/리뷰, 기업 프로젝트, 밋업 프로젝트 등록 모달을 추가했습니다(Modal/ModalActions).
    • 공통 폼(FormField), 선택(SelectField), 텍스트(TextareaField), 이미지 업로드(ImageUploadBox) 컴포넌트와 배너/썸네일 업로드 흐름을 제공합니다.
    • 라벨-입력 연결 및 선택 값 표시/드롭다운 동작을 포함합니다.
  • 개선
    • 아이콘 생성 시 SVG 내부 ID 충돌 가능성을 줄이도록 처리했습니다.
    • 입력 컴포넌트 포커스 링 스타일을 보강했습니다.
  • 테스트
    • 모달, 입력/선택/업로드, 버튼 상태와 주요 상호작용을 검증하는 테스트를 추가했습니다.

@github-actions github-actions Bot changed the title Modal 컴포넌트 제작 [Feat] Modal 컴포넌트 제작 Jul 11, 2026
@github-actions github-actions Bot added the ✨Feature 새로운 기능 label Jul 11, 2026
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

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

Walkthrough

공통 입력·선택·업로드 컴포넌트와 제어형 Modal, 네 가지 등록 모달을 추가했습니다. 각 모달의 Storybook 시나리오와 상호작용 테스트를 구성하고 barrel export를 확장했으며, SVG 생성 시 내부 ID prefix 처리를 적용했습니다.

Changes

공통 UI 및 등록 모달

Layer / File(s) Summary
공통 입력·업로드 기반
src/components/common/*, src/hooks/common/*, src/components/introductions/*
폼 필드, 선택 필드, 텍스트 영역, 기수 필드, 이미지 업로드 및 파일 입력 훅을 추가하고 관련 테스트·Storybook·경로를 갱신했습니다.
Modal 및 액션 기반
src/components/common/Modal/*, src/components/common/ModalActions/*
제어형 Modal, 오버레이, 제목·본문·푸터와 저장·취소 액션을 추가하고 Storybook 및 테스트를 구성했습니다.
블로그 후기 등록 모달
src/components/blog-reviews/BlogReviewModal/*
파트·활동·제목·썸네일 입력과 저장·취소 동작을 구현하고 Storybook 및 테스트를 추가했습니다.
기업 프로젝트 등록 모달
src/components/projects/CompanyProjectModal/*
기수·기업명·소개·배너 입력과 저장·취소 동작을 구현하고 Storybook 및 테스트를 추가했습니다.
밋업 프로젝트 등록 모달
src/components/projects/MeetupProjectModal/*
프로젝트 정보, 팀원 관리, 포스터 업로드, 링크 입력과 저장·취소 동작을 구현하고 Storybook 및 테스트를 추가했습니다.
후기 등록 모달
src/components/reviews/ReviewModal/*
팀·이름·내용 입력과 저장·취소 동작을 구현하고 Storybook 및 테스트를 추가했습니다.
컴포넌트 공개 경로 정리
src/components/index.ts, src/components/common/index.ts, src/components/projects/index.ts, src/hooks/index.ts
신규 컴포넌트와 공통 훅의 barrel export를 확장하고 introductions 기반 경로를 common 기반 경로로 변경했습니다.

SVG 아이콘 생성

Layer / File(s) Summary
SVG 내부 ID prefix 설정
scripts/build-icons.ts
SVGO prefixIds 플러그인에 컴포넌트 이름을 전달해 SVG 내부 ID와 참조를 prefix 처리하도록 변경했습니다.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 여러 모달 컴포넌트 추가라는 핵심 변경을 짧고 정확하게 요약합니다.
Linked Issues check ✅ Passed 직접 이슈의 4개 모달 제작 요구사항을 모두 구현했고, 관련 공용 컴포넌트도 함께 추가됐습니다.
Out of Scope Changes check ✅ Passed 스토리·테스트·배럴 export와 아이콘 ID prefix 수정은 공용화와 검증을 위한 범위 내 변경입니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/modal-components-15

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

@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

1 similar comment
@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

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

Actionable comments posted: 14

🤖 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 `@src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx`:
- Around line 34-90: BlogReviewModal 테스트에 활동 선택과 이미지 업로드 상호작용 검증이 누락되어 있습니다. 기존
파트 드롭다운 테스트 패턴을 참고해 활동 combobox에서 값을 선택한 뒤 onActivityChange 호출 인자를 검증하는 테스트를
추가하세요. 또한 ImageUploadBox를 통해 이미지 선택 시 onThumbnailChange, 삭제 시 onThumbnailDelete가
호출되는 테스트를 추가하고, 기존 renderModal 및 사용자 상호작용 방식을 재사용하세요.

In `@src/components/common/FormField/FormField.tsx`:
- Around line 12-14: FormField의 시각적 span을 실제 label 요소로 변경하고, 고유한 id를 생성하거나 전달받아
htmlFor로 연결하세요. FormField 호출부의 input 및 select 컨트롤에도 동일한 id를 지정하고, 커스텀 선택 컴포넌트는
필요 시 aria-labelledby로 라벨과 연결되도록 관련 props와 구현을 업데이트하세요.

In `@src/components/common/Inputfield/Inputfield.tsx`:
- Around line 16-18: Inputfield의 input에 outline-none만 적용되어 키보드 포커스 표시가 없습니다.
Inputfield 컴포넌트의 input className에 프로젝트 포커스 토큰을 사용하는 focus-visible 스타일을 추가하거나,
래퍼에 focus-within 스타일을 적용해 키보드 사용 시 명확한 포커스 링이 표시되도록 수정하세요.

In `@src/components/common/Modal/Modal.test.tsx`:
- Around line 6-56: Modal 테스트에 Dialog.Backdrop 클릭 동작을 검증하는 테스트를 추가하세요. 기존 Modal
테스트 구조에서 열려 있는 모달과 mock onOpenChange를 렌더링한 뒤, backdrop 요소를 식별해 클릭하고
onOpenChange가 false와 이벤트 인자로 호출되는지 확인하세요.

In `@src/components/common/Modal/Modal.tsx`:
- Around line 19-23: Dialog.Popup의 고정 너비 설정에 좁은 뷰포트 대응을 추가하세요. className 구성에서
w-[460px]와 함께 max-w-[calc(100vw-2rem)] 같은 viewport 기반 max-width 클래스를 지정해 모바일
화면에서 모달이 가로로 넘치지 않도록 수정하세요.

In `@src/components/common/TextareaField/TextareaField.tsx`:
- Around line 16-18: The TextareaField textarea removes the default focus
indicator without providing a keyboard focus replacement. Update the textarea
class list in TextareaField to add the project’s focus-token-based focus-visible
styling while retaining the existing appearance and behavior.

In `@src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx`:
- Around line 23-68: CompanyProjectModal tests are missing coverage for banner
upload and deletion callbacks. Add tests alongside the existing input and button
interaction tests that locate the ImageUploadBox interaction controls, trigger
banner selection and deletion, and assert onBannerChange and onBannerDelete are
called with the expected values.

In `@src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx`:
- Around line 66-68: Replace the options-empty SelectField used for the “기수”
display in CompanyProjectModal with the shared read-only/static-value approach,
and apply the same common fix to the corresponding field in BlogReviewModal.
Ensure the rendered cardinal value remains unchanged without treating it as a
selectable field.
- Around line 79-87: Replace the `Inputfield` used in the `프로젝트 소개` `FormField`
within `CompanyProjectModal` with `TextareaField`, preserving the existing
`value`, `onChange`, and `placeholder` props so multiline project descriptions
are supported.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx`:
- Around line 46-105: MeetupProjectModal 테스트에 주요 controlled callback 상호작용 검증이
부족하다. 기존 describe('MeetupProjectModal')에 프로젝트명·URL 입력 변경 후 onTypeChange 호출, 취소
버튼 클릭 후 onCancel 호출, 저장하기 버튼 클릭 후 onSave 호출, 포스터 파일 선택 및 삭제 후 각각
onFileChange·onDelete 호출을 확인하는 최소 테스트를 추가하라. 기존 renderModal 헬퍼와 실제 접근 가능한 역할·라벨을
사용해 각 콜백의 인자까지 검증하라.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx`:
- Around line 115-118: MeetupProjectModal의 기수 필드가 빈 options를 가진 SelectField로
렌더링되고 변경 처리도 없습니다. 해당 필드를 고정 표시로 사용할 경우 SelectField 대신 readOnly 입력으로 변경하고, 변경
가능해야 한다면 유효한 기수 옵션과 onValueChange 콜백을 연결하세요.
- Around line 146-154: 프로젝트 소개 입력이 한 줄 전용 Inputfield로 구현되어 있습니다.
MeetupProjectModal의 프로젝트 소개 FormField에서 Inputfield를 공통 TextareaField로 교체하고, 기존
intro 값·onIntroChange 핸들러·placeholder가 그대로 동작하도록 연결하세요.
- Around line 155-203: 팀원 관리의 반복되는 SelectField와 이름 Inputfield, 그리고 URL 입력 컨트롤에
고유하고 명확한 접근성 이름을 추가하세요. draft 필드와 각 member 행의 컨트롤이 서로 다른 id와 label로 연결되도록 하거나,
최소한 member 이름/파트를 식별하는 aria-label을 설정하며, 관련 URL 입력에도 동일한 방식으로 적용하세요.

In `@src/components/reviews/ReviewModal/ReviewModal.test.tsx`:
- Around line 11-18: ReviewModal 테스트에 실제 팀 선택 변경 시나리오를 추가하세요. renderModal의
onTeamChange mock을 사용해 팀 선택 UI를 조작한 뒤, 선택한 값이 인자로 전달되어 onTeamChange가 호출되는지
검증하세요. 기본 렌더링 테스트와 별도로 부모 상태 갱신 연결을 확인할 수 있도록 구성하세요.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: cc6018e2-a6b9-48b0-aa4d-5707eedd1091

📥 Commits

Reviewing files that changed from the base of the PR and between 8f78b4d and 3eed7c9.

⛔ Files ignored due to path filters (11)
  • src/assets/icons/generated/AddIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/AttachFileIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/ChevronDownIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/DashboardIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/DeleteIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/ErrorIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/IntroductionIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/LogoutIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/MentoringIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/UploadIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/svg/add.svg is excluded by !**/*.svg, !**/*.svg
📒 Files selected for processing (48)
  • scripts/build-icons.ts
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.stories.tsx
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx
  • src/components/blog-reviews/BlogReviewModal/index.ts
  • src/components/blog-reviews/index.ts
  • src/components/common/FormField/FormField.tsx
  • src/components/common/FormField/index.ts
  • src/components/common/ImageUploadBox/ImageUploadBox.stories.tsx
  • src/components/common/ImageUploadBox/ImageUploadBox.test.tsx
  • src/components/common/ImageUploadBox/ImageUploadBox.tsx
  • src/components/common/ImageUploadBox/index.ts
  • src/components/common/Inputfield/Inputfield.stories.tsx
  • src/components/common/Inputfield/Inputfield.tsx
  • src/components/common/Inputfield/index.ts
  • src/components/common/Modal/Modal.stories.tsx
  • src/components/common/Modal/Modal.test.tsx
  • src/components/common/Modal/Modal.tsx
  • src/components/common/Modal/index.ts
  • src/components/common/SelectField/SelectField.stories.tsx
  • src/components/common/SelectField/SelectField.test.tsx
  • src/components/common/SelectField/SelectField.tsx
  • src/components/common/SelectField/index.ts
  • src/components/common/TextareaField/TextareaField.stories.tsx
  • src/components/common/TextareaField/TextareaField.tsx
  • src/components/common/TextareaField/index.ts
  • src/components/common/index.ts
  • src/components/index.ts
  • src/components/introductions/IntroductionCard/IntroductionCard.tsx
  • src/components/introductions/PartnerImageBox/PartnerImageBox.tsx
  • src/components/introductions/index.ts
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.stories.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx
  • src/components/projects/CompanyProjectModal/index.ts
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.stories.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx
  • src/components/projects/MeetupProjectModal/index.ts
  • src/components/projects/index.ts
  • src/components/reviews/ReviewModal/ReviewModal.stories.tsx
  • src/components/reviews/ReviewModal/ReviewModal.test.tsx
  • src/components/reviews/ReviewModal/ReviewModal.tsx
  • src/components/reviews/ReviewModal/index.ts
  • src/components/reviews/index.ts
  • src/hooks/common/index.ts
  • src/hooks/common/useImageFileInput.ts
  • src/hooks/index.ts
💤 Files with no reviewable changes (1)
  • src/components/introductions/index.ts

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

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: 14

🤖 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 `@src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx`:
- Around line 34-90: BlogReviewModal 테스트에 활동 선택과 이미지 업로드 상호작용 검증이 누락되어 있습니다. 기존
파트 드롭다운 테스트 패턴을 참고해 활동 combobox에서 값을 선택한 뒤 onActivityChange 호출 인자를 검증하는 테스트를
추가하세요. 또한 ImageUploadBox를 통해 이미지 선택 시 onThumbnailChange, 삭제 시 onThumbnailDelete가
호출되는 테스트를 추가하고, 기존 renderModal 및 사용자 상호작용 방식을 재사용하세요.

In `@src/components/common/FormField/FormField.tsx`:
- Around line 12-14: FormField의 시각적 span을 실제 label 요소로 변경하고, 고유한 id를 생성하거나 전달받아
htmlFor로 연결하세요. FormField 호출부의 input 및 select 컨트롤에도 동일한 id를 지정하고, 커스텀 선택 컴포넌트는
필요 시 aria-labelledby로 라벨과 연결되도록 관련 props와 구현을 업데이트하세요.

In `@src/components/common/Inputfield/Inputfield.tsx`:
- Around line 16-18: Inputfield의 input에 outline-none만 적용되어 키보드 포커스 표시가 없습니다.
Inputfield 컴포넌트의 input className에 프로젝트 포커스 토큰을 사용하는 focus-visible 스타일을 추가하거나,
래퍼에 focus-within 스타일을 적용해 키보드 사용 시 명확한 포커스 링이 표시되도록 수정하세요.

In `@src/components/common/Modal/Modal.test.tsx`:
- Around line 6-56: Modal 테스트에 Dialog.Backdrop 클릭 동작을 검증하는 테스트를 추가하세요. 기존 Modal
테스트 구조에서 열려 있는 모달과 mock onOpenChange를 렌더링한 뒤, backdrop 요소를 식별해 클릭하고
onOpenChange가 false와 이벤트 인자로 호출되는지 확인하세요.

In `@src/components/common/Modal/Modal.tsx`:
- Around line 19-23: Dialog.Popup의 고정 너비 설정에 좁은 뷰포트 대응을 추가하세요. className 구성에서
w-[460px]와 함께 max-w-[calc(100vw-2rem)] 같은 viewport 기반 max-width 클래스를 지정해 모바일
화면에서 모달이 가로로 넘치지 않도록 수정하세요.

In `@src/components/common/TextareaField/TextareaField.tsx`:
- Around line 16-18: The TextareaField textarea removes the default focus
indicator without providing a keyboard focus replacement. Update the textarea
class list in TextareaField to add the project’s focus-token-based focus-visible
styling while retaining the existing appearance and behavior.

In `@src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx`:
- Around line 23-68: CompanyProjectModal tests are missing coverage for banner
upload and deletion callbacks. Add tests alongside the existing input and button
interaction tests that locate the ImageUploadBox interaction controls, trigger
banner selection and deletion, and assert onBannerChange and onBannerDelete are
called with the expected values.

In `@src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx`:
- Around line 66-68: Replace the options-empty SelectField used for the “기수”
display in CompanyProjectModal with the shared read-only/static-value approach,
and apply the same common fix to the corresponding field in BlogReviewModal.
Ensure the rendered cardinal value remains unchanged without treating it as a
selectable field.
- Around line 79-87: Replace the `Inputfield` used in the `프로젝트 소개` `FormField`
within `CompanyProjectModal` with `TextareaField`, preserving the existing
`value`, `onChange`, and `placeholder` props so multiline project descriptions
are supported.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx`:
- Around line 46-105: MeetupProjectModal 테스트에 주요 controlled callback 상호작용 검증이
부족하다. 기존 describe('MeetupProjectModal')에 프로젝트명·URL 입력 변경 후 onTypeChange 호출, 취소
버튼 클릭 후 onCancel 호출, 저장하기 버튼 클릭 후 onSave 호출, 포스터 파일 선택 및 삭제 후 각각
onFileChange·onDelete 호출을 확인하는 최소 테스트를 추가하라. 기존 renderModal 헬퍼와 실제 접근 가능한 역할·라벨을
사용해 각 콜백의 인자까지 검증하라.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx`:
- Around line 115-118: MeetupProjectModal의 기수 필드가 빈 options를 가진 SelectField로
렌더링되고 변경 처리도 없습니다. 해당 필드를 고정 표시로 사용할 경우 SelectField 대신 readOnly 입력으로 변경하고, 변경
가능해야 한다면 유효한 기수 옵션과 onValueChange 콜백을 연결하세요.
- Around line 146-154: 프로젝트 소개 입력이 한 줄 전용 Inputfield로 구현되어 있습니다.
MeetupProjectModal의 프로젝트 소개 FormField에서 Inputfield를 공통 TextareaField로 교체하고, 기존
intro 값·onIntroChange 핸들러·placeholder가 그대로 동작하도록 연결하세요.
- Around line 155-203: 팀원 관리의 반복되는 SelectField와 이름 Inputfield, 그리고 URL 입력 컨트롤에
고유하고 명확한 접근성 이름을 추가하세요. draft 필드와 각 member 행의 컨트롤이 서로 다른 id와 label로 연결되도록 하거나,
최소한 member 이름/파트를 식별하는 aria-label을 설정하며, 관련 URL 입력에도 동일한 방식으로 적용하세요.

In `@src/components/reviews/ReviewModal/ReviewModal.test.tsx`:
- Around line 11-18: ReviewModal 테스트에 실제 팀 선택 변경 시나리오를 추가하세요. renderModal의
onTeamChange mock을 사용해 팀 선택 UI를 조작한 뒤, 선택한 값이 인자로 전달되어 onTeamChange가 호출되는지
검증하세요. 기본 렌더링 테스트와 별도로 부모 상태 갱신 연결을 확인할 수 있도록 구성하세요.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: cc6018e2-a6b9-48b0-aa4d-5707eedd1091

📥 Commits

Reviewing files that changed from the base of the PR and between 8f78b4d and 3eed7c9.

⛔ Files ignored due to path filters (11)
  • src/assets/icons/generated/AddIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/AttachFileIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/ChevronDownIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/DashboardIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/DeleteIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/ErrorIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/IntroductionIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/LogoutIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/MentoringIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/generated/UploadIcon.tsx is excluded by !**/generated/**
  • src/assets/icons/svg/add.svg is excluded by !**/*.svg, !**/*.svg
📒 Files selected for processing (48)
  • scripts/build-icons.ts
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.stories.tsx
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx
  • src/components/blog-reviews/BlogReviewModal/index.ts
  • src/components/blog-reviews/index.ts
  • src/components/common/FormField/FormField.tsx
  • src/components/common/FormField/index.ts
  • src/components/common/ImageUploadBox/ImageUploadBox.stories.tsx
  • src/components/common/ImageUploadBox/ImageUploadBox.test.tsx
  • src/components/common/ImageUploadBox/ImageUploadBox.tsx
  • src/components/common/ImageUploadBox/index.ts
  • src/components/common/Inputfield/Inputfield.stories.tsx
  • src/components/common/Inputfield/Inputfield.tsx
  • src/components/common/Inputfield/index.ts
  • src/components/common/Modal/Modal.stories.tsx
  • src/components/common/Modal/Modal.test.tsx
  • src/components/common/Modal/Modal.tsx
  • src/components/common/Modal/index.ts
  • src/components/common/SelectField/SelectField.stories.tsx
  • src/components/common/SelectField/SelectField.test.tsx
  • src/components/common/SelectField/SelectField.tsx
  • src/components/common/SelectField/index.ts
  • src/components/common/TextareaField/TextareaField.stories.tsx
  • src/components/common/TextareaField/TextareaField.tsx
  • src/components/common/TextareaField/index.ts
  • src/components/common/index.ts
  • src/components/index.ts
  • src/components/introductions/IntroductionCard/IntroductionCard.tsx
  • src/components/introductions/PartnerImageBox/PartnerImageBox.tsx
  • src/components/introductions/index.ts
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.stories.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx
  • src/components/projects/CompanyProjectModal/index.ts
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.stories.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx
  • src/components/projects/MeetupProjectModal/index.ts
  • src/components/projects/index.ts
  • src/components/reviews/ReviewModal/ReviewModal.stories.tsx
  • src/components/reviews/ReviewModal/ReviewModal.test.tsx
  • src/components/reviews/ReviewModal/ReviewModal.tsx
  • src/components/reviews/ReviewModal/index.ts
  • src/components/reviews/index.ts
  • src/hooks/common/index.ts
  • src/hooks/common/useImageFileInput.ts
  • src/hooks/index.ts
💤 Files with no reviewable changes (1)
  • src/components/introductions/index.ts
🛑 Comments failed to post (14)
src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx (1)

34-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

활동 선택 및 이미지 업로드 상호작용 테스트 누락

파트 드롭다운 선택 테스트는 있지만 활동 드롭다운 선택 시 onActivityChange 호출 여부, ImageUploadBox를 통한 onThumbnailChange/onThumbnailDelete 호출 여부에 대한 테스트가 없습니다. 파트 테스트 패턴을 그대로 재사용할 수 있어 추가 비용이 낮습니다.

🤖 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 `@src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx` around
lines 34 - 90, BlogReviewModal 테스트에 활동 선택과 이미지 업로드 상호작용 검증이 누락되어 있습니다. 기존 파트
드롭다운 테스트 패턴을 참고해 활동 combobox에서 값을 선택한 뒤 onActivityChange 호출 인자를 검증하는 테스트를 추가하세요.
또한 ImageUploadBox를 통해 이미지 선택 시 onThumbnailChange, 삭제 시 onThumbnailDelete가 호출되는
테스트를 추가하고, 기존 renderModal 및 사용자 상호작용 방식을 재사용하세요.

Source: Path instructions

src/components/common/FormField/FormField.tsx (1)

12-14: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

라벨을 실제 폼 컨트롤과 연결해 주세요.

span은 시각적 텍스트만 제공하므로 입력/선택 필드와의 관계가 전달되지 않습니다. htmlFor와 고유 id 계약을 추가해 <label>로 렌더링하고, 호출부에서도 연결해야 합니다. 커스텀 선택 컴포넌트는 필요에 따라 aria-labelledby를 사용하세요.

🤖 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 `@src/components/common/FormField/FormField.tsx` around lines 12 - 14,
FormField의 시각적 span을 실제 label 요소로 변경하고, 고유한 id를 생성하거나 전달받아 htmlFor로 연결하세요.
FormField 호출부의 input 및 select 컨트롤에도 동일한 id를 지정하고, 커스텀 선택 컴포넌트는 필요 시
aria-labelledby로 라벨과 연결되도록 관련 props와 구현을 업데이트하세요.

Source: Path instructions

src/components/common/Inputfield/Inputfield.tsx (1)

16-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] file list"
git ls-files 'src/components/common/Inputfield/Inputfield.tsx' 'src/components/common/**' | sed -n '1,120p'

echo
echo "[2] outline"
ast-grep outline src/components/common/Inputfield/Inputfield.tsx --view expanded

echo
echo "[3] file content"
cat -n src/components/common/Inputfield/Inputfield.tsx

echo
echo "[4] search for focus-visible/focus-within patterns in common components"
rg -n --hidden --glob 'src/components/common/**' 'focus-visible|focus-within|outline-none|ring-|focus:' .

Repository: kusitms-com/makers-admin-fe

Length of output: 5118


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] focus-related patterns across src"
rg -n --hidden --glob 'src/**' 'focus-visible|focus-within|ring-|outline-none|focus:' src

echo
echo "[2] inspect TextareaField"
cat -n src/components/common/TextareaField/TextareaField.tsx

echo
echo "[3] inspect SelectField around focus styles"
sed -n '1,140p' src/components/common/SelectField/SelectField.tsx

echo
echo "[4] inspect Button for shared interaction styling"
sed -n '1,140p' src/components/common/Button/Button.tsx

Repository: kusitms-com/makers-admin-fe

Length of output: 6961


키보드 포커스 표시를 추가해 주세요. outline-none으로 기본 포커스 링을 제거했지만 대체 focus-visible 또는 래퍼의 focus-within 상태가 없습니다. 키보드 사용자가 현재 입력 위치를 확인할 수 있도록 프로젝트 포커스 토큰을 적용해 주세요.

🤖 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 `@src/components/common/Inputfield/Inputfield.tsx` around lines 16 - 18,
Inputfield의 input에 outline-none만 적용되어 키보드 포커스 표시가 없습니다. Inputfield 컴포넌트의 input
className에 프로젝트 포커스 토큰을 사용하는 focus-visible 스타일을 추가하거나, 래퍼에 focus-within 스타일을 적용해
키보드 사용 시 명확한 포커스 링이 표시되도록 수정하세요.

Source: Path instructions

src/components/common/Modal/Modal.test.tsx (1)

6-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

backdrop 클릭으로 닫히는 동작에 대한 테스트 누락

open/close, Escape, footer 렌더링은 잘 커버되지만 Dialog.Backdrop 클릭 시 onOpenChange(false, ...)가 호출되는지 검증하는 테스트가 없습니다. 모달의 핵심 UX 동작 중 하나이므로 추가하면 회귀 방지에 도움이 됩니다.

🤖 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 `@src/components/common/Modal/Modal.test.tsx` around lines 6 - 56, Modal 테스트에
Dialog.Backdrop 클릭 동작을 검증하는 테스트를 추가하세요. 기존 Modal 테스트 구조에서 열려 있는 모달과 mock
onOpenChange를 렌더링한 뒤, backdrop 요소를 식별해 클릭하고 onOpenChange가 false와 이벤트 인자로 호출되는지
확인하세요.
src/components/common/Modal/Modal.tsx (1)

19-23: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

좁은 뷰포트에서 모달 폭을 제한해야 합니다.

w-[460px]만 지정되어 모바일이나 좁은 브라우저에서는 모달이 화면을 가로로 넘칠 수 있습니다. 저장·취소 버튼과 입력 필드가 화면 밖으로 밀리지 않도록 max-w-[calc(100vw-2rem)] 같은 viewport 기반 제한을 추가해 주세요.

권장 수정
-            'border-line-neutral bg-fill-normal fixed top-1/2 left-1/2 z-50 flex max-h-[90vh] w-[460px] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border shadow-[0px_16px_24px_-6px_rgba(23,23,23,0.08),0px_6px_10px_-4px_rgba(23,23,23,0.08)]',
+            'border-line-neutral bg-fill-normal fixed top-1/2 left-1/2 z-50 flex max-h-[90vh] w-[460px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border shadow-[0px_16px_24px_-6px_rgba(23,23,23,0.08),0px_6px_10px_-4px_rgba(23,23,23,0.08)]',
📝 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.

        <Dialog.Popup
          className={cn(
            'border-line-neutral bg-fill-normal fixed top-1/2 left-1/2 z-50 flex max-h-[90vh] w-[460px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border shadow-[0px_16px_24px_-6px_rgba(23,23,23,0.08),0px_6px_10px_-4px_rgba(23,23,23,0.08)]',
            className,
          )}
🤖 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 `@src/components/common/Modal/Modal.tsx` around lines 19 - 23, Dialog.Popup의 고정
너비 설정에 좁은 뷰포트 대응을 추가하세요. className 구성에서 w-[460px]와 함께 max-w-[calc(100vw-2rem)]
같은 viewport 기반 max-width 클래스를 지정해 모바일 화면에서 모달이 가로로 넘치지 않도록 수정하세요.
src/components/common/TextareaField/TextareaField.tsx (1)

16-18: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

키보드 포커스 표시를 복구해야 합니다.

outline-none으로 기본 포커스 표시를 제거했지만 focus-visible 대체 스타일이 없습니다. 키보드 사용자가 현재 입력 필드를 확인할 수 있도록 프로젝트 포커스 토큰을 사용한 visible focus style을 추가해 주세요.

🤖 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 `@src/components/common/TextareaField/TextareaField.tsx` around lines 16 - 18,
The TextareaField textarea removes the default focus indicator without providing
a keyboard focus replacement. Update the textarea class list in TextareaField to
add the project’s focus-token-based focus-visible styling while retaining the
existing appearance and behavior.

Source: Path instructions

src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx (1)

23-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

배너 업로드 상호작용 테스트 누락

onBannerChange/onBannerDelete 콜백에 대한 테스트가 없습니다. "기업 이름" 입력 테스트 패턴을 재사용해 ImageUploadBox 상호작용도 검증하는 것을 권장합니다.

🤖 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 `@src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx`
around lines 23 - 68, CompanyProjectModal tests are missing coverage for banner
upload and deletion callbacks. Add tests alongside the existing input and button
interaction tests that locate the ImageUploadBox interaction controls, trigger
banner selection and deletion, and assert onBannerChange and onBannerDelete are
called with the expected values.

Source: Path instructions

src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx (2)

66-68: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

"기수" 필드 SelectField 재사용 - 동일 문제 반복

BlogReviewModal.tsx Line 74-76과 동일하게 정적 값 표시에 옵션 없는 SelectField를 사용하고 있습니다. 같은 근본 원인이므로 공통 수정이 필요합니다.

🤖 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 `@src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx` around
lines 66 - 68, Replace the options-empty SelectField used for the “기수” display
in CompanyProjectModal with the shared read-only/static-value approach, and
apply the same common fix to the corresponding field in BlogReviewModal. Ensure
the rendered cardinal value remains unchanged without treating it as a
selectable field.

79-87: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- CompanyProjectModal.tsx outline ---\n'
ast-grep outline src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx --view expanded || true

printf '\n--- TextareaField files ---\n'
fd -a 'TextareaField.*' src || true

printf '\n--- Inputfield files ---\n'
fd -a 'Inputfield.*' src || true

Repository: kusitms-com/makers-admin-fe

Length of output: 256


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant component and the common textarea field in small slices.
for f in \
  src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx \
  src/components/common/TextareaField/TextareaField.tsx \
  src/components/common/Inputfield/Inputfield.tsx
do
  if [ -f "$f" ]; then
    echo "===== $f ====="
    wc -l "$f"
  fi
done

echo "===== CompanyProjectModal.tsx (lines 1-180) ====="
sed -n '1,180p' src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx

echo "===== TextareaField.tsx ====="
sed -n '1,220p' src/components/common/TextareaField/TextareaField.tsx

echo "===== Inputfield.tsx ====="
sed -n '1,220p' src/components/common/Inputfield/Inputfield.tsx

Repository: kusitms-com/makers-admin-fe

Length of output: 4512


프로젝트 소개TextareaField로 교체하세요.
지금 Inputfield라서 여러 줄 입력이 막히고, 소개 영역 의도와도 맞지 않습니다. TextareaField를 쓰면 onChange 타입도 그대로 맞습니다.

🤖 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 `@src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx` around
lines 79 - 87, Replace the `Inputfield` used in the `프로젝트 소개` `FormField` within
`CompanyProjectModal` with `TextareaField`, preserving the existing `value`,
`onChange`, and `placeholder` props so multiline project descriptions are
supported.
src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx (1)

46-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

주요 controlled callback 테스트를 보강하세요.

현재 테스트는 팀원 흐름과 저장 버튼 비활성화만 검증합니다. onTypeChange, 프로젝트/URL 입력 변경, onCancel, onSave, 포스터 onFileChange·onDelete가 올바르게 호출되는지 최소한의 상호작용 테스트를 추가해야 wiring 회귀를 잡을 수 있습니다.

🤖 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 `@src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx`
around lines 46 - 105, MeetupProjectModal 테스트에 주요 controlled callback 상호작용 검증이
부족하다. 기존 describe('MeetupProjectModal')에 프로젝트명·URL 입력 변경 후 onTypeChange 호출, 취소
버튼 클릭 후 onCancel 호출, 저장하기 버튼 클릭 후 onSave 호출, 포스터 파일 선택 및 삭제 후 각각
onFileChange·onDelete 호출을 확인하는 최소 테스트를 추가하라. 기존 renderModal 헬퍼와 실제 접근 가능한 역할·라벨을
사용해 각 콜백의 인자까지 검증하라.

Source: Path instructions

src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx (3)

115-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

기수 필드에 빈 선택지를 넘기지 마세요.

SelectFieldoptions={[]}을 전달하고 변경 콜백도 연결하지 않아, 선택 컨트롤이지만 사용자가 선택할 항목이 없습니다. 고정 표시라면 readOnly 입력으로 렌더링하고, 변경 가능해야 한다면 실제 옵션과 onValueChange를 전달하세요.

🤖 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 `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx` around
lines 115 - 118, MeetupProjectModal의 기수 필드가 빈 options를 가진 SelectField로 렌더링되고 변경
처리도 없습니다. 해당 필드를 고정 표시로 사용할 경우 SelectField 대신 readOnly 입력으로 변경하고, 변경 가능해야 한다면
유효한 기수 옵션과 onValueChange 콜백을 연결하세요.

146-154: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

프로젝트 소개를 TextareaField로 변경하세요.

현재 Inputfield라서 한 줄만 입력할 수 있습니다. PR 요구사항의 프로젝트 소개는 긴 여러 줄 텍스트를 입력하는 영역이므로, 공통 TextareaField를 사용해야 합니다.

🤖 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 `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx` around
lines 146 - 154, 프로젝트 소개 입력이 한 줄 전용 Inputfield로 구현되어 있습니다. MeetupProjectModal의
프로젝트 소개 FormField에서 Inputfield를 공통 TextareaField로 교체하고, 기존 intro 값·onIntroChange
핸들러·placeholder가 그대로 동작하도록 연결하세요.

155-203: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

반복 필드에 개별 접근 가능한 이름을 연결하세요.

팀원 관리의 각 선택/이름 필드와 URL 입력은 외부 그룹 라벨 또는 일반 span에만 의존합니다. 특히 저장된 팀원 이름 입력에는 라벨이나 placeholder도 없어 여러 행을 스크린 리더로 구분할 수 없습니다. 각 컨트롤에 label-id 연결 또는 명확한 aria-label을 추가하세요.

Also applies to: 213-244

🤖 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 `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx` around
lines 155 - 203, 팀원 관리의 반복되는 SelectField와 이름 Inputfield, 그리고 URL 입력 컨트롤에 고유하고
명확한 접근성 이름을 추가하세요. draft 필드와 각 member 행의 컨트롤이 서로 다른 id와 label로 연결되도록 하거나, 최소한
member 이름/파트를 식별하는 aria-label을 설정하며, 관련 URL 입력에도 동일한 방식으로 적용하세요.

Source: Path instructions

src/components/reviews/ReviewModal/ReviewModal.test.tsx (1)

11-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

파트 선택 콜백을 테스트해 주세요.

onTeamChange를 mock으로 주입하지만 실제 선택 변경은 검증하지 않습니다. 선택 후 부모 상태 갱신이 끊기는 회귀를 방지하도록 선택값과 함께 onTeamChange 호출을 확인하는 테스트를 추가하세요.

Also applies to: 41-57

🤖 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 `@src/components/reviews/ReviewModal/ReviewModal.test.tsx` around lines 11 -
18, ReviewModal 테스트에 실제 팀 선택 변경 시나리오를 추가하세요. renderModal의 onTeamChange mock을 사용해
팀 선택 UI를 조작한 뒤, 선택한 값이 인자로 전달되어 onTeamChange가 호출되는지 검증하세요. 기본 렌더링 테스트와 별도로 부모 상태
갱신 연결을 확인할 수 있도록 구성하세요.

Source: Path instructions

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

수고하셨습니다!! 확인 부탁드려요~ 👍

export function FormField({ label, children, className }: FormFieldProps) {
return (
<div className={cn('flex flex-col gap-2', className)}>
<span className="text-label-13b text-label-normal">{label}</span>

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.

단순 span으로 렌더링 하지 말고 접근성 챙겨서 label 태그로 연결하는건 어떨까요?

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.

Base UI로 접근성 챙겨서 사용한 거 좋은 것 같아요!! 다만 공용 Modal 구조가 고정적이라 예외 처리마다 prop이 늘어날 수 있을 수도 있을 것 같은데, header/footer 같은 slot 구조로 열어두면 더 유연할 것 같습니다!!

@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

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

Actionable comments posted: 12

🤖 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 `@src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx`:
- Around line 87-100: Extract the duplicated cancel/save button block from the
BlogReviewModal footer into the shared footer component identified by the
consolidate guidance, then reuse that component here with the existing onCancel,
onSave, saveDisabled, variants, sizes, and labels preserved.
- Around line 55-57: Extract the repeated 기수 FormField and SelectField markup
from BlogReviewModal into a shared component, then reuse it in BlogReviewModal,
CompanyProjectModal, and MeetupProjectModal. Preserve the current
`${String(cardinal)}기` value and existing field styling and behavior across all
three modals.

In `@src/components/common/FormField/FormField.tsx`:
- Around line 11-20: Update the FormField ID handling so the child’s existing id
is preferred, otherwise use the generated fieldId; use this same resolved ID for
both the cloned child’s id and the label’s htmlFor, preserving the label-field
accessibility connection.

In `@src/components/common/Modal/Modal.tsx`:
- Around line 79-83: Resolve the Fast Refresh warning around the compound
`Modal` export by moving the `Object.assign` composition into a separate barrel
module, or by adding a narrowly scoped exception for this compound-component
pattern. Keep `ModalRoot`, `ModalTitle`, `ModalBody`, and `ModalFooter` exports
and the public `Modal` API unchanged.

In `@src/components/common/SelectField/SelectField.tsx`:
- Around line 36-40: Update the read-only fallback in SelectField so the element
rendered when onValueChange is absent is labelable and supports FormField’s
htmlFor connection; replace the generic div while preserving its id, aria-label,
classes, and selectedLabel content. Add a test covering the FormField +
SelectField read-only combination and verify getByLabelText('기수') resolves the
field.

In `@src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx`:
- Around line 47-49: Remove the duplicated 기수 field block from
CompanyProjectModal and reuse the shared component or existing abstraction used
by BlogReviewModal and MeetupProjectModal. Preserve the current cardinal value
formatting and layout while consolidating the common implementation.
- Around line 77-90: CompanyProjectModal의 Modal.Footer 버튼 블록이 BlogReviewModal 및
MeetupProjectModal과 중복됩니다. 공통 Footer 버튼 UI를 재사용 가능한 컴포넌트 또는 기존 공통 구현으로 통합하고,
CompanyProjectModal의 취소·저장 동작과 saveDisabled 상태 전달은 그대로 유지하세요.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx`:
- Around line 128-136: Update the saveDisabled=false test to query the single
저장하기 button with a singular role query instead of getAllByRole and selecting the
last element, then click that button and preserve the existing onSave assertion.

In `@src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx`:
- Around line 97-99: Remove the duplicated 기수 FormField block from
MeetupProjectModal and reuse the shared implementation or component already used
by CompanyProjectModal and BlogReviewModal, preserving the existing cardinal
value and layout behavior.
- Around line 240-253: MeetupProjectModal의 Modal.Footer 버튼 블록이
CompanyProjectModal 및 BlogReviewModal과 중복됩니다. 해당 공통 Footer 버튼 UI를 재사용 가능한 기존
컴포넌트 또는 공통 패턴으로 통합하고, MeetupProjectModal에서는 saveDisabled, onCancel, onSave 등 필요한
동작과 상태만 전달해 중복 마크업을 제거하세요.
- Around line 164-192: Update the member removal Button in the members.map block
to use a unique accessible name that includes the associated member.name, while
preserving its visible “삭제하기” label. Ensure it is distinguishable from other
member removal buttons and the poster image deletion button rendered by
ImageUploadBox.
- Around line 155-162: Update the “추가하기” Button in MeetupProjectModal so it sets
the disabled prop when draftName.trim() is empty, matching the existing save
button behavior; keep the current variant styling and handleAddMember callback
unchanged.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 0d0ba2a3-e68a-44be-9565-f55c7dd86675

📥 Commits

Reviewing files that changed from the base of the PR and between 3eed7c9 and a104463.

📒 Files selected for processing (14)
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.test.tsx
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx
  • src/components/common/FormField/FormField.tsx
  • src/components/common/Inputfield/Inputfield.tsx
  • src/components/common/Modal/Modal.stories.tsx
  • src/components/common/Modal/Modal.test.tsx
  • src/components/common/Modal/Modal.tsx
  • src/components/common/SelectField/SelectField.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.test.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.test.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx
  • src/components/reviews/ReviewModal/ReviewModal.test.tsx
  • src/components/reviews/ReviewModal/ReviewModal.tsx

Comment on lines +55 to +57
<FormField label="기수" className="flex-1">
<SelectField value={`${String(cardinal)}기`} options={[]} />
</FormField>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

기수 필드 블록 중복.

CompanyProjectModal.tsx, MeetupProjectModal.tsx와 동일한 기수 FormField+SelectField 블록이 반복됩니다. 공유 컴포넌트로 추출 권장(상세는 컨솔리데이트 코멘트 참고).

🤖 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 `@src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx` around lines
55 - 57, Extract the repeated 기수 FormField and SelectField markup from
BlogReviewModal into a shared component, then reuse it in BlogReviewModal,
CompanyProjectModal, and MeetupProjectModal. Preserve the current
`${String(cardinal)}기` value and existing field styling and behavior across all
three modals.

Source: Path instructions

Comment thread src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx Outdated
Comment thread src/components/common/FormField/FormField.tsx
Comment thread src/components/common/Modal/Modal.tsx Outdated
Comment thread src/components/common/SelectField/SelectField.tsx Outdated
Comment thread src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx Outdated
Comment thread src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx
Comment thread src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx Outdated
Comment thread src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx Outdated
@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

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

수고하셨습니다! 코멘트 확인해주세요~

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.

compound 전환보다는 slot 구조가 더 디자인과 맞을 것 같습니다. header/footer prop처럼 필요한 영역만 주입하면 될 것 같습니다!

interface ModalProps {
  title?: string
  header?: ReactNode
  footer?: ReactNode
  children: ReactNode
}

이런식으로요! 😄

const fieldId = useId()
const isElementChild = isValidElement<{ id?: string }>(children)
const resolvedId = isElementChild ? (children.props.id ?? fieldId) : fieldId
const field = isElementChild ? cloneElement(children, { id: resolvedId }) : children

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.

커스텀 입력 컴포넌트가 들어올 때도 실제 입력 요소와 잘 연결되는지 확인해보시면 좋을 것 같습니다!!
ex) ImageUploadBox

Modal.Title/Body/Footer 대신 title/header/footer props로 필요한 영역만 주입하도록 바꿔 사용 측 구조를 단순화
Modal.Title/Body/Footer 조합 대신 title/header/footer props를 사용하도록 사용처를 갱신
@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

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

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 `@src/components/common/Modal/Modal.tsx`:
- Around line 5-13: header만 제공되는 경우에도 접근 가능한 모달 이름이 유지되도록 Modal의 다이얼로그 렌더링을
수정하세요. header를 표시할 때 숨김 Dialog.Title을 유지하거나 aria-label/aria-labelledby를
ModalProps로 받아 Dialog에 전달하고, 기존 title 경로의 동작은 보존하세요.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: d927a9f3-d363-4150-a41e-7d7c95cbc269

📥 Commits

Reviewing files that changed from the base of the PR and between efc4f95 and 4e1a952.

📒 Files selected for processing (8)
  • src/components/blog-reviews/BlogReviewModal/BlogReviewModal.tsx
  • src/components/common/Modal/Modal.stories.tsx
  • src/components/common/Modal/Modal.test.tsx
  • src/components/common/Modal/Modal.tsx
  • src/components/common/ModalActions/ModalActions.tsx
  • src/components/projects/CompanyProjectModal/CompanyProjectModal.tsx
  • src/components/projects/MeetupProjectModal/MeetupProjectModal.tsx
  • src/components/reviews/ReviewModal/ReviewModal.tsx

Comment thread src/components/common/Modal/Modal.tsx
ImageUploadBox가 id prop을 받지 않고 내부에서 독자적으로 id를 생성해, FormField의 label과 실제 파일 input이 연결되지 않던 문제를 수정
@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

@github-actions

Copy link
Copy Markdown

CI 결과

항목 결과
TypeScript 🟢 통과
Prettier 🟢 통과
Generated 🟢 통과
ESLint 🟢 통과
Test 🟢 통과
Build 🟢 통과

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

LGTM 💯

@leemanjae02 leemanjae02 merged commit 9eaf7ec into main Jul 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feature 새로운 기능

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] #15 - Modal 컴포넌트 제작

2 participants