프로젝트를 수행하면서 위와 같은 모달창을 구현해야 했다.
위의 이미지와 같이 로그인 요청, 수량 변경, 알림, 삭제의 상황에서 그에 맞는 내용을 담아 사용자에게 보여주고 싶었다.
그리고 font-weight
의 속성과 같이 각기 다른 스타일도 요구하지만 대부분의 UI가 동일하기 때문에 모달창을 컴포넌트화 한 채로 동적인 모달창을 만들기로 했다.
모달창이 띄워지는 상황에 맞춰서 알맞은 콘텐츠를 props
로 넘겨주면 될 듯하다.
우선, 위의 애니매이션에서처럼 상품 상세페이지에서 장바구니 담기 버튼을 눌렀을 때에 모달이 띄워지고 없어지게 하는 건 isModal
이라는 Boolean
값으로 관리한다.
그리고 띄워진 모달창이 어떤 콘텐츠를 띄울지를 결정하는 건 modalMODE
라는 상태변수로 관리한다.
마지막으로 사용자에게 나타낼 알림문구를 나타낼 상태변수 또한 생성한다.
// src/view/ProductDetail.js
const [isModal, setIsModal] = useState(false);
const [modalMODE, setModalMODE] = useState('');
const [message, setMessage] = useState('');
위에 작성한 두 상태변수를 장바구니 버튼을 눌렀을 때에 다음과 같이 변경해준다.
또한 장바구니 버튼을 눌렀을 때에 modalMode
의 상태 값을 변경해줌과 동시에 사용자에게 나타낼 알림문구도 변경해준다.
const putInCart = useCallback(async() => {
try {...
setMessage("장바구니에 추가되었습니다.")
} catch (err) {
setMessage(err.response.data.FAIL_message) // "현재 재고보다 더 많은 수량을 담을 수 없습니다."
} finally {
setIsModal(true)
setModalMODE('ALERT')
}
}, [setIsModal, setModalMODE, setMessage])
...
// 장바구니 버튼
<input type='button' onClick={putInCart} value='장바구니' className={styles.cart} />
Modal을 위한 상태 값들을 Modal
컴포넌트에 넘겨준다.
<Modal isModal={isModal} setIsModal={setIsModal} MODE={modalMODE} message={message} />
이제 Modal
이라는 컴포넌트를 생성한 뒤에 기본적인 구조의 코드를 작성해보자.
let content = null;
export const Modal = ({ MODE, isModal, ...props }) => {
if (MODE === 'ALERT') {
content = <AlertModal {...props} />;
}
return (
<div className={isModal ? `${styles.modal} ${styles.open}` : `${styles.modal}`}>
<div className={isModal ? `${styles.container}` : 'none'} >
{content}
</div>
</div>
)
}
@keyframes modal-bg-show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.modal {
display: none;
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
z-index: 99;
background-color: rgba(0, 0, 0, 0.6);
}
.container {
width: 360px;
height: 200px;
border: 1px solid #c4c4c4;
position: relative;
margin: 0 auto;
top: 300px;
overflow: hidden;
background-color: #fff;
border-radius: 5px;
}
.open {
display: block;
animation: modal-bg-show 0.3s;
}
위에서 언급했듯이 위의 코드와 같이 MODE
라는 값을 통해 어떤 콘텐츠를 나타낼지 표시해줄 것이다.
사실 여기까지 진행되었다면 동적인 모달창 구현은 어느정도 마무리되었다.
이제 모달창을 띄울 모든 상황에 맞춰 ModalMODE
상태 값을 MODE
라는 props
값으로 넘겨주면 된다.
// src/components/CartItem.js
// 수량 변경하는 버튼
<div className={styles['p-count']}
onClick={() => (
setIsModal(true),
setModalMODE('QUANTITY') // modalMODE를 QUANTITY 라는 값으로 변경
)
}>
</div>
// 삭제하는 버튼
// src/components/CartItem.js
<input type='button' className={styles.delete}
onClick={() => (
setIsModal(true),
setModalMODE('DELETE') // modalMODE를 DELETE 라는 값으로 변경
}/>
// src/components/Nav.js
// 로그인을 하지 않은 상태에서 장바구니 버튼을 눌렀을 때
<input type="button" value="장바구니" onClick={() => setIsModal(!isModal)} className={styles.cart}/>
이렇게 변경된 MODE
값을 통해 각기 다른 콘텐츠를 나타낼 수 있게 해준다.
import { LoginModal, DeleteModal, QuantityModal, AlertModal } from './ModalContents';
let content = null;
export const Modal = ({ MODE, isModal, ...props }) => {
if (MODE === 'ALERT') {
content = <AlertModal {...props} />;
} else if (MODE === 'DELETE') {
content = <DeleteModal {...props}/>;
} else if (MODE === 'QUANTITY') {
content = <QuantityModal {...props} />
} else {
content = <LoginModal {...props} isModal={isModal} />;
}
return (
<div className={isModal ? `${styles.modal} ${styles.open}` : `${styles.modal}`}>
<div className={isModal ? `${styles.container}` : 'none'} >
{content}
</div>
</div>
)
}
마지막으로 각 콘텐츠에 담길 컴포넌트들을 생성해주면 완성이다.
// src/components/ModalContents.js
export const LoginModal = ({ setIsModal, isModal }) => {
return(
<>
<img src={closeImage} alt={'닫기 버튼'} onClick={() => setIsModal(false)}/>
<h4>로그인이 필요한 서비스입니다.<br/>로그인 하시겠습니까?</h4>
<div>
<input type='button' value='아니오' onClick={() => setIsModal(!isModal)}/>
<Link to={'/login'}>
<input type='button' value='예' />
</Link>
</div>
</>
)
}
// src/components/ModalContents.js
export const DeleteModal = ({ setIsModal, deleteItem }) => {
return (
<>
<img src={closeImage} alt={'닫기 버튼'} onClick={() => setIsModal(false)}/>
<h4>상품을 삭제하시겠습니까?</h4>
<div>
<input type='button' value='취소' onClick={() => setIsModal(false)}/>
<input type='button' value='확인' onClick={deleteItem}/>
</div>
</>
)
}
// src/components/ModalContents.js
export const QuantityModal = ({ setIsModal, cart, handlePlus, handleMinus, updateQuantity }) => {
return(
<>
<img src={closeImage} alt={'닫기 버튼'} onClick={() => setIsModal(false)}/>
<article onClick={() => setIsModal(true)}>
<input type='button' value='' onClick={handleMinus} className={styles.minus} />
<span>{cart.quantity}</span>
<input type='button' value='' onClick={handlePlus} className={styles.plus} />
</article>
<div>
<input type='button' value='취소' onClick={() => setIsModal(false)}/>
<input type='button' value='확인' onClick={updateQuantity}/>
</div>
</>
)
}
// src/components/ModalContents.js
export const AlertModal = ({ setIsModal, message }) => {
return (
<>
<img src={closeImage} alt={'닫기 버튼'} onClick={() => setIsModal(false)}/>
<p>{message}</p>
<div>
<input type='button' value='취소' onClick={() => setIsModal(false)}/>
<input type='button' value='확인' onClick={() => setIsModal(false)}/>
</div>
</>
)
}