Mantine Menu.Sub 컴포넌트 개선
최근 구직활동을 하면서, 서류에서 계속 탈락하는 나날을 보내고 있었습니다. 이력서를 수정하고, 이제까지 해왔던 부분들을 다시 상기하는 도중에 Mantine UI Discord 에서 위에 첨부한 이미지 내용을 확인하게 되었습니다.
Mantine Menu.Sub 컴포넌트 개선


최근 구직활동을 하면서, 서류에서 계속 탈락하는 나날을 보내고 있었습니다. 이력서를 수정하고, 이제까지 해왔던 부분들을 다시 상기하는 도중에 Mantine UI Discord 에서 위에 첨부한 이미지 내용을 확인하게 되었습니다.
참고하라는 링크 내용을 확인했는데, 안전 영역을 설정하여 사용자가 하위 메뉴 항목으로 쉽게 전환하는 아티클 이었습니다.
혹시나, 해당 내용에 대해서 이미 누군가 구현을 했는지 공식 문서를 살펴보았습니다. 내용이 업데이트 되어있지 않은 부분을 확인하였고 Mantine 저장소를 fork 를 해와서 Codex를 활용하여 해당 부분에 대한 작업이 되어있는지 확인 하였습니다. (https://github.com/mantinedev/mantine)
처리가 되어 있지 않아, 해당 이슈 작업을 진행 했습니다.
먼저 진행했던 작업은 Mantine Menu 컴포넌트 구조 분석이었습니다.
<Menu>
<Menu.Target>
<Button>Open menu</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>Dashboard</Menu.Item>
<Menu.Item>Settings</Menu.Item>
</Menu.Dropdown>
</Menu>
// ...
Menu.displayName = '@mantine/core/Menu';
Menu.classes = classes;
Menu.Item = MenuItem;
Menu.Label = MenuLabel;
Menu.Dropdown = MenuDropdown;
Menu.Target = MenuTarget;
Menu.Divider = MenuDivider;
Menu.Sub = MenuSub;
Mantine Menu 컴포넌트는 Compound Component 패턴으로 설계가 되어있습니다.
import { Button, Menu } from '@mantine/core';
function Demo() {
return (
<Menu width={200} position="bottom-start">
<Menu.Target>
<Button>Toggle Menu</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>Dashboard</Menu.Item>
<Menu.Sub openDelay={120} closeDelay={150}>
<Menu.Sub.Target>
<Menu.Sub.Item>Products</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>All products</Menu.Item>
<Menu.Item>Categories</Menu.Item>
<Menu.Item>Tags</Menu.Item>
<Menu.Item>Attributes</Menu.Item>
<Menu.Item>Shipping classes</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
<Menu.Item>Customers</Menu.Item>
<Menu.Item>Reports</Menu.Item>
<Menu.Sub>
<Menu.Sub.Target>
<Menu.Sub.Item>Orders</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>Open</Menu.Item>
<Menu.Item>Completed</Menu.Item>
<Menu.Item>Cancelled</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
<Menu.Sub>
<Menu.Sub.Target>
<Menu.Sub.Item>Settings</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>Profile</Menu.Item>
<Menu.Item>Security</Menu.Item>
<Menu.Item>Notifications</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
</Menu.Dropdown>
</Menu>
);
}
Sub 메뉴는 Menu 컴포넌트 자식으로 Menu.Sub 컴포넌트가 담당하고 있습니다.
MenuSub 함수를 실제로 확인하면, SubMenuContext 를 둬서 관리하고 있음을 확인할 수 있습니다.
하위 메뉴에 대한 안전영역을 설정해줘야 하므로, 저는 Menu.Sub 에 해당 props를 추가하여 작업을 하기로 결정합니다.
내부 코드를 확인하니, 아래와 같은 코드가 있었습니다.
const { getReferenceProps, getFloatingProps } = useInteractions([
useHover(context, {
handleClose: safePolygon(),
delay: { open: openDelay, close: closeDelay },
}),
]);
useHover() 는 Menu.Sub의 Menu.Sub.Item에 마우스가 올라가면 submenu를 여는 기능을 제공해주는 훅입니다.
확인해보니 safePolygon() 자체는 이미 Menu.Sub 내부에서 사용되고 있었습니다.
다만 기본 옵션으로만 호출되고 있었고, 사용자가 buffer, requireIntent 같은 옵션을 조정할 방법은 제공되지 않았습니다.
따라서 이번 작업의 초점은 safe polygon 기능을 새로 구현하는 것이 아니라, 이미 내부에서 사용 중이던 Floating UI의 safePolygon 옵션을 Menu.Sub의 public API로 노출하는 것이었습니다.
safePolygon() 는 @floating-ui/react 패키지에서 제공해주는 함수이며 해당 패키지는 floating 요소를 만들기 위한 리액트 패키지입니다.
safePolygon()은 마우스가 reference에서 dropdown 방향으로 이동할 때, 두 요소 사이에 보이지 않는 안전 영역을 만들고 그 영역 안에서는 닫힘을 지연합니다. submenu처럼 target과 dropdown 사이에 gap이 생기는 UI에서 특히 중요합니다.
safePolygon() 에 대한 옵션을 prop 형태로 받도록 하고 이를 useHover에 연결해주는 형태로 진행했습니다.
// add props
safeAreaPolygon?: boolean | SafePolygonOptions; // default true
// ...
const { getReferenceProps, getFloatingProps } = useInteractions([
useHover(context, {
handleClose: safeAreaPolygon
? safePolygon(typeof safeAreaPolygon === 'object' ? safeAreaPolygon : undefined)
: undefined,
delay: { open: openDelay, close: closeDelay },
}),
]);
기본값은 기존 동작과 동일하게 유지했습니다.
safeAreaPolygon={true}또는 생략:safePolygon()사용safeAreaPolygon={false}:handleClose를 비워 safe polygon 비활성화safeAreaPolygon={{ buffer: 16 }}:safePolygon({ buffer: 16 })처럼 옵션 전달
이렇게 하면 기존 사용자에게는 breaking change가 없고, submenu 간격이 큰 경우에는 개발자가 직접 safe polygon 계산을 조정할 수 있습니다.
마무리
이번 기여에서 새로 polygon 알고리즘을 직접 구현한 것은 아닙니다. 오히려 기존 코드 안에 이미 존재하던 좋은 추상화를 발견하고, 그것을 사용자가 조정할 수 있는 public API로 열어준 작은 개선에 가까웠습니다.
하지만 오픈소스 기여가 항상 거대한 기능을 새로 만드는 일만은 아니라는 점을 다시 느꼈습니다. 기존 구조를 읽고, 라이브러리가 이미 의존하고 있는 도구의 역할을 이해하고, 사용자 입장에서 필요한 조절 지점을 찾아 안전하게 노출하는 것도 충분히 의미 있는 기여였습니다.
특히 이번 작업에서는 Codex를 활용해 Mantine의 Menu 컴포넌트 구조, compound component 패턴, SubMenuContext, Floating UI와의 연결 지점을 빠르게 파악할 수 있었습니다. 처음 보는 코드베이스에서도 관련 파일을 따라가며 흐름을 정리하고, 어떤 부분을 건드려야 하는지 좁혀가는 과정이 인상적이었습니다.
우연히 발견한 Discord 메시지로 시작한 작업이 실제 PR로 이어지고, 머지까지 된 경험은 꽤 큰 동기부여가 되었습니다. 앞으로도 거창한 기능이 아니더라도, 실제 사용자가 겪는 작은 불편을 발견하고 코드베이스의 방향에 맞게 개선하는 방식으로 오픈소스에 꾸준히 기여해보고 싶습니다.
메타데이터
- post_id
- d89cedf5266e
- slug
- mantine-menu-sub-컴포넌트-개선-d89cedf5266e
- url
- https://medium.com/@zero86/mantine-menu-sub-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B0%9C%EC%84%A0-d89cedf5266e
- canonical_url
- https://medium.com/@zero86/mantine-menu-sub-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B0%9C%EC%84%A0-d89cedf5266e
- author_url
- https://medium.com/@zero86
- status
- ok
- fetched_at
- 2026-06-12 07:40:50