Skip to content

fix: 폼 모달 입력 및 닫힘 동작 개선 - #113

Merged
yooseohyeon merged 9 commits into
devfrom
fix/modal-dismiss-behavior
Aug 11, 2026
Merged

fix: 폼 모달 입력 및 닫힘 동작 개선#113
yooseohyeon merged 9 commits into
devfrom
fix/modal-dismiss-behavior

Conversation

@yooseohyeon

@yooseohyeon yooseohyeon commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

폼·채팅 모달의 의도치 않은 제출 및 닫힘을 방지하고, 텍스트 입력 검증과 오류 메시지 접근성을 개선했습니다.


🔥 변경 사항

  • 견적 보내기·견적 반려·리뷰 작성 모달
    • textarea Enter 제출 동작 제거
    • 텍스트 입력 최소·최대 글자 수 제한을 공통 상수로 관리
  • 폼 모달 및 채팅 모달에서 ESC·오버레이 클릭으로 닫히지 않도록 Modaldismissible 옵션 추가
    • 해당 변경사항 modal.stories에 반영
  • 공통 Input·Textarea가 오류 메시지를 aria-describedby로 연결하도록 개선
  • eslint.config.mjs의 전역 ignore 목록에 storybook-static/**추가
    • 대용량 번들 파일 검사로 발생하던 Babel 로그와 대량의 불필요한 lint 경고 제거
  • 사용하지 않는 스토리 파일 및 import 제거

✅ 체크리스트

  • 로컬에서 정상 동작을 확인했습니다.
  • 기존 기능에 영향을 주지 않는지 확인했습니다.
  • 불필요한 console.log를 제거했습니다.
  • lint를 통과했습니다.
  • README 또는 문서를 수정했습니다. (필요 시)
  • API 명세와 일치하는지 확인했습니다.

📷 스크린샷 (선택)

동작 및 접근성 개선으로, 별도 UI 변경 사항은 없습니다.

변경사항 반영한 스토리북 링크: https://fix-modal-dismiss-behavior--6a7054bcd216f9a9c84655d6.chromatic.com/?path=/docs/feedback-emptystate--docs


💬 To Reviewer

  • dismissible={false}는 ESC와 오버레이 클릭만 막고, X 버튼 닫힘은 유지합니다.
  • Input·Textarea에 기존 aria-describedby가 전달된 경우에도 오류 메시지 id를 함께 연결합니다.
  • Storybook 정적 산출물은 빌드 결과물이므로 ESLint 검사 대상에서 제외했습니다.

@yooseohyeon yooseohyeon self-assigned this Aug 11, 2026
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
moving-frontend Ready Ready Preview Aug 11, 2026 9:55am
moving-frontend-p2ol Ready Ready Preview Aug 11, 2026 9:55am

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 40 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: 59026ba2-1a9e-47b3-a8af-e43e188a7f85

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3d177 and 7dc440e.

📒 Files selected for processing (2)
  • src/components/estimate/RejectEstimateModal.tsx
  • src/components/estimate/SendEstimateModal.tsx
📝 Walkthrough

Walkthrough

공통 텍스트 길이 상수를 견적·리뷰 모달에 적용했습니다. 모달의 Escape 및 오버레이 닫기를 제어하는 dismissible 옵션을 추가했습니다. InputTextarea의 오류 메시지 접근성 연결을 보강했습니다. Storybook 예제와 정적 검사 설정도 갱신했습니다.

Changes

입력 및 모달 동작

Layer / File(s) Summary
모달 닫기 제어
src/components/common/Modal/ModalMain.tsx
dismissible 옵션을 추가했습니다. 값이 false이면 Escape 키와 오버레이 클릭으로 onClose를 호출하지 않습니다.
공통 텍스트 검증과 제출 동작
src/lib/constants/validation.ts, src/components/estimate/*Modal.tsx, src/components/review/ReviewWriteModal.tsx
최소 10자와 최대 1000자의 공통 상수를 추가했습니다. 세 입력 모달의 검증, maxLength, 글자 수 표시가 상수를 사용합니다. Enter 키 제출과 관련 핸들러를 제거했습니다.
입력 오류 메시지 접근성
src/components/common/Input/Input.tsx, src/components/common/Input/Textarea.tsx
오류 메시지에 고유 ID를 부여하고 aria-describedby로 연결했습니다. 기존 aria-describedby 값은 유지합니다.
모달 사용처와 Storybook 제어형 예제
src/components/chat/ChatRoomModal.tsx, src/stories/Modal.stories.tsx, src/stories/*estimatemodal.stories.tsx
채팅 모달을 닫을 수 없도록 설정했습니다. Modal Storybook 예제는 openonClose를 사용하며 종료 모션과 dismissible 설정을 문서화합니다. 견적·리뷰 모달 스토리는 삭제되었습니다.

검사 및 스토리 정리

Layer / File(s) Summary
검사 설정과 미사용 코드 정리
eslint.config.mjs, src/lib/api/profile.ts, src/stories/Icons.stories.tsx, src/stories/SelectableChip.stories.tsx
storybook-static/**을 ESLint 제외 경로에 추가했습니다. 사용하지 않는 import, 상수, 예외 매개변수를 제거했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant 사용자
  participant EstimateModal
  participant ModalMain
  participant Textarea
  사용자->>EstimateModal: 텍스트 입력
  EstimateModal->>Textarea: 공통 길이 제한 적용
  사용자->>EstimateModal: 제출
  EstimateModal->>ModalMain: dismissible=false 설정
  사용자->>ModalMain: Escape 키 또는 오버레이 클릭
  ModalMain-->>EstimateModal: 닫기 동작 차단
Loading

Possibly related PRs

Suggested labels: 🐛 fix, 🏢코드리뷰

Suggested reviewers: juengseulki, soooob43, 9g-g9

🚥 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 폼 모달의 입력 동작과 닫힘 동작 개선이라는 주요 변경 사항을 간결하고 명확하게 요약합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/modal-dismiss-behavior

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

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

🧹 Nitpick comments (2)
src/lib/constants/validation.ts (1)

2-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

as const를 적용하세요.

경로 규칙은 상수에 as const를 사용해 리터럴 타입을 유지하도록 요구합니다.

수정 예시
-export const MIN_TEXT_CONTENT_LENGTH = 10;
-export const MAX_TEXT_CONTENT_LENGTH = 1000;
+export const MIN_TEXT_CONTENT_LENGTH = 10 as const;
+export const MAX_TEXT_CONTENT_LENGTH = 1000 as const;

경로 지침에 따라 "as const 를 붙여 리터럴 타입을 유지합니다." 규칙을 적용하세요.

🤖 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/lib/constants/validation.ts` around lines 2 - 3, Apply `as const` to both
MIN_TEXT_CONTENT_LENGTH and MAX_TEXT_CONTENT_LENGTH in the validation constants
so their inferred types remain numeric literals.

Source: Path instructions

src/components/estimate/RejectEstimateModal.tsx (1)

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

최소 글자 수 안내에 MIN_TEXT_CONTENT_LENGTH를 사용하세요.

검증은 공통 상수를 사용하지만 안내 문구는 10을 직접 사용합니다. 최소 길이 상수가 변경되면 입력 검증과 사용자 안내가 불일치합니다.

  • src/components/estimate/RejectEstimateModal.tsx#L98-L98: placeholder`최소 ${MIN_TEXT_CONTENT_LENGTH}자 이상 입력해 주세요`로 변경하세요.
  • src/components/estimate/SendEstimateModal.tsx#L142-L142: placeholder`최소 ${MIN_TEXT_CONTENT_LENGTH}자 이상 입력해 주세요`로 변경하세요.

경로 지침에 따라 "매직 넘버·매직 스트링을 지양합니다." 규칙을 적용하세요.

🤖 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/estimate/RejectEstimateModal.tsx` at line 98, Update the
placeholder in src/components/estimate/RejectEstimateModal.tsx at lines 98-98 to
interpolate the existing MIN_TEXT_CONTENT_LENGTH constant instead of hardcoding
10. Apply the same placeholder change in
src/components/estimate/SendEstimateModal.tsx at lines 142-142, preserving the
existing Korean message.

Source: Path instructions

🤖 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 `@src/components/estimate/RejectEstimateModal.tsx`:
- Line 98: Update the placeholder in
src/components/estimate/RejectEstimateModal.tsx at lines 98-98 to interpolate
the existing MIN_TEXT_CONTENT_LENGTH constant instead of hardcoding 10. Apply
the same placeholder change in src/components/estimate/SendEstimateModal.tsx at
lines 142-142, preserving the existing Korean message.

In `@src/lib/constants/validation.ts`:
- Around line 2-3: Apply `as const` to both MIN_TEXT_CONTENT_LENGTH and
MAX_TEXT_CONTENT_LENGTH in the validation constants so their inferred types
remain numeric literals.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 84269257-0daa-4503-ba74-3f4b26d0c83c

📥 Commits

Reviewing files that changed from the base of the PR and between 5449230 and 3e3d177.

📒 Files selected for processing (17)
  • eslint.config.mjs
  • src/components/chat/ChatRoomModal.tsx
  • src/components/common/Input/Input.tsx
  • src/components/common/Input/Textarea.tsx
  • src/components/common/Modal/ModalMain.tsx
  • src/components/estimate/RejectEstimateModal.tsx
  • src/components/estimate/SendEstimateModal.tsx
  • src/components/review/ReviewWriteModal.tsx
  • src/lib/api/profile.ts
  • src/lib/constants/validation.ts
  • src/stories/Icons.stories.tsx
  • src/stories/Modal.stories.tsx
  • src/stories/Rejectestimatemodal.stories.tsx
  • src/stories/ReviewWriteModal.stories.tsx
  • src/stories/SelectableChip.stories.tsx
  • src/stories/Sendestimatemodal.stories.tsx
  • src/stories/Skeleton.stories.tsx
💤 Files with no reviewable changes (5)
  • src/stories/Rejectestimatemodal.stories.tsx
  • src/stories/Icons.stories.tsx
  • src/stories/Sendestimatemodal.stories.tsx
  • src/stories/ReviewWriteModal.stories.tsx
  • src/stories/Skeleton.stories.tsx

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

📋 PR 리뷰

👍 좋았던 점

  • 공통 Modaldismissible 옵션을 추가하고 기본값을 true로 유지해 기존 모달 동작에 영향을 최소화한 점이 좋았습니다.
  • dismissible={false}일 때 ESC와 오버레이 클릭만 차단하고, 기존 X 버튼은 그대로 사용할 수 있어 PR 설명과 실제 동작이 일치합니다.
  • 견적 보내기, 견적 반려, 리뷰 작성, 채팅 모달에 dismissible={false}가 실제로 적용되어 의도치 않은 닫힘 방지 범위가 빠지지 않고 반영되었습니다.
  • 견적 보내기·반려·리뷰 textarea에서 기존 Enter 제출용 onKeyDown 로직을 제거해 Enter가 본래 textarea의 줄바꿈 동작으로만 사용되도록 한 점이 좋았습니다.
  • 코멘트·반려 사유·리뷰의 최소/최대 길이를 MIN_TEXT_CONTENT_LENGTH, MAX_TEXT_CONTENT_LENGTH 공통 상수로 분리해 동일한 10~1000자 정책을 한 곳에서 관리하도록 정리한 점이 좋았습니다.
  • 각 폼의 maxLength, placeholder, validation error, 글자 수 표시까지 같은 공통 상수를 사용하고 있어 검증과 UI 문구가 서로 어긋날 가능성을 줄였습니다.
  • 공통 Input, Textarea에서 기존에 전달된 aria-describedby를 덮어쓰지 않고 오류 메시지 id를 추가로 병합한 점이 좋았습니다.
  • 오류가 있을 때만 생성된 id를 aria-describedby에 연결하고 실제 오류 Text에도 동일 id를 부여해 스크린리더가 입력 요소와 오류 설명의 관계를 인식할 수 있도록 한 점이 적절합니다.
  • ChatRoomModal에도 동일하게 dismissible={false}를 적용해 채팅 도중 ESC나 오버레이 클릭으로 대화창이 실수로 닫히는 문제를 막은 점도 작업 목적과 잘 맞습니다.

🔍 확인 및 제안

1. dismissible={false} 동작 범위

현재 구현은 useFocusTraponEscape와 overlay click handler만 dismissible 조건으로 막고 있습니다.

따라서 PR에 적힌 것처럼 ESC/오버레이 클릭만 차단하고,
Modal.Close를 통한 명시적인 닫기는 그대로 유지됩니다.

의도한 동작대로 잘 구현된 것으로 보입니다.

2. textarea Enter 제출 제거

견적 보내기, 견적 반려, 리뷰 작성 세 모달 모두 기존 onKeyDown 기반 Enter 제출 로직이 제거되었습니다.

각 textarea는 더 이상 Enter를 submit trigger로 사용하지 않기 때문에
여러 줄 입력 중 실수로 제출되는 문제는 잘 막힌 것으로 보입니다.

3. 공통 길이 제한

세 입력 모두 기존 정책이 10~1000자로 동일했고,
이번에 공통 상수로 묶은 뒤 maxLength, validation, 에러 문구, 카운터까지 모두 동일 상수를 사용하고 있어 빠진 부분은 없어 보입니다.

4. aria-describedby

기존 aria-describedby가 전달되는 경우에도

기존 id + 오류 메시지 id

형태로 합쳐주고 있어 기존 접근성 설명을 잃지 않습니다.

Input, Textarea 모두 같은 방식으로 적용되어 있어 공통 컴포넌트 간 처리도 일관적입니다.

💬 To Reviewer

말씀해주신 세 가지 부분을 중점적으로 확인했습니다.

먼저 dismissible={false}는 실제 구현상 ESC와 오버레이 클릭만 차단합니다.

useFocusTraponEscapedismissible이 true일 때만 전달하고,
overlay handler에서도 !dismissible이면 바로 return하도록 되어 있습니다.

반면 X 버튼은 기존 Modal.CloseonClose 흐름을 그대로 사용하므로
명시적인 닫기 동작은 유지됩니다.

Input·Textareaaria-describedby 처리도
기존 prop을 대체하지 않고 오류 메시지 id를 함께 합치는 방식으로 구현되어 있습니다.

따라서 기존에 helper text 등 다른 설명 id가 연결되어 있던 경우에도
기존 설명 + 오류 메시지를 모두 스크린리더에서 참조할 수 있습니다.

Storybook 정적 산출물을 ESLint 검사에서 제외하는 변경도
빌드 결과물 자체를 소스 lint 대상으로 두지 않는 목적이라 적절한 방향으로 보입니다.

이번 PR에서 설명한 작업 범위를 기준으로 확인했을 때 빠진 부분은 보이지 않았습니다.

수고하셨습니다! 👍

@yooseohyeon
yooseohyeon merged commit c57dc88 into dev Aug 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants