모달이란?
모달(Modal)은 사용자의 이목을 끌기 위해 사용하는 화면전환 기법을 말한다.
모달의 종류를 세 가지정도로만 나누어보자면,
안내창(Alert), 확인창(Confirm), 입력창(Prompt)로 크게 나눌 수 있는 것 같다.
우선, ant-design에서 Modal 탭에 들어가보았다.
만약 ant-Design, material-ui에서 Modal을 사용하는 것이 너무 힘들다면, 조건부 렌더링을 통해 직접 만들 수도 있다.
import { Modal } from "antd";
export default function ModalAlertPage() {
const onClickSuccess = (): void => {
Modal.success({ content: "게시물 등록에 성공했습니다!!" });
};
const onClickError = (): void => {
Modal.error({ content: "비밀번호가 틀렸습니다!!" });
};
return (
<>
<button onClick={onClickSuccess}>성공했을 때!!</button>
<button onClick={onClickError}>실패했을 때!!</button>
</>
);
}
위 예제 코드는 Modal을 활용한 간단한 안내창을 보여주는 것이다.
추가로, Modal을 내 마음대로 Custom해서 사용하는 것도 당연히 가능하다.
import { Modal } from "antd";
import { useState } from "react";
export default function ModalAlertPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = (): void => {
setIsModalOpen(true);
};
const handleOk = (): void => {
setIsModalOpen(false);
};
const handleCancel = (): void => {
setIsModalOpen(false);
};
return (
<>
<button onClick={showModal}>모달창 열기!!</button>
<Modal
title="모달 제목"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
>
비밀번호 입력 : <input type="password" />
</Modal>
</>
);
}
Modal의 props로 전달되는 open 프로퍼티가 모달 작동 여부를 결정한다
위 코드를 보면, isModalOpen의 기본 값은 false이고 버튼을 클릭하면 해당 스테이트 값을 true로 변경하는 showModal 함수를 실행한다. 만약 확인 버튼 혹은 취소버튼을 누르면 스테이트 값이 false가 되면서 모달이 닫힌다.
그런데, 위와 같이 작성하면 조금 문제가 있다.
비밀번호를 입력하는 모달이기 때문에 보안성이 상당히 중요하다.
위 코드는, OK 혹은 Cancel 버튼을 눌렀을 때 해당 모달이 리셋되는 것이 아니라 그저 숨김처리가 되는 것이다.
만약 내가 공공PC에서 모달에 비밀번호를 입력한 뒤 다음 사용자가 모달을 열었을 때, 내 비밀번호가 노출되어 있으면 곤란하다.
위에서 본 방법이 모달 숨기는 방법에 해당하고, 아래 코드는 모달을 삭제하는 방법이다.
return (
<>
<button onClick={showModal}>모달창 열기!!</button>
{isModalOpen && (
<Modal
title="모달 제목"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
>
비밀번호 입력 : <input type="password" />
</Modal>
)}
</>
);
조건부 렌더링을 활용해서 isModalOpen이 true인 경우에만 모달 창을 렌더링한다.
이렇게 작성하면, 모달을 오픈할 때마다 새로운 모달이 열리게 된다.
React에는 주소를 검색하면 우편번호 및 번지 수, 도로명 주소를 알려주는 라이브러리가 존재한다.
다른 웹사이트에서 아주 많이 보던 라이브러리이다.
reat-daum-postcode npm 링크
아래 명령어로 설치 가능하다.
npm install --save react-daum-postcode
yarn add react-daum-postcode
아래는 react-daum-postcode를 활용한 예제 코드이다.
import { Modal } from "antd";
import { useState } from "react";
import { DaumPostcodeEmbed } from "react-daum-postcode";
import type { Address } from "react-daum-postcode";
export default function ModalAlertPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = (): void => {
setIsModalOpen(true);
};
const handleOk = (): void => {
setIsModalOpen(false);
};
const handleCancel = (): void => {
setIsModalOpen(false);
};
const handleComplete = (data: Address): void => {
console.log(data);
setIsModalOpen(false);
};
return (
<>
<button onClick={showModal}>모달창 열기!!</button>
{/* 모달 종료 방식 1 - 모달 숨기는 방법(ex, 이력서 등) */}
<Modal open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
<DaumPostcodeEmbed onComplete={handleComplete} />
</Modal>
{/* 모달 종료 방식 2 - 모달 삭제하는 방법(ex, 신용카드, 비밀번호 등) */}
{isModalOpen && (
<Modal open={true} onOk={handleOk} onCancel={handleCancel}>
<DaumPostcodeEmbed onComplete={handleComplete} />
</Modal>
)}
</>
);
}
마찬가지로, 확인창과 취소창을 눌렀을 때 modal state 변화로 모달을 닫아준다.
이 때, 위에서 언급한 2가지 방법으로 모달을 종료할 수 있다!
state의 원리에 대해서 공부할 때 배운 부분인데, 이 상자에 담긴 내용들은 함수가 모두 끝나게 되면 화면에 반영이 된다. state를 이해하기 위해 만들었던 counter를 다시 보자.
import { useState } from 'react'
export default function CounterLetDocumentPage() {
const [count, setCount] = useState(0);
function onClickCountUp() {
setCount(count+1);
setCount(count+1);
setCount(count+1);
}
return (
<div>
<div>{count}</div>
<button onClick={onClickCountUp}>카운트 올리기!!!</button>
</div>
)
}
위 예제를 보면, 버튼을 클릭할 때마다 count가 3씩 증가하도록 구현된 것 같지만
실제로는 1씩 증가하게 된다. state의 원리를 생각하면 마지막 setCount(count+1)만 실행되기 때문이다.
그러나, 때로는 진짜로 3씩 증가하고 싶은 경우도 생긴다.
이럴 때는, prev를 사용하여 state값을 가지고 있는 임시저장공간에 접근해주어야 한다.
import { useState } from "react";
export default function CounterLetDocumentPage() {
const [count, setCount] = useState(0);
function onClickCountUp() {
// setCount(count+1);
// setCount(count+1);
// setCount(count+1);
// setCount(count+1);
// setCount(count+1);
//임시 저장 공간에 들어있는 state값을 수정(이렇게하면 5씩 증가)
setCount((prev) => {
prev + 1;
});
setCount((prev) => {
prev + 1;
});
setCount((prev) => {
prev + 1;
});
}
return (
<div>
<div>{count}</div>
<button onClick={onClickCountUp}>카운트 올리기!!!</button>
</div>
);
}
위와 같이 작성하면, 우리가 생각한 대로 정상적으로 동작한다!
이를 모달(Modal) 활용에 적용시켜보면 아래와 같다.
const showModal = (): void => {
//setIsModalOpen(true);
setIsModalOpen((prev) => !prev);
};
const handleOk = (): void => {
//setIsModalOpen(false);
setIsModalOpen((prev) => !prev);
};
const handleCancel = (): void => {
//setIsModalOpen(false);
setIsModalOpen((prev) => !prev);
};
const handleComplete = (data: Address): void => {
console.log(data);
//setIsModalOpen(false);
setIsModalOpen((prev) => !prev);
};
그러나, 위 코드는 중복 코드가 너무나도 명확하게 눈에 띈다.
중복 코드는 리팩토링 해주는 것이 코드 가독성을 향상시키기 때문에 아래와 같이 리팩토링 해준다.
const onToggleModal = (): void => {
setIsModalOpen((prev) => !prev);
};
const handleOk = (): void => {
//setIsModalOpen(false);
onToggleModal();
};
const handleCancel = (): void => {
//setIsModalOpen(false);
onToggleModal();
};
const handleComplete = (data: Address): void => {
console.log(data);
//setIsModalOpen(false);
onToggleModal();
};