1. 간단하게 모달 창 구현하기( + 배경 블러 처리)
이런 Home화면에서 도움말 버튼을 클릭했을 때, 모달창이 떠서 정보를 확인하고 싶은데 어떻게 할 수 있을까?
나는 리액트를 처음 접하게 되었을 때, 굉장히 막막하게 느껴졌던 기억이 있어 쉽게 정리를 해보고자 한다.
Modal 기능 구현에는 단 State 1개 함수 2개로 구현이 가능하다.
const [isOpen, setIsOpen] = useState(false);
const modalOpen = () => {
setIsOpen(true);
};
const modalClose = () => {
setIsOpen(false);
};
Home화면에 위의 코드에 해당하는 모달창이 열려있는지 닫혀 있는지에 대한 상태인 isOpen State1개와 열고 닫는 함수 2개가 필요하다.
Q. 단순 State와 이벤트 처리 함수로 가능할까?
A. 추후 Modal 컴포넌트에서 설명하도록 하겠다.
그렇다면 Home화면에는 어떤 컴포넌트만 존재하면 될까?
이후 Home화면에 Modal을 열 버튼컴포넌트와 Modal컴포넌트만 존재하면 된다!
return (
<>
<HelpButton modalOpen={modalOpen} />
...
<Modal isOpen={isOpen} modalClose={modalClose} />
</>
);
Q. Modal 컴포넌트가 위처럼 존재하면 화면을 가리지 않나요?
A. 가리지 않는다. 밑에 modal 코드를 보면서 설명하도록 하겠다.
return (
<>
{isOpen && (
<>
<BlurContainer onClick={modalClose} />
<BaseContainer>
...
</BaseContainer>
</>
)}
</>
);
Modal 페이지에서는 isOpen과 modalClose를 props로 받고, isOpen이 true면 렌더링 false면 화면에 안 띄워주면 모달이 완성된다!
위 코드에서 BlurContainer는 모달창 제외 흐리게 처리한 것이고, 모달창은 Base에 해당된다.
const BlurContainer = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(4px);
z-index: 1;
`;
const BaseContainer = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 75vw;
height: 85vh;
border-radius: 20px;
background-color: white;
z-index: 2;
padding: 10px 20px;
`;
z-index를 통해 Blur처리를 해준 div를 높이 1에 전체 화면을 채우고, 내용이 들어갈 Base div를 그위에 z-index=2를 통해 얹어준형태이다.
이때 바깥을 클릭했을 때와 X표시를 클릭했을 때 둘 다 모달이 닫히기 위해
<BlurContainer onClick={modalClose} />
<BaseContainer>
<CloseContainer>
<img
src={closeIconUrl}
alt="Close Icon"
width="18px"
onClick={modalClose}
/>
</CloseContainer>
두 파트 모두 onClick시에 madalClose함수를 전해주도록 하면 된다. 이렇게 모달을 닫게 됨으로써 모달을 켜고 끄는 모든 기능이 완성된 것이다.
2. Modal 구현 정리
1. Home 화면에 isOpen State
2. Home 화면에 modalClose, modalOpen 메서드
3. Home 화면에 모달을 열 수 있는 버튼에 props로 modalOpen전해주기
4. Button에서 모달을 열 수 있도록 받은 props인 modalOpen을 버튼 onClick시에 전해주기
5. Modal에서 X버튼 또는 blur처리한 div를 클릭 시에 꺼지도록 onClcik에 modalClose를 전해주기
3. 전체 코드
- Modal 전체 코드
import React from 'react';
import styled from 'styled-components';
import closeIconUrl from '../assets/close.svg';
const Modal = ({ isOpen, modalClose }) => {
return (
<>
{isOpen && (
<>
<BluContainer onClick={modalClose} />
<BaseContainer>
<CloseContainer>
<img
src={closeIconUrl}
alt="Close Icon"
width="18px"
onClick={modalClose}
/>
</CloseContainer>
<h1>모달창</h1>
</BaseContainer>
</>
)}
</>
);
};
const BluContainer = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(4px);
z-index: 1;
`;
const BaseContainer = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 75vw;
height: 85vh;
border-radius: 20px;
background-color: white;
z-index: 2;
padding: 10px 20px;
`;
const CloseContainer = styled.div`
width: 100%;
height: 30px;
display: flex;
justify-content: end;
`;
export default Modal;
'
- Home 전체 코드
import styled from 'styled-components';
import { useState } from 'react';
import ConvertButton from '../components/ConvertButton';
import TextField from '../components/TextField';
import CopyButton from '../components/CopyButton';
import HelpButton from '../components/HelpButton';
import Modal from './Modal';
function Home() {
const [input, setInput] = useState('');
const [result, setResult] = useState('');
const [isOpen, setIsOpen] = useState(false);
const onChangeInput = (e) => {
setInput(e.target.value);
};
const onClickConvert = () => {
const firstProcess = input.replace(/[“”]/g, '"');
const secondProcess = firstProcess.replace(/^\d{2}/gm, '');
const thirdProcess = secondProcess.replace(/^ /gm, '');
setResult(thirdProcess);
};
const onClickCopy = () => {
handleCopyClipBoard(result);
};
const handleCopyClipBoard = async (text) => {
try {
await navigator.clipboard.writeText(text);
alert('클립보드에 링크가 복사되었습니다.');
} catch (e) {
alert('복사에 실패하였습니다');
}
};
const modalOpen = () => {
setIsOpen(true);
console.log(isOpen);
};
const modalClose = () => {
setIsOpen(false);
};
return (
<>
<AllowedContainer>
<TopContainer>
<HelpButton modalOpen={modalOpen} />
</TopContainer>
<BaseContainer>
<TextField
text="📝 코드를 입력 하세요"
value={input}
onChange={onChangeInput}
></TextField>
<ButtonWrapper>
<ConvertButton onClick={onClickConvert}></ConvertButton>
<CopyButton onClick={onClickCopy}></CopyButton>
</ButtonWrapper>
<TextField text="🤖 변환 결과" value={result}></TextField>
</BaseContainer>
</AllowedContainer>
<Modal isOpen={isOpen} modalClose={modalClose} />
</>
);
}
export default Home;
const AllowedContainer = styled.div`
height: 100vh;
padding: 20px;
`;
const TopContainer = styled.div`
width: 100%;
height: 20px;
display: flex;
justify-content: end;
`;
const BaseContainer = styled.div`
display: flex;
padding: 20px;
width: 100%;
height: 100%;
align-items: center;
justify-content: space-around;
`;
const ButtonWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 40px;
align-items: center;
`;
- 모달 버튼 전체 코드
import React from 'react';
import questionIconUrl from '../assets/question.svg';
import styled from 'styled-components';
const HelpButton = ({ modalOpen }) => {
return (
<Container onClick={modalOpen}>
<ButtonText>도움말</ButtonText>
<img src={questionIconUrl} alt="Question Icon" width="15px" />
<Info>
<ButtonText>소개</ButtonText> <ButtonText>📢</ButtonText>
</Info>
</Container>
);
};
export default HelpButton;
const Container = styled.div`
width: 105px;
height: 45px;
border-radius: 15px;
background: #52b7fa;
box-shadow: 0px 5px 0px 0px #157dc2;
display: flex;
gap: 10px;
align-items: center;
padding-left: 10px;
position: relative;
&:hover {
margin-top: 10px;
margin-bottom: 5px;
box-shadow: 0px 0px 0px 0px #157dc2;
}
`;
const ButtonText = styled.div`
text-align: center;
font-size: 18px;
color: white;
`;
const Info = styled.div`
width: 100%;
height: 100%;
position: absolute;
background: #52b7fa;
top: 0;
left: 0;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
border-radius: 15px;
animation: fadeInOut 12s infinite ease-in-out;
opacity: 0;
pointer-events: none;
@keyframes fadeInOut {
0%,
20% {
opacity: 0;
}
25%,
75% {
opacity: 1;
}
80%,
100% {
opacity: 0;
}
}
`;
'React.js > React 프로젝트 및 구현' 카테고리의 다른 글
[React.js] 리액트 글자 타이핑 효과 구현 (직접 구현/전체 코드) (0) | 2024.10.27 |
---|---|
[React.js] 리액트 원페이지 스크롤 구현 (직접 구현/스크롤 유도) (4) | 2024.10.27 |
[React.js] 구글 페이지 번역 (전체 번역 및 언어 선택 버튼) (4) | 2024.10.18 |
[React.js] vite.config.js에서 env사용하기 (0) | 2024.10.13 |
[React.js] input checkbox css / 간단하게 checkbox css 구현 ( styled-components) (0) | 2024.08.19 |