Search
๐Ÿ’ก

Tooltip

Created
2026/01/14 12:33
Tags
2026/01/14 15:29

Context

Sidebar ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๋˜ ์ค‘ Sidebar ๋‚ด๋ถ€์—์„œ Tooltip์„ ํ™œ์šฉํ•˜๋Š” ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ•˜์˜€๋‹ค. ์ด์ฒ˜๋Ÿผ shadcn/ui๋Š” ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์žฌํ™œ์šฉํ•˜๋Š” ๋ฉ‹์ง„ ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์žฌํ™œ์šฉ์„ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€, ๋‚ด๋ถ€๊ตฌ์กฐ๋Š” ์–ด๋–ป๊ฒŒ ์ž‘์„ฑ ๋˜์–ด์žˆ๋Š”์ง€ ๋ถ„์„ํ•  ํ•„์š”์„ฑ์ด ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€๋‹ค.

Agenda

โ€ข
Tooltip ์ปดํฌ๋„ŒํŠธ์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ดํ•ดํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•จ

Contents

TooltipProvider
SidebarProvider์˜ ๋งˆ์ง€๋ง‰์˜ return์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด TooltipProvider๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
return ( <SidebarContext.Provider value={contextValue}> <TooltipProvider delayDuration={0}> ... </TooltipProvider> </SidebarContext.Provider> );
JavaScript
๋ณต์‚ฌ
Tooltip์˜ ์˜ˆ์‹œ๋Š” ๊ณต์‹๋ฌธ์„œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, Sidebar ๋‚ด๋ถ€์—์„œ ์ด๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
... import * as TooltipPrimitive from '@radix-ui/react-tooltip'; ... const TooltipProvider = ({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) => { return ( <TooltipPrimitive.Provider data-slot='tooltip-provider' delayDuration={delayDuration} {...props} /> ); };
JavaScript
๋ณต์‚ฌ
TooltipProvider๋Š” ์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑ ๋˜์–ด์žˆ๋‹ค. TooltipPrimitive.Provider๋ผ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด ๊ฒƒ์€ radix-ui์— ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. radix-ui์—์„œ ์ œ๊ณตํ•˜๋Š” Tooltip์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— Sidebar์ฒ˜๋Ÿผ Provider๋ฅผ ๋งŒ๋“ค๊ณ , Context๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
radix-ui์˜ react-tooltip์€ default export๊ฐ€ ์—†๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด export { } ํ˜•ํƒœ๋กœ ๋ชจ๋“ˆ์„ export ํ•œ๋‹ค.
export { Arrow, Content, Portal, Provider, Root, Tooltip, TooltipArrow, type TooltipArrowProps, TooltipContent, type TooltipContentProps, TooltipPortal, type TooltipPortalProps, type TooltipProps, TooltipProvider, type TooltipProviderProps, TooltipTrigger, type TooltipTriggerProps, Trigger, createTooltipScope };
JavaScript
๋ณต์‚ฌ
๋งŒ์•ฝ *๋ฅผ ์ง€์šฐ๊ณ  Provider๋งŒ importํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜๋ฉด ๋œ๋‹ค.
import { Provider as TooltipPrimitiveProvider } from '@radix-ui/react-tooltip';
JavaScript
๋ณต์‚ฌ
delayDuration ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” number ํƒ€์ž…์œผ๋กœ Tooltip์ด ๋‚˜ํƒ€๋‚˜๋Š” ๋”œ๋ ˆ์ด๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” 0์œผ๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์ง€์ •ํ•˜์—ฌ ๋ฐ”๋กœ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚œ๋‹ค.
Tooltip
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) { return ( <TooltipProvider> <TooltipPrimitive.Root data-slot='tooltip' {...props} /> </TooltipProvider> ); }
JavaScript
๋ณต์‚ฌ
์œ„๋Š” shadcn/ui์˜ Tooltip ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„๋ถ€์ด๋‹ค. TooltipProvider์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Radix Tooltip์„ shadcn/ui ์Šคํƒ€์ผ๋กœ ํฌ์žฅํ•œ wrapper์ด๋‹ค. ์ด Tooltip์€ React.ComponentProps<typeof TooltipPrimitive.Root>๋ฅผ ํ†ตํ•ด radix-ui์˜ Tooltip - Root ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์‹ ๊ธฐํ•œ ๊ฒƒ์€ ์œ„์˜ TooltipProvider๋ฅผ ์ด Tooltip ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. radix-ui์—์„œ Tooltip์€ Provider ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๊ฐ€ ๋˜์–ด์žˆ๋Š”๋ฐ, ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ Provider๋ฅผ ์žŠ์–ด๋„ Tooltip์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๊ฐ€ ๋˜์–ด์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋งŒ์•ฝ์— ์‚ฌ์šฉ์ž๊ฐ€ ์ •์ƒ์ ์œผ๋กœ Provider๋ฅผ ๋ฐฐ์น˜ํ–ˆ๊ณ  ๊ทธ ์•ˆ์—์„œ Tooltip์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด Provider๊ฐ€ ๋‘ ๊ฐœ ์ƒ๊ธฐ๋Š” ๊ฒƒ์ธ๋ฐ, ๋ถˆํ•„์š”ํ•˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐ ๋œ๋‹ค.
TooltipTrigger
const TooltipTrigger = ({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) => { return <TooltipPrimitive.Trigger data-slot='tooltip-trigger' {...props} />; };
JavaScript
๋ณต์‚ฌ
TooltipTrigger๋Š” Tooltip์„ ์ผœ๊ณ  ๋„๋Š” ๋ฒ„ํŠผ์ด๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ radix-ui์˜ Trigger๋ฅผ ๊ฐ์‹ธ๋Š” wrapper ํ˜•ํƒœ์ด๋ฉฐ radix-ui์˜ Trigger์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ  ๋„˜๊ฒจ์ค€๋‹ค.
TooltipContent
const TooltipContent = ({ className, sideOffset = 0, children, ...props }: React.ComponentProps<typeof TooltipPrimitive.Content>) => { return ( <TooltipPrimitive.Portal> <TooltipPrimitive.Content data-slot='tooltip-content' sideOffset={sideOffset} className={cn( 'bg-foreground text-background', 'animate-in fade-in-0 zoom-in-95', 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'z-50 w-fit origin-(--radix-tooltip-content-transform-origin)', 'rounded-md px-3 py-1.5 text-xs text-balance', className, )} {...props} > {children} <TooltipPrimitive.Arrow className='bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]' /> </TooltipPrimitive.Content> </TooltipPrimitive.Portal> ); };
JavaScript
๋ณต์‚ฌ
Tooltip์˜ ์ปจํ…์ธ ๋ฅผ ๋‹ด๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. ๊ตฌํ˜„๋ถ€์˜ ์‹œ์ž‘๋ถ€ํ„ฐ ํŠน์ดํ•œ ๋ถ€๋ถ„์ด ์กด์žฌํ•œ๋‹ค. ๋ฐ”๋กœ Portal์ด๋‹ค. Portal์„ ์‚ฌ์šฉํ•˜๊ฒŒ๋˜๋ฉด body ํƒœ๊ทธ ์•ˆ์— ๋ Œ๋”๋ง ํ•˜๋˜ ์‹ค์ œ ๋‹ค๋ฅธ ๊ณณ์— ๋ Œ๋”๋ง ํ•œ๋‹ค. ๋…ผ๋ฆฌ์ ์œผ๋กœ๋Š” ๋ฐฐ์น˜ํ•œ ์œ„์น˜์— ์†ํ•˜์ง€๋งŒ ์‹ค์ œ DOM ์œ„์น˜๋Š” ๋‹ค๋ฅธ ๊ณณ์— ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” UI ์ปดํฌ๋„ŒํŠธ ํŠน์„ฑ ๋•Œ๋ฌธ์ธ๋ฐ, ํˆดํŒ์ด๋‚˜ ๋ชจ๋‹ฌ, ๋“œ๋กญ๋‹ค์šด, ํŒ์˜ค๋ฒ„ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋Š” z-index๋‚˜ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐ์— ์˜ํ–ฅ์„ ์‰ฝ๊ฒŒ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ Portal์„ ์‚ฌ์šฉํ•ด ํ•ญ์ƒ ํ™”๋ฉด ์œ„์— ๋œฐ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.