Skip to content

[FEAT] 공통 모달 구현 (#248)#413

Merged
kanghaeun merged 6 commits intodevelopfrom
feat/common-modal#248
Feb 5, 2026
Merged

[FEAT] 공통 모달 구현 (#248)#413
kanghaeun merged 6 commits intodevelopfrom
feat/common-modal#248

Conversation

@kanghaeun
Copy link
Contributor

#️⃣연관된 이슈

closes #248

📝작업 내용

1. Modal 컴포넌트 (Modal/index.tsx)

Props:

isOpen - 모달 열림/닫힘 상태
onClose - 모달 닫기 함수
title - 모달 제목
children - 모달 내용
description - 모달 설명 (선택)
size - 모달 크기 (sm | md | lg)
variant - 스타일 variant (modal | popover)
anchorRef - popover일 때 기준 요소 ref

주요 기능:

  • React Portal: document.body에 렌더링
  • 접근성: ESC 키 닫기, 포커스 트래핑, aria-modal, aria-labelledby
  • Popover variant: 기준 요소 옆에 위치, 스크롤 시 따라감 (position: fixed)
  • useLayoutEffect: popover 위치 계산 시 깜빡임 방지
  • modalWidthRef: 스크롤 시 width 재계산 방지

2. useModal Hook (useModal.ts)

const { isOpen, openModal, closeModal, toggle } = useModal();

isOpen - 모달 상태
openModal() - 모달 열기
closeModal() - 모달 닫기
toggle() - 토글

3. Modal 스타일 (Modal/index.styled.ts)

  • fadeIn / scaleIn 애니메이션
  • Overlay: modal은 반투명 배경, popover는 투명
  • Content:
    • 크기: sm(13rem), md(20rem), lg(30rem)
    • popover일 때 position: fixed로 스크롤 따라감
    • position 계산 전 visibility: hidden (깜빡임 방지)

- fadeIn / scaleIn 애니메이션
- Overlay: modal은 반투명 배경, popover는 투명
- Content 크기: sm(13rem), md(20rem), lg(30rem)
- popover일 때 position: fixed로 스크롤 따라감
- position 계산 전 visibility: hidden (깜빡임 방지)
@kanghaeun kanghaeun requested a review from ganimjeong February 2, 2026 12:30
@kanghaeun kanghaeun self-assigned this Feb 2, 2026
@kanghaeun kanghaeun added the ✨ Feature 기능 구현 이슈 label Feb 2, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @kanghaeun, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 애플리케이션 전반에 걸쳐 일관되고 접근성 높은 모달 및 팝오버 UI를 제공하기 위한 공통 모달 컴포넌트와 관련 훅을 구현합니다. 이를 통해 개발자는 다양한 시나리오에서 쉽게 모달을 활용할 수 있으며, 사용자에게는 향상된 접근성과 부드러운 인터랙션을 제공합니다. 기존 댓글 삭제 기능에 새로운 모달을 적용하여 실제 사용 사례를 보여줍니다.

Highlights

  • 새로운 공통 모달 컴포넌트 구현: Modal 컴포넌트와 useModal 훅을 추가하여 애플리케이션 전반에 걸쳐 재사용 가능한 모달 시스템을 구축했습니다.
  • 접근성 기능 강화: ESC 키로 닫기, 포커스 트래핑, aria-modal, aria-labelledby 등 웹 접근성 표준을 준수하여 모든 사용자가 모달을 쉽게 이용할 수 있도록 했습니다.
  • 두 가지 모달 유형 지원: modal (기본 다이얼로그)과 popover (기준 요소 옆에 위치하며 스크롤 시 위치 유지) 두 가지 변형을 지원하여 다양한 UI 요구사항에 대응할 수 있습니다.
  • 애니메이션 및 반응형 디자인: fadeInscaleIn 애니메이션을 적용하고, sm, md, lg 세 가지 크기 옵션을 제공하며 반응형 디자인을 지원합니다.
  • 댓글 삭제 확인 모달 적용: CommentItem 컴포넌트의 댓글 삭제 기능에 새로 구현된 Modal 컴포넌트를 적용하여 사용자 경험을 개선했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

이 PR은 공통 Modal 컴포넌트와 useModal 훅을 구현하여 코드의 재사용성과 접근성을 높이는 좋은 변경입니다. 전반적으로 코드가 잘 작성되었으며, 특히 접근성을 고려한 포커스 관리와 popover variant 구현이 인상적입니다. 몇 가지 개선점을 제안하며, 이를 통해 컴포넌트의 유연성과 안정성을 더욱 높일 수 있을 것입니다.

Comment on lines +80 to +81
top: $position.top - 10,
left: $position.left - 30,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

popover의 위치를 조정하기 위해 하드코딩된 -10, -30 같은 매직 넘버를 사용하고 있습니다. 이렇게 하면 컴포넌트의 재사용성이 떨어지고, 다른 위치에 popover를 표시하고 싶을 때 스타일 파일을 수정해야 하는 불편함이 있습니다. 이 로직을 스타일 컴포넌트에서 제거하고, Modal/index.tsx 컴포넌트 내에서 위치를 계산할 때 offset을 적용하는 것이 좋습니다. offset 값을 prop으로 받아 더욱 유연하게 만드는 것도 좋은 방법입니다.

Suggested change
top: $position.top - 10,
left: $position.left - 30,
top: $position.top,
left: $position.left,

Comment on lines +150 to +152
setTimeout(() => {
titleRef.current?.focus();
}, 100);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

setTimeout에 100ms라는 고정된 시간을 사용하는 것은 시스템 성능에 따라 불안정하게 동작할 수 있습니다. 모달이 나타나는 애니메이션 시간(0.2s)보다 짧아서 문제가 될 수도 있고, 불필요한 딜레이일 수도 있습니다. requestAnimationFrame을 사용하여 다음 렌더링 프레임에 포커스를 이동시키는 것이 더 안정적인 방법이 될 수 있습니다.

Suggested change
setTimeout(() => {
titleRef.current?.focus();
}, 100);
requestAnimationFrame(() => {
titleRef.current?.focus();
});

Comment on lines +175 to +177
setTimeout(() => {
previousFocusRef.current?.focus();
}, 100);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

모달이 닫힐 때 포커스를 이전 요소로 복원하기 위해 setTimeout을 사용하고 있습니다. 하지만 현재 코드에는 모달이 사라질 때 별도의 애니메이션이 없으므로, setTimeout 없이 즉시 포커스를 복원해도 문제가 없습니다. 불필요한 딜레이를 제거하는 것이 좋습니다.

        previousFocusRef.current?.focus();

Copy link
Contributor

@ganimjeong ganimjeong left a comment

Choose a reason for hiding this comment

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

모달이 뜰 때 (화면 오른쪽에 뜨는)화면 스크롤바가 사라지면서 배경이 살짝 넓어지게 돼서 덜컥거리는 것 같은데
스크롤바 너비만큼 바디에 padding 추가하는 등 방식으로 수정하면 어떨까욤

if (variant === 'popover' && anchorRef?.current && modalRef.current) {
const anchorRect = anchorRef.current.getBoundingClientRect();

// 스크롤 시에는 저장된 width 사용, 초기에는 새로 계산
Copy link
Contributor

Choose a reason for hiding this comment

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

주석들은 의도적으로 남긴건가용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

학습 과정에서 구현한 코드라 이해 포인트가 될 수 있는 부분을 중심으로 주석을 남겨두었어용

@kanghaeun kanghaeun requested a review from ganimjeong February 5, 2026 14:57
@kanghaeun kanghaeun merged commit 9e46e9c into develop Feb 5, 2026
5 checks passed
@kanghaeun kanghaeun deleted the feat/common-modal#248 branch February 5, 2026 15:10
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] 댓글 삭제 확인 모달(Modal) 구현

2 participants