1. 전역에 ModalProvider를 만든다.

'use client';

import { useEffect, useState } from 'react';

import Modal from '@/components/Modal';

function ModalProvider() {
  const [isMounted, setIsMounted] = useState(false);

  // 모달은 서버에서 실행안되니깐 트릭걸어줘야함
  useEffect(() => {
    setIsMounted(true);
  }, []);
	// 서버에서는 null 보여줌
  if (!isMounted) {
    return null;
  }

  return (
    <>
      <Modal />
    </>
  );
}

export default ModalProvider;

2. Radix 사용

npm install @radix-ui/react-dialog

Radix로 기본이 되는 부모 모달 컴포넌트 생성

import * as Dialog from '@radix-ui/react-dialog';
import { ReactNode } from 'react';
import { IoMdClose } from 'react-icons/io';

interface ModalProps {
  isOpen: boolean;
  onChange: (open: boolean) => void;
  title: string;
  description: string;
  children: ReactNode;
}

function Modal({ isOpen, onChange, title, description, children }: ModalProps) {
  return (
    <Dialog.Root open={isOpen} defaultOpen={isOpen} onOpenChange={onChange}>
      <Dialog.Portal>
        <Dialog.Overlay className='bg-neutral-900/90 backdrop-blur-sm fixed inset-0' />
        <Dialog.Content className='fixed drop-shadow-md border border-neutral-700 top-[50%] left-[50%] max-h-full h-full md:h-auto md:max-h-[85vh] w-full md:w-[90vw] md:max-w-[450px] translate-x-[-50%] translate-y-[-50%] bg-neutral-800 p-[25px] focus:outline-none'>
          <Dialog.Title className='text-xl text-center font-bold mb-4'>
            {title}
          </Dialog.Title>
          <Dialog.Description className='mb-5 text-sm leading-normal text-center'>
            {description}
          </Dialog.Description>
          <div>{children}</div>
          <Dialog.Close asChild>
            <button className='text-neutral-400 hover:text-white absolute top-[10px] right-[10px] inline-flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-full focus:outline-none'>
              <IoMdClose />
            </button>
          </Dialog.Close>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

export default Modal;

자식이 되는 AuthModal 생성

'use client';

import Modal from './Modal';

function AuthModal() {
  return (
    <Modal
      title='Welcome back'
      description='Login to your account!'
      isOpen
      onChange={() => {}}
    >
      Auth Modal children
    </Modal>
  );
}

export default AuthModal;

AuthModal을 컨트롤하기 위한 전역 State 생성

import { create } from 'zustand';

interface AuthModalStore {
  isOpen: boolean;
  onOpen: () => void;
  onClose: () => void;
}

const useAuthModal = create<AuthModalStore>((set) => ({
  isOpen: false,
  onOpen: () => set({ isOpen: true }),
  onClose: () => set({ isOpen: false }),
}));

export default useAuthModal;