↓↓↓↓이 프로젝트의 전체 코드는 GitHub에서 확인하실 수 있습니다.↓↓↓↓
https://github.com/GangHyun95/calendar-todo
GitHub - GangHyun95/calendar-todo: with typescript
with typescript. Contribute to GangHyun95/calendar-todo development by creating an account on GitHub.
github.com
이번 포스팅에서는 To-Do List에서 모달 컴포넌트를 구현하고 사용하는 방법에 대해 다뤄보겠습니다.
이 모달 컴포넌트는 '할 일 추가하기' 기능을 위한 UI를 구현한 것으로, 추후 실제 기능 구현이나 수정 기능에도 재사용할 수 있도록 유연하게 설계되었습니다.
모달 컴포넌트 구현
import React from "react";
import styles from "./Modal.module.css";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
header?: string;
}
export default function Modal({
isOpen,
onClose,
children,
header,
}: ModalProps) {
if (!isOpen) return null;
const handleBgClick = (e: React.MouseEvent<HTMLElement>) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div className={styles["modal-overlay"]} onClick={handleBgClick}>
<div
className={styles["modal-content"]}
onClick={(e) => e.stopPropagation()}
>
{header && (
<div className={styles["modal-header"]}>
<h2>{header}</h2>
</div>
)}
<div className={styles["modal-body"]}>{children}</div>
</div>
</div>
);
}
이 모달 컴포넌트는 isOpen 상태에 따라 렌더링 여부를 결정합니다. onClose는 모달을 닫는 함수로, 배경 클릭 시 모달을 닫을 수 있도록 처리되었습니다. header는 선택적으로 모달의 상단에 표시될 제목을 정의할 수 있으며, children을 통해 모달 내부의 컨텐츠를 동적으로 전달받아 표시합니다.
모달 컴포넌트 사용 예시
import React, { useEffect, useState } from "react";
import styles from "./TodoHeader.module.css";
import { GiNotebook } from "react-icons/gi";
import Modal from "../Modal/Modal";
import { useDate } from "../../context/DateContext";
export default function TodoHeader() {
const [isModalOpen, setModalOpen] = useState<boolean>(false);
const { currentDate } = useDate();
const [taskDate, setTaskDate] = useState(
currentDate.toISOString().split("T")[0]
);
const openModal = () => setModalOpen(true);
const closeModal = () => setModalOpen(false);
function formatDate(date: Date) {
return new Date(date.getTime() - date.getTimezoneOffset() * 60000)
.toISOString()
.split("T")[0];
}
useEffect(() => {
setTaskDate(formatDate(currentDate));
}, [currentDate]);
return (
<div className={styles["todo-header"]}>
<div className={styles.left}></div>
<div className={styles.center}>
<span>Check List</span>
<GiNotebook />
</div>
<div className={styles.right}>
<button className={styles.button} onClick={openModal}>
<span>New Task</span>
</button>
</div>
<Modal isOpen={isModalOpen} onClose={closeModal} header="추가하기">
<div>
<input
type="date"
className={styles.date}
id="task-date"
name="task-date"
value={taskDate}
onChange={(e) => setTaskDate(e.target.value)}
/>
</div>
<div>
<textarea
id="task-text"
className={styles.text}
name="task-text"
rows={4}
placeholder="할 일을 입력 해 주세요."
></textarea>
</div>
<div className={styles["button-wrap"]}>
<button onClick={closeModal}>추가</button>
<button onClick={closeModal}>취소</button>
</div>
</Modal>
</div>
);
}
코드 설명
- taskDate: taskDate는 input[type="date"] 필드의 value로 사용되는 State입니다.
이 State는 사용자가 선택한 날짜를 저장하며, 기본적으로 현재 선택된 날짜(currentDate)를 기반으로 초기화됩니다. - formatDate: currentDate를 YYYY-MM-DD 형식으로 변환하기 위해 formatDate 함수를 사용합니다.
이는 input[type="date"]의 value로 사용하기 위함입니다. - getTimezoneOffset:
getTimezoneOffset 메서드는 현재 로컬 시간과 UTC 시간 간의 차이를 분 단위로 반환합니다. 한국 시간은 UTC보다 9시간 빠르기 때문에, 이 차이를 고려하지 않으면 날짜가 하루 전으로 표시될 수 있습니다. 반환된 값이 분 단위이므로, 밀리초로 변환하기 위해 1분을 60000밀리초로 계산한 후, 이를 현재 시간에서 빼 줌으로써 정확한 로컬 시간을 얻을 수 있습니다.
- 모달 관리: isModalOpen 상태로 모달의 열림/닫힘을 관리합니다. openModal과 closeModal 함수는 각각 모달을 열고 닫는 역할을 합니다.
date.getFullYear(), date.getMonth() + 1, date.getDate()를 사용해서 처리할 수도 있었지만, month와 date가 한 자리일 경우 앞에 0을 붙여서 반환하는 방식보다 이 방법이 더 간단하다고 판단하여 이렇게 구현했습니다.
'React > Calendar Todo' 카테고리의 다른 글
React로 달력형 To-Do List 만들기 - 마무리 (with TypeScript) (0) | 2024.08.20 |
---|---|
React로 달력형 To-Do List 만들기 - 5 (with TypeScript) (0) | 2024.08.19 |
React로 달력형 To-Do List 만들기 - 4 (with TypeScript) (0) | 2024.08.16 |
React로 달력형 To-Do List 만들기 - 2 (with TypeScript) (0) | 2024.08.14 |
React로 달력형 To-Do List 만들기 - 1 (with TypeScript) (0) | 2024.08.13 |