사용자 인터페이스 디자인 개념에서 자식 윈도에서 부모 윈도로 돌아가기 전에 사용자의 상호동작을 요구하는 창을 말한다.
2차 프로젝트에서 메가박스에서는 로그인을 할 경우 사이트로 이동되는 것이 아닌 로그인 모달창(modal window)으로 로그인을 진행하기 때문에 만들게 되었다.
modal 창을 만들 때 동기분의 블로그를 참고하여 만드니 아주 쉽게 되었으나 정리가 필요하다는 느낌이 들었다.
먼저 코드를 만들기 전 modal 창을 만들 때 nav에서 로그인 버튼을 클릭하면 modal 창을 만들게하고 modal 창이 종료되는 것은
1. modal 창 바깥을 클릭했을 때
2. esc버튼을 눌렀을 때
3. modal 창의 X 버튼을 눌렀을 때
이렇게 3가지 경우를 모두 생각해야했다.
Nav.js
const [isModalOpen, setIsModalOpen] = useState(false); // 처음은 false로 초기값을 설정하고 click을 했을 때 true로 변경하여 modal 창이 열리게 한다.
const modalOpen = () => {
setIsModalOpen(true);
}; // modalOpen을 onClick안에 넣었을 때 ture가 될 수 있도록 변경
...
return (
...
<NavRightText onClick={localStorage.getItem('token') ? signOut : modalOpen}> //token이 없으면 modal 창 open 설정
{localStorage.getItem('token') ? '로그아웃' : '로그인'} // token 없으면 로그인으로 문구 변경
</NavRightText>
{isModalOpen && <Login setIsModalOpen={setIsModalOpen} />} // isModalOpen 값이 false여야 Login component가 실행될 수 있도록 설정
...
Login.js
const Login = ({ setIsModalOpen }) => {
const modalClose = e => {
if (e.target === e.currentTarget) {
setIsModalOpen(false);
}
};
return (
<LoginWrapper onClick={modalClose}>
<LoginModal setIsModalOpen={setIsModalOpen} />
</LoginWrapper>
);
const LoginWrapper = styled.section`
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
z-index: 10;
`;
export default Login;
};
e.target은 클릭했을 때 해당 클릭한 부분을 표시하고 e.currentTarget은 클릭한 전체를 감싸는 부분을 나타낸다.
해당 코드에서 e.currentTarget은 LoginWrapper styled component이며 e.target이 로그인화면의 바깥부분을 클릭해야 창이 닫힐 수 있다.
console.log(e.currentTarget) 결과
LoginModal.js
const LoginModal = ({ setIsModalOpen }) => {
useEffect(() => {
const escKeyModalClose = e => {
if (e.key === 'Escape') {
setIsModalOpen(false);
}
}; // esc key를 눌렀을 때 Modal 창 close
window.addEventListener('keydown', escKeyModalClose); // 위에 만들어 놓은 escKeyModalClose를 keydown했을 때 이벤트로 등록한다. 즉, esc를 눌렀을 때 modal창 종료
return () => window.removeEventListener('keydown', escKeyModalClose); // 위의 이벤트를 제거
}, [setIsModalOpen]);
return (
<LoginModalWrapper>
<LoginHeader setIsModalOpen={setIsModalOpen} />
<LoginBody />
</LoginModalWrapper>
);
};
const LoginModalWrapper = styled.header`
position: fixed;
width: 26.25rem;
top: 50%;
left: 50%;
background-color: white;
transform: translate(-50%, -50%);
z-index: 20;
`;
export default LoginModal;
잘못된 예
if (e.keyCode === 27) {
setIsModalOpen(false);
}
처음 코드를 만들었을 때 esc에 해당하는 27번의 아스키코드를 사용했으나 멘토님께서 현재 아스키코드를 사용하지 않는다고 하여 검색해보니 여러 자료들이 나와 답변을 활용하여 진행했다.
https://stackoverflow.com/questions/3369593/how-to-detect-escape-key-press-with-pure-js-or-jquery
LoginHeader.js
const LoginHeader = ({ setIsModalOpen }) => {
return (
<LoginHeaderWrapper>
<LoginTop>로그인</LoginTop>
<ExitButton onClick={() => setIsModalOpen(false)}>
<AiOutlineClose color="white" size="16" />
</ExitButton>
</LoginHeaderWrapper>
);
};
const LoginHeaderWrapper = styled.div`
position: relative;
width: 26.25rem;
height: 2.813rem;
background-color: ${props => props.theme.colors.purple};
`;
const LoginTop = styled.h3`
padding: 0.938rem 1.25rem 0 1.25rem;
color: ${props => props.theme.colors.white};
font-size: 1.2rem;
`;
const ExitButton = styled.div`
position: absolute;
top: 0.938rem;
right: 1.25rem;
color: white;
cursor: pointer;
`;
export default LoginHeader;
reference
https://velog.io/@remon/React-Project-%EB%A6%AC%EB%B7%B0-%EC%93%B0%EA%B8%B0-%ED%8F%BC-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-Form-%EC%97%B4%EA%B3%A0-%EB%8B%AB%EA%B8%B0
https://velog.io/@edie_ko/JavaScript-event-target%EA%B3%BC-currentTarget%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
https://opentutorials.org/course/1375/6761
https://developer.mozilla.org/ko/docs/Web/API/EventTarget/removeEventListener
https://webisfree.com/2019-12-30/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EA%B1%B0-removeeventlistener
https://stackoverflow.com/questions/3369593/how-to-detect-escape-key-press-with-pure-js-or-jquery
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode