Search
πŸ’‘

Dialog

Created
2026/01/22 07:23
Tags
2026/01/22 09:17

Context

Alertλ‚˜ Confirmκ³Ό 같이 μ‚¬μš©μžκ°€ μƒν˜Έμž‘μš©ν•˜λŠ” λͺ¨λ‹¬μ„ λ§Œλ“€ λ•Œ 화면에 λ³„λ„μ˜ 창을 λ„μš°λŠ” μ»΄ν¬λ„ŒνŠΈκ°€ ν•„μš”ν•˜λ‹€. shadcn/uiμ—μ„œλŠ” 이λ₯Ό Dialogλ₯Ό ν™œμš©ν•΄μ„œ λ§Œλ“œλŠ”λ°, 이 κΈ€μ—μ„œλŠ” 이 Dialog에 λŒ€ν•œ λ‚΄μš©μ„ 닀룬닀.

Agenda

β€’
Dialog μ»΄ν¬λ„ŒνŠΈμ˜ λ‚΄μš©μ„ λΆ„μ„ν•˜κ³  μ΄ν•΄ν•˜λŠ” 것을 λͺ©ν‘œλ‘œ 함

Contents

μ‚¬μš©μžμ—κ²Œ 데이터λ₯Ό μž…λ ₯ν•˜λŠ” λͺ¨λ‹¬μ„ μ œκ³΅ν•˜κ³ μ‹Άκ±°λ‚˜ Contextλ₯Ό λΆ„λ¦¬ν•˜μ—¬ 데이터 μž‘μ—…μ„ ν•˜κ³  싢은 κ²½μš°κ°€ μ‘΄μž¬ν•œλ‹€. 이 λ•Œ λͺ¨λ‹¬μ„ 화면에 λ„μ›Œ μ‚¬μš©μžμ˜ 관심을 λ„λŠ” μ „ν™˜ μž‘μ—…μ„ μˆ˜ν–‰ν•œλ‹€. shadcn/uiμ—μ„œλŠ” Dialog μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ 이λ₯Ό κ΅¬ν˜„ν•˜λŠ”λ° Dialogλ₯Ό κ΅¬μ„±ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈ λ˜ν•œ μ—¬λŸΏ μ‘΄μž¬ν•œλ‹€.
Dialog
const Dialog = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) => { return <DialogPrimitive.Root data-slot='dialog' {...props} />; };
TypeScript
볡사
Dialog μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. radix-ui의 Dialog - Rootλ₯Ό κ°μ‹ΈλŠ” wrapper ν˜•μ‹μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈκ°€ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€. 후에 λ‚˜μ˜¬ Dialogλ₯Ό κ΅¬μ„±ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈλ“€μ„ κ°μ‹ΈλŠ” ν˜•νƒœλ‘œ μ‚¬μš©λœλ‹€.
DialogTrigger
const DialogTrigger = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) => { return <DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />; };
TypeScript
볡사
DialogTrigger μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. radix-ui의 Dialog - Triggerλ₯Ό κ°μ‹ΈλŠ” wrapper ν˜•νƒœλ‘œ μ»΄ν¬λ„ŒνŠΈκ°€ κ΅¬ν˜„λ˜μ–΄ μžˆλ‹€. Dialog의 μ—΄λ¦Ό μƒνƒœλ₯Ό ν† κΈ€ ν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.
DialogPortal
const DialogPortal = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) => { return <DialogPrimitive.Portal data-slot='dialog-portal' {...props} />; };
TypeScript
볡사
DialogPortal μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. radix-ui의 Dialog - Portal을 κ°μ‹ΈλŠ” wrapper ν˜•νƒœλ‘œ μ»΄ν¬λ„ŒνŠΈκ°€ κ΅¬ν˜„λ˜μ–΄ μžˆλ‹€. DialogPortal은 μ»΄ν¬λ„ŒνŠΈλ₯Ό λ°°μΉ˜ν•œ μœ„μΉ˜μ™€ 상관 없이 DOM의 λ‹€λ₯Έ μœ„μΉ˜μ— λ Œλ”λ§ ν•˜κ²Œ ν•΄μ£ΌλŠ” κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€. UIμ—μ„œ 툴팁, λ“œλ‘­λ‹€μš΄, λͺ¨λ‹¬κ°™μ€ κ²½μš°λŠ” λΆ€λͺ¨ DOM ꡬ쑰에 영ν–₯을 λ°›μœΌλ©΄ μ•ˆ λ˜λŠ” κ²½μš°κ°€ λ§Žμ€λ° λŒ€ν‘œμ μœΌλ‘œ z-index μ§€μ˜₯κ³Ό 같이 λΆ€λͺ¨ μ»΄ν¬λ„ŒνŠΈμ˜ μŠ€νƒ€μΌλ§μ— 영ν–₯을 λ°›μ•„ μ»΄ν¬λ„ŒνŠΈκ°€ μ˜¬λ°”λ₯΄κ²Œ λ Œλ”λ§ λ˜μ§€ μ•ŠλŠ” κ²½μš°μ΄λ‹€.
DialogClose
const DialogClose = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) => { return <DialogPrimitive.Close data-slot='dialog-close' {...props} />; };
TypeScript
볡사
Dialogλ₯Ό 닫을 수 있게 ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. radix-ui의 Dialog - Close μ»΄ν¬λ„ŒνŠΈλ₯Ό κ°μ‹ΈλŠ” ν˜•νƒœλ‘œ κ΅¬ν˜„ λ˜μ–΄μžˆλ‹€.
DialogOverlay
const DialogOverlay = ({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) => { return ( <DialogPrimitive.Overlay data-slot='dialog-overlay' className={cn( 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fase-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50', className, )} {...props} /> ); };
TypeScript
볡사
Dialog의 λ°°κ²½ μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. fixed와 inset을 μ‚¬μš©ν•˜μ—¬ ν™”λ©΄ 전체λ₯Ό 감싸고 data-state 속성을 ν™œμš©ν•˜μ—¬ μ—΄λ¦Ό μƒνƒœμ— 따라 μŠ€νƒ€μΌλ§μ„ μ μš©ν•œλ‹€.
DialogContent
const DialogContent = ({ className, children, showCloseButton = true, ...props }: React.ComponentProps<typeof DialogPrimitive.Content> & { showCloseButton?: boolean; }) => { return ( <DialogPortal data-slot='dialog-portal'> <DialogOverlay /> <DialogPrimitive.Content data-slot='dialog-content' className={cn( 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg', className, )} {...props} > {children} {showCloseButton && ( <DialogPrimitive.Close data-slot='dialog-close' className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" > <XIcon /> <span className='sr-only'>Close</span> </DialogPrimitive.Close> )} </DialogPrimitive.Content> </DialogPortal> ); };
TypeScript
볡사
DialogContent μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. Dialog의 컨텐츠λ₯Ό ν‘œμ‹œν•˜λ©° 이전에 μž‘μ„±ν•œ μ»΄ν¬λ„ŒνŠΈλ“€μ„ μž¬ν™œμš©ν•˜μ—¬ κ΅¬ν˜„ν•œλ‹€. DialogContent에 ν‘œμ‹œ λ˜λŠ” 컨텐츠듀은 children으둜 radix-ui의 Dialog - Content 내뢀에 λ Œλ”λ§ λœλ‹€.
DialogHeader
const DialogHeader = ({ className, ...props }: React.ComponentProps<'div'>) => { return ( <div data-slot='dialog-header' className={cn('flex flex-col gap-2 text-center sm:text-left', className)} {...props} /> ); };
TypeScript
볡사
Dialog의 상단에 ν‘œμ‹œ λ˜λŠ” Headerλ₯Ό μœ„ν•œ μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. flex display이고 μ„Έλ‘œλ‘œ μ»΄ν¬λ„ŒνŠΈλ“€μ„ λ°°μΉ˜ν•œλ‹€.
DialogFooter
const DialogFooter = ({ className, ...props }: React.ComponentProps<'div'>) => { return ( <div data-slot='dialog-footer' className={cn( 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className, )} {...props} /> ); };
TypeScript
볡사
Dialog ν•˜λ‹¨μ— 배치 λ˜λŠ” DialogFooter μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. νŠΉμ΄ν•œ 뢀뢄이 μžˆλŠ”λ° λ°”λ‘œ flex-col-reverse이닀. flex둜 배치 λ˜λŠ” μˆœμ„œλ₯Ό λ³€κ²½ν•œκ±΄λ° μ˜ˆμ‹œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.
[Save] [Cancel]
Plain Text
볡사
μœ„μ˜ ν˜•μ‹μœΌλ‘œ λ²„νŠΌμ΄ 배치 λ˜μ–΄μžˆλ‹€κ³  κ°€μ •ν•œλ‹€. sm:flex-rowλŠ” λ°μŠ€ν¬ν†± 크기의 ν™”λ©΄μ—μ„œ μš”μ†Œλ₯Ό κ°€λ‘œλ‘œ λ°°μΉ˜ν•œλ‹€. 그리고 λͺ¨λ°”일 ν™”λ©΄μœΌλ‘œ κ°€λ©΄ μ›λž˜λŠ” μ•„λž˜μ™€ 같이 μ„Έλ‘œλ‘œ 배치 λ˜μ–΄μ•Ό ν•œλ‹€.
[Save] [Cancel]
Plain Text
볡사
ν•˜μ§€λ§Œ, flex-col-reverse둜 인해 μ•„λž˜μ™€ 같이 배치 λœλ‹€.
[Cancel] [Save]
Plain Text
볡사
λ²„νŠΌμ˜ κΈ°λŠ₯에 μ€‘μš”λ„λ₯Ό λΆ€μ—¬ν•˜μ—¬ λ²„νŠΌμ˜ μš°μ„ μˆœμœ„λ₯Ό μƒκ°ν•œ 배치라고 μƒκ°ν•œλ‹€.
DialogTitle
const DialogTitle = ({ className, ...props }: React.ComponentProps<'div'>) => { return ( <DialogPrimitive.Title data-slot='dialog-title' className={cn('text-lg leading-none font-semibold', className)} {...props} /> ); };
TypeScript
볡사
DialogTitle 내뢀에 배치 λ˜λŠ” Title μ»΄ν¬λ„ŒνŠΈμ΄λ‹€.
DialogDescription
const DialogDescription = ({ className, ...props }: React.ComponentProps<'div'>) => { return ( <DialogPrimitive.Description data-slot='dialog-description' className={cn('text-muted-foreground text-sm', className)} {...props} /> ); };
TypeScript
볡사
이 μ»΄ν¬λ„ŒνŠΈλŠ” Dialog에 λŒ€ν•œ μ„€λͺ…을 ν‘œμ‹œν•˜κΈ° μœ„ν•œ μ»΄ν¬λ„ŒνŠΈμ΄λ‹€. 예제λ₯Ό 보면 주둜 DialogHeader μ•ˆμ— DialogTitleκ³Ό ν˜•μ œ depth에 배치 λœλ‹€.