모달에서 onKeydown 이벤트를 설정할 일이 있었다.
처음에는 onKeyDown에 submit처럼 기본이벤트가 있을거라 생각해서 커스텀모달을 만들때 onKeydown 이벤트에 event.preventDefault()를 걸어주었는데, 이를 모달 내부에 추가되는 input창에서 입력시 외부 onKeydown이벤트가 이벤트 버블링에 의해 실행되기 때문에 event.stopPropagation()을 써줘야하는 문제가 있었다.
하지만 기본 이벤트가 없기 때문에 event.preventDefault() 및 event.stopPropagation()이 필요없다는 것을 깨닫고 삭제 해주어 코드를 간편하게 만들어주었다.
// components/modalCustom.tsx
"use client";
import React, {
Fragment,
KeyboardEvent,
ReactNode,
useEffect,
useState,
} from "react";
import ReactDOM from "react-dom";
import { ModalCustomProps } from "@/type/interface";
// name: 필수
export default function ModalCustom({
name,
buttonString = { cancel: "취소하기", add: "등록하기" },
design = "",
changeButton = false,
open,
onClose,
onClickEvent,
children,
}: ModalCustomProps) {
const [prevScrollY, setPrevScrollY] = useState<number | undefined>(undefined);
const [hasScrollbar, setHasScrollbar] = useState<boolean>(false);
// 스크롤이 있는지 확인하는 함수
const checkScrollbar = (): void => {
setHasScrollbar(document.body.scrollHeight > window.innerHeight);
};
const keyDownHandler = (event: KeyboardEvent<HTMLInputElement>) => {
if (!open) {
return;
}
//event.preventDefault();
// 이것을 막음으로써 이벤트 버블링이 일어나 디버깅에 시간이 소요되었다.
if (event.key === "Enter") {
document.getElementById(`${name}AddButton`)?.click();
} else if (event.key === "Escape") {
document.getElementById(`${name}CancelButton`)?.click();
}
};
useEffect((): void => {
checkScrollbar();
// 스크롤을 방지하고 현재 위치를 반환
const preventScroll = (): void => {
const currentScrollY = window.scrollY;
document.body.style.position = "fixed";
document.body.style.width = "100%";
document.body.style.top = `-${currentScrollY}px`; // 현재 스크롤 위치
document.body.style.overflowY = hasScrollbar ? "scroll" : "hidden";
setPrevScrollY(currentScrollY);
};
// 스크롤을 허용하고, 스크롤 방지 함수에서 반환된 위치로 이동
const allowScroll = (): void => {
document.body.style.position = "";
document.body.style.width = "";
document.body.style.top = "";
document.body.style.overflowY = "";
if (prevScrollY !== undefined) {
window.scrollTo(0, prevScrollY);
}
};
// 모달이 열릴 때마다 스크롤바 여부를 확인하고, 스크롤 방지/허용 함수 호출
if (open) {
checkScrollbar();
preventScroll();
} else {
allowScroll();
}
}, [open, hasScrollbar, prevScrollY]);
if (!open) return null;
return ReactDOM.createPortal(
<Fragment>
<div
className="fixed top-0 bottom-0 left-0 right-0 z-50 bg-black bg-opacity-40"
onClick={onClose}
/>
<div
tabIndex={0}
onKeyDown={keyDownHandler}
className={`fixed z-50 px-20 py-12 transform -translate-x-1/2 -translate-y-1/2 bg-white top-1/2 left-1/2 rounded-xl outline-none ${design}`}
>
{children}
<div className="flex justify-center mt-5 ">
<button
id={`${name}CancelButton`}
onClick={onClose}
className={`px-12 py-2 mr-6 text-sm font-semibold rounded-md text-neutral-100 ring-1 ring-inset ring-f5red-700/10 ${changeButton ? "bg-f5green-350 hover:bg-f5green-300" : "bg-f5red-350 hover:bg-f5red-300"}`}
>
{buttonString.cancel}
</button>
<button
id={`${name}AddButton`}
onClick={async () => {
if (await onClickEvent()) {
onClose();
}
}}
type="button"
className={`px-12 py-2 text-sm font-semibold rounded-md text-neutral-100 ring-1 ring-inset ring-f5green-700/10 ${changeButton ? "bg-f5red-350 hover:bg-f5red-300" : "bg-f5green-350 hover:bg-f5green-300"}`}
>
{buttonString.add}
</button>
</div>
</div>
</Fragment>,
document.getElementById("globalModal") as HTMLElement
);
}
// myEssay.tsx
const handleKeyDown = (
e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
if (e.key !== "Enter" && e.key !== "Escape") {
e.stopPropagation();
}
}; // 불필요한 함수
//그외
<ModalCustom
open={isEssayChangeModalOpen}
name="changeEssay"
onClose={() => setIsEssayChangeModalOpen(false)}
onClickEvent={changeEssay}
buttonString={{ cancel: "취소하기", add: "변경하기" }}
>
<div className="flex flex-col h-min-[800px] text-xl font-medium ">
<span className="text-center">🖋 자소서 변경하기 🖋</span>
<label htmlFor="inputTitle" className="font-bold">
자기소개서 항목
</label>
<input
type="text"
placeholder="빌 경우 원래 항목이 들어갑니다"
className="w-[700px] min-h-16 p-1 h-auto text-sm focus:outline-f5green-300 my-3 "
ref={essayTitleChangeRef}
id="inputTitle"
onKeyDown={(e) => handleKeyDown(e)}
// 이 onKeyDown 처리는 필요 없는 것이였다.
/>
<label htmlFor="inputEssay" className="font-bold ">
자기소개서 내용
</label>
<textarea
placeholder="빌 경우 원래 내용이 들어갑니다."
className="w-[700px] max-w-[100%] p-1 h-auto mt-3 min-h-40 text-sm my-3 focus:outline-f5green-300 text-start"
ref={essayChangeRef}
id="inputEssay"
/>
</div>
</ModalCustom>