어떤 서비스를 이용할때 어떤 버튼을 클릭하면 모달이 화면에 뜨는것을 우리는 자주 볼 수 있습니다. 이 모달을 닫기 클릭을 클릭하지 않으면 화면에서 안 닫힌다고 하면 사용자 입장에서는 매우 불편하다고 느낄 수 있습니다. 열려있는 해당 모달에 볼일이 없으면 모달 외의 화면을 클릭 했을때 모달이 닫히면 사용자 입장에서 편하고 좋지 않을까요?
오늘은 모달이 화면에 떳을때 외부 영역 클릭 시 화면에서 사라지는 기능을 리액트로 구현해보도록 하겠습니다.
제가 만들고 있는 서비스를 예를 들어 설명하도록 하겠습니다.
제가 만든 드롭다운 입니다.
저 영역을 클릭하면
이렇게 드롭다운이 펼쳐지면서 선택 할 수 있는 게시물 분류 방법들이 화면에 보여지게 됩니다.
근데 어떤 분류 방법이 있는지만 보고 싶고 드롭다운 모달을 화면에서 지우고 싶을 수 있겠죠? 근데 그 방법이 다시 드롭다운 영역을 클릭해야만 모달이 닫힌다면 너무 불편하지 않을까요?
그래서 드롭다운 영역이 아닌 다른 외부 영역을 클릭했을 때 해당 모달이 닫히는 기능을 구현할려고 하는 것 입니다.
그럼 구현을 시작 해 봅시다.
저는 css 또는 다른 자바스크립트 코드는 생략하고 오직 해당 기능 구현 코드만 설명 드리겠습니다.
일단 모달이 열고 닫히는 기능을 구현하기 위해서 모달이 열렸는지 닫혔는지에 대한 isDropDownModal 이라는 state 를 생성 합니다.
해당 state 는 드롭다운 모달이 열려있는지에 대한 여부 state 입니다.
기본 값은 false 로 선언 합니다.
처음 화면에 랜더링 되었을때는 당연히 드롭다운 모달이 닫혀 있어야 겠죠?
const [isDropDownModal, setIsDropDownModal] = useState(false);
const onclickDropDown = ()=> {
setIsDropDownModal(prev=>!prev);
};
const Home = ()=> {
return <div>
<DropDown onClick = {onclickDropDown}/>
{isDropDownModal && <DropDownModal/>}
</div>
};
export default Home;
일단 위와 같이 저는 홈이라는 컴포넌트에 DropDown 컴포넌트를 불러왔습니다. 해당 컴포넌트가 저희가 위에서 보는 드롭다운 버튼 입니다. 그리고 isDropDownModal 이 true 일때만 DropDownModal 이 화면에 보이도록 하였습니다.
그리고 DropDown 을 클릭하면 onclickDropDown 함수가 실행되어 isDropDownModal state 를 변경하도록 코드를 작성 하였습니다.
현재까지는 DropDown 컴포넌트를 클릭해야만 DropDownModal 이 화면에 보이고 DropDown 컴포넌트를 클릭해야만 DropDownModal 이 닫히는 것까지 구현 한 것 입니다.
오늘 우리가 구현할려는 것은 여기서 한단계 더 업그레이드인 DropDownModal 이 열렸을때 다른 영역을 클릭했을때도 DropDownModal 이 닫히는 것 입니다! 다시 우리가 구현해야할 기능을 기억하고 이제 기능을 구현하러 가 봅시다.
기능 구현에서 저는 useRef 를 사용했습니다. useRef 가 뭔지 아시나요?
간단하게 설명하자면 react 에서 사용자가 DOM 에 접근하게 도와주는 hooks 입니다.
useRef 를 사용해서 DropDown 컴포넌트에 접근하고 바디에 mousedown 리스너를 걸어서 마우스를 클릭 할때마다 콜백 함수가 호출되어 현재 클릭한 DOM 과 DropDownRef DOM 을 비교해서 다르다면 DropDownModal 이 닫히도록 코드를 구현 하였습니다.
그럼 코드로 보시죠.
const [isDropDownModal, setIsDropDownModal] = useState(false);
const dropDownRef = useRef();
const onclickDropDown = ()=> {
setIsDropDownModal(prev=>!prev);
};
useEffect(()=> {
const outSideClick = (e) => {
const { target } = e;
if (
isDropDownModal &&
dropDownRef.current &&
!dropDownRef.current.contains(target)
) {
setIsDropDownModal(false);
}
};
document.addEventListener("mousedown", outSideClick);
}, [isSelectSort]);
const Home = ()=> {
return <div>
<DropDown ref = {dropDownRef} onClick = {onclickDropDown}/>
{isDropDownModal && <DropDownModal/>}
</div>
};
export default Home;
위와 같이 useEffect 안에 랜더링 될 때 mousedown 이벤트를 걸어서 클릭 될때마다 outSideClick 콜백 함수가 호출되어 현재 클릭한 DOM 과 dropDownRef 에 저장되어 있는 DOM 과 비교하여 다르다면 DropDownModal 을 isDropDownModal state 를 false 로 변경하여 닫히도록 구현 하였습니다.
위와 같이 사소하지만 작은 기능들이 사용자에게 한번 더 사용하고 싶은 서비스로 기억하게 될 것 입니다.