Skip to content

feat: 햄버거 메뉴 닫기 동작 개선#1102

Merged
jeongyou merged 3 commits intodevelopfrom
feature/close-user-menu-#1096
Mar 31, 2026
Merged

feat: 햄버거 메뉴 닫기 동작 개선#1102
jeongyou merged 3 commits intodevelopfrom
feature/close-user-menu-#1096

Conversation

@jeongyou
Copy link
Copy Markdown
Member

@jeongyou jeongyou commented Mar 23, 2026

close #1096

As-Is

  • 우측 상단 햄버거 버튼 클릭 시 사용자 메뉴가 열림
  • 메뉴가 열린 상태에서 외부 영역을 클릭해도 닫히지 않음
  • Escape 입력으로도 닫히지 않아, 버튼을 다시 눌러야만 종료 가능
  • 일반적인 드롭다운 종료 패턴과 달라 마우스/키보드 사용자 모두 불편함이 있었음

To-Be

  • 햄버거 메뉴 외부 클릭 시 메뉴가 닫히도록 개선
  • Escape 입력 시 메뉴가 닫히도록 개선
  • 사용자 메뉴 열림/닫힘 로직을 useUserMenuVisibility 훅으로 분리
  • 메뉴 트리거를 button으로 감싸고 aria-expanded, aria-haspopup, aria-controls 속성 추가
  • Escape로 닫힌 경우 트리거 버튼으로 포커스가 돌아가도록 처리
  • 사용자 메뉴 닫기 동작 관련 테스트 추가

Check List

  • 테스트가 전부 통과되었나요?
  • 모든 commit이 push 되었나요?
  • merge할 branch를 확인했나요?
  • Assignee를 지정했나요?
  • Label을 지정했나요?

Test Screenshot

(Optional) Additional Description

  • 외부 클릭 닫기와 Escape 닫기를 사용자 메뉴 전용 훅으로 분리해, 이후 유사한 오버레이 UI에도 재사용할 수 있도록 정리했습니다.
  • 포커스 복귀는 모든 닫힘 상황이 아니라 Escape로 닫는 경우에만 적용했습니다. 외부 클릭 시에는 사용자가 실제로 클릭한 요소의 포커스를 유지하는 쪽이 자연스럽다고 판단했습니다.
  • 사용자 메뉴 테스트를 추가해 아래 케이스를 검증했습니다.
    • 초기 접근성 속성 노출
    • 트리거 클릭 시 메뉴 열림
    • 메뉴 내부 클릭 시 유지
    • 외부 클릭 시 닫힘
    • Escape 입력 시 닫힘 및 포커스 복귀
    • Escape 외 키 입력 무시
    • 닫힌 상태에서 외부 클릭/Escape 입력 무영향

Summary by CodeRabbit

릴리스 노트

  • 개선 사항

    • 사용자 메뉴에 키보드 네비게이션 지원 추가 (Escape 키로 메뉴 닫기)
    • 메뉴 외부 영역 클릭 시 자동 닫힘
    • 접근성 개선으로 스크린 리더 호환성 향상
  • 테스트

    • 사용자 메뉴 상호작용에 대한 포괄적인 테스트 스위트 추가

@jeongyou jeongyou requested review from AHHYUNJU and ohgus March 23, 2026 07:17
@jeongyou jeongyou self-assigned this Mar 23, 2026
@jeongyou jeongyou added the feature 새로운 기능 추가 label Mar 23, 2026
@github-actions github-actions Bot requested a review from aydenote March 23, 2026 07:18
@github-actions github-actions Bot added the frontend 프론트 작업 label Mar 23, 2026
@meolichae meolichae moved this to In Progress in 2025-routie Mar 23, 2026
@jeongyou
Copy link
Copy Markdown
Member Author

@coderabbitai 리뷰 해줘

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

@jeongyou 네, PR #1102 리뷰를 시작하겠습니다! 🐰

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

이 PR은 사용자 메뉴(햄버거 메뉴)의 외부 영역 클릭과 Escape 키 입력으로 메뉴를 닫는 기능을 추가합니다. 새로운 useUserMenuVisibility 훅이 메뉴 상태와 참조를 관리하며, UserMenuButton 컴포넌트가 이 훅을 사용하도록 변경되었습니다. 스타일링이 추가되어 트리거 버튼에 포커스 스타일이 적용되고, 접근성 속성(aria 속성들)이 포함됩니다. 메뉴 열기, 닫기, 키보드 처리, 외부 클릭 감지를 검증하는 포괄적인 테스트가 추가되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 '햄버거 메뉴 닫기 동작 개선'으로, 변경의 핵심인 사용자 메뉴의 닫기 동작 개선을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 저장소의 템플릿을 따르고 있으며, As-Is, To-Be 섹션에서 문제 상황과 해결 방안을 상세히 설명하고 있고 체크리스트가 완료되어 있습니다.
Linked Issues check ✅ Passed 모든 코드 변경이 이슈 #1096의 요구사항을 충족합니다. 외부 클릭 감지, Escape 키 처리, 훅 분리, 접근성 속성 추가, 테스트 작성이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #1096의 범위 내에 있으며, 사용자 메뉴의 닫기 동작 개선과 관련된 파일들만 수정되었습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/close-user-menu-#1096

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
frontend/src/domains/auth/components/UserMenuButton/__tests__/UserMenuButton.test.tsx (1)

22-28: userEvent 인스턴스를 일관되게 재사용하는 것을 권장합니다.

openUserMenu 헬퍼 함수 내에서 userEvent.setup()을 호출하면 테스트마다 새로운 인스턴스가 생성됩니다. 동일 테스트 내에서 헬퍼와 별도의 userEvent.setup() 호출을 혼용하면(예: Line 57) 이벤트 타이밍 문제로 flaky 테스트가 발생할 수 있습니다.

♻️ userEvent 인스턴스를 인자로 받도록 수정 제안
-const openUserMenu = async () => {
+const openUserMenu = async (user: ReturnType<typeof userEvent.setup>) => {
   const triggerButton = screen.getByRole('button', { name: '사용자 메뉴 열기' });

-  await userEvent.setup().click(triggerButton);
+  await user.click(triggerButton);

   return triggerButton;
 };

사용 예시:

it('메뉴 내부 클릭만으로는 메뉴가 닫히지 않는다', async () => {
  const user = userEvent.setup();
  render(<UserMenuButton positioning="relative" />);

  await openUserMenu(user);
  await user.click(screen.getByRole('button', { name: '내 동선 목록' }));

  expect(screen.getByTestId('user-menu')).toBeInTheDocument();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/domains/auth/components/UserMenuButton/__tests__/UserMenuButton.test.tsx`
around lines 22 - 28, The helper openUserMenu currently calls userEvent.setup()
internally which creates a new userEvent instance per call and can cause flaky
timing issues when tests also call userEvent.setup() separately; change
openUserMenu to accept a userEvent instance parameter (e.g., user) and use that
to perform the click instead of calling userEvent.setup() inside the helper,
updating all tests to pass a single shared user = userEvent.setup() into
openUserMenu to ensure a consistent event instance across the test (reference:
openUserMenu and userEvent.setup()).
frontend/src/domains/auth/hooks/useUserMenuVisibility.ts (1)

8-14: closeUserMenuuseCallback으로 감싸는 것을 고려해주세요.

closeUserMenu 함수가 useEffect 내부에서 사용되지만 의존성 배열에 포함되어 있지 않습니다. 현재 코드는 setIsUserMenuOpenuserMenuTriggerButtonRef가 안정적인 참조이므로 정상 동작하지만, ESLint exhaustive-deps 규칙에서 경고가 발생할 수 있습니다.

♻️ useCallback 적용 제안
-import { useEffect, useRef, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';

 const useUserMenuVisibility = () => {
   const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
   const userMenuRef = useRef<HTMLDivElement>(null);
   const userMenuTriggerButtonRef = useRef<HTMLButtonElement>(null);

-  const closeUserMenu = (shouldRestoreFocus = false) => {
+  const closeUserMenu = useCallback((shouldRestoreFocus = false) => {
     setIsUserMenuOpen(false);

     if (shouldRestoreFocus) {
       userMenuTriggerButtonRef.current?.focus();
     }
-  };
+  }, []);

   useEffect(() => {
     if (!isUserMenuOpen) {
       return;
     }
     // ... handlers remain the same
-  }, [isUserMenuOpen]);
+  }, [isUserMenuOpen, closeUserMenu]);

Also applies to: 16-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/domains/auth/hooks/useUserMenuVisibility.ts` around lines 8 -
14, Wrap the closeUserMenu function in useCallback to stabilize its identity for
useEffect dependencies: change the standalone const closeUserMenu = (...) => {
... } to a useCallback hook that returns the same logic and include
setIsUserMenuOpen and userMenuTriggerButtonRef in the dependency array; do the
same for any analogous handlers in this file (the other functions mentioned
around lines 16-43) so ESLint exhaustive-deps warnings are resolved and effects
can safely list these callbacks as dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@frontend/src/domains/auth/components/UserMenuButton/__tests__/UserMenuButton.test.tsx`:
- Around line 22-28: The helper openUserMenu currently calls userEvent.setup()
internally which creates a new userEvent instance per call and can cause flaky
timing issues when tests also call userEvent.setup() separately; change
openUserMenu to accept a userEvent instance parameter (e.g., user) and use that
to perform the click instead of calling userEvent.setup() inside the helper,
updating all tests to pass a single shared user = userEvent.setup() into
openUserMenu to ensure a consistent event instance across the test (reference:
openUserMenu and userEvent.setup()).

In `@frontend/src/domains/auth/hooks/useUserMenuVisibility.ts`:
- Around line 8-14: Wrap the closeUserMenu function in useCallback to stabilize
its identity for useEffect dependencies: change the standalone const
closeUserMenu = (...) => { ... } to a useCallback hook that returns the same
logic and include setIsUserMenuOpen and userMenuTriggerButtonRef in the
dependency array; do the same for any analogous handlers in this file (the other
functions mentioned around lines 16-43) so ESLint exhaustive-deps warnings are
resolved and effects can safely list these callbacks as dependencies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2795c37-1264-4ed2-9da7-83170b645f41

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3a061 and d04c53e.

📒 Files selected for processing (4)
  • frontend/src/domains/auth/components/UserMenuButton/UserMenuButton.styles.ts
  • frontend/src/domains/auth/components/UserMenuButton/UserMenuButton.tsx
  • frontend/src/domains/auth/components/UserMenuButton/__tests__/UserMenuButton.test.tsx
  • frontend/src/domains/auth/hooks/useUserMenuVisibility.ts

@jeongyou jeongyou merged commit 92c7d8d into develop Mar 31, 2026
3 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in 2025-routie Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 새로운 기능 추가 frontend 프론트 작업

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[TASK] 햄버거 메뉴 외부 클릭 및 Escape 입력으로 닫기 동작 개선

2 participants