ํธํ๊ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ๊ณ ์ถ์๋๋ฐ ๋ด๊ฐ ์ํ์ง ์๋ ๋ชจ์ ์ฐ๊ธฐ ์ซ์ด์ ใฑ-
์ผ๋จ utils/calendar/getCalendarDate.ts ๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ ๋ ์ง ๊ณ์ฐํ๋ ํจ์๋ฅผ ์์ฑํ๋ค.
@date-fns ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฑด ์ฒ์์ด๋ผ ๋ง์ ์ฝ๋๋ฅผ ๊ตฌ๊ฒฝํ๊ณ .. ๊ทธ์ค์ ํ ๋ฌ์ ๋ ์ง๋ฅผ ์ฃผ๋ณ๋ก ๋๋์ด ๋ฐฐ์ด๋ก ๋ฐํํ๋ ํจ์๋ฅผ ์์ฑ ํ ๊ฑธ ๋ถ์ํด์ ํ์ ์ ์ถ๊ฐํด์ ์์ฑํ๋ค. ๊ธฐ์ต์ด ์ ์๋๋๋ฐ ๋ฒจ๋ก๊ทธ ๊ธ์ด์๋ ๋ฏ? ์ ์ผ ์ดํดํ๊ธฐ ํธํ๊ฒ ์์ฑํ์ ์ ์ฐธ๊ณ ํด ์์ฑํ๋ค.
@date-fns ๋ฉ์๋๋ ๊ณต์ ๋ฌธ์๋ ๊ตฌ๊ธ๋ง ๋ฒ๊ฐ์๊ฐ๋ฉด์ ์ฐพ์ ์ฌ์ฉํจ !!
// ์ฃผ๋ฅผ ๋ฐํ
function takeWeek(selectedDate: Date = new Date()): () => Date[] {
let date: Date = startOfWeek(startOfDay(selectedDate))
return function (): Date[] {
const week: Date[] = [...Array(7)].map((_, idx) => addDays(date, idx))
date = addDays(week[6], 1)
return week
}
}
// ๋ง์ง๋ง ์ฃผ์ ๋ง์ง๋ง ๋ ์ ๋ฐํ
function lastDay(range: Date[][]): Date | null {
if (range.length === 0) {
return null
}
const lastWeek = range[range.length - 1]
if (lastWeek.length < 7) {
return null
}
return lastWeek[6]
}
// ์์ ํจ์๋ฅผ ์ฌ์ฉํด์ ๋ฌ์ ์ฃผ ๋ณ๋ก ๋๋๊ณ ๋ฐฐ์ด๋ก ๋ฐํ
export default function takeMonth(startDate: Date = new Date()): () => Date[][] {
let month: Date[][] = []
let date: Date = startDate
return function (): Date[][] {
const weekGen = takeWeek(startOfMonth(date))
const endDate: Date = startOfDay(endOfMonth(date))
while (lastDay(month)! < endDate) {
month.push(weekGen())
}
const range: Date[][] = month
month = []
date = addDays(lastDay(range)!, 1)
return range
}
}
๊ทธ๋ฆฌ๊ณ Calendar ํ์ด์ง์ ์ฌ์ฉํ ์ปดํฌ๋ํธ๋ก CalendarList, CalendarItem๋ก ๋๋ ๋ถ๋ฆฌํ๋ค.
์ฒ์์๋ CalendarList์ ์บ๋ฆฐ๋์ ํ์ํ ๋ชจ๋ ๊ฑธ ๋ชฐ๋นตํด์ ๋ฃ์๋๋ฐ, ์ฐ๋ค๋ณด๋ ์ดํ ์ถ๊ฐํ CRUD ๊ธฐ๋ฅ์ ํ์คํฌ ๊ฐฏ์๊น์ง ๋ฐํํ๋๋ก ๋ฃ์ผ๋ฉด ์์ฒญ๋๊ฒ ์ฝ๋๊ฐ ๊ธธ์ด์ง ๊ฒ ๊ฐ์๋ค.
์ผ๋จ CalendarList >>
์ฌ๊ธฐ์๋ ํฌ๋๋ฆฌ์คํธ ํ์ด์ง์์ ๋น์ผ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ฒ๋ผ fetchTaskMonth๋ผ๋ ์ํ๊ด๋ฆฌ ํจ์๋ฅผ ๋ฐ๋ก ์์ฑํ๊ณ useEffect๋ก currentDate๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค currentDate์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ก ํ๋ค.
const CalendarList = () => {
const toggleSidebar = useToggleSidebar(state => state.toggleSidebar)
const [currentDate, setCurrentDate] = useState(new Date())
const fetchTaskMonth = useTaskStore(state => state.fetchTaskMonth)
const prevMonth = useCallback(() => {
const minusMonth = subMonths(currentDate, 1)
setCurrentDate(minusMonth)
fetchTaskMonth(minusMonth)
}, [currentDate, fetchTaskMonth])
const nextMonth = useCallback(() => {
const addMonth = addMonths(currentDate, 1)
setCurrentDate(addMonth)
fetchTaskMonth(addMonth)
}, [currentDate, fetchTaskMonth])
useEffect(() => {
fetchTaskMonth(currentDate)
}, [currentDate, fetchTaskMonth])
return (
<>
<div>
<div>
<div>
<button onClick={toggleSidebar}>
<Image src={ThreeLine} alt="line" style={{ width: 20, height: 16 }} />
</button>
<div>Calendar</div>
</div>
<div>
<button onClick={prevMonth} className="text-neutral-600">
{'< '}prev
</button>
<h1>{format(currentDate, 'MMMM yyyy')}</h1>
<button onClick={nextMonth}>
next {' >'}
</button>
</div>
<div>
<WeekNames /> // ์ํ์๋ชฉ๊ธํ ์ผ
<CalendarItem currentDate={currentDate} /> // CalendterItem ์ปดํฌ๋ํธ์ ํ์ฌ ๋ณด์ฌ์ง๋ date๋ฅผ (default๋ ์ฌ์ฉ์์ ๋ก์ปฌ ์ธํ
๋ ์ฐ์์ผ) props๋ก ์ ๋ฌํด์ ์ปดํฌ๋ํธ ๋ด์์ ๋ฌ๋ ฅ ์ ํ์ด ๋ ์ ์๋๋ก ํจ
</div>
</div>
</div>
</>
)
}
export default CalendarList
WeekNames๋ ์์ผ ํ์ ๋ถ๋ฆฌ์์ผ๋๊ฑฐ์ >์ผ์ํ์๋ชฉ๊ธํ <
๊ทธ๋ฆฌ๊ณ CalendarItem์ currentDate๋ฅผ props๋ก ์ ๋ฌํด์ ์ ๋ ์ง๋ฅผ due_date๋ก task๋ฅผ ์์ฑํ ์ ์๋๋ก ํ๋ค.
default ๊ฐ์ ์ฌ์ฉ์ ๋ก์ปฌ ๊ธฐ์ค ๋ ์ง๋ก ์ค์ ํด๋์๋ค! prevMonth ๋ฒํผ๊ณผ nextMonth ๋ฒํผ์ผ๋ก ๋ ์ง๊ฐ ์ด๋๋์์ ๋ ํด๋น ๊ฐ์ผ๋ก currentDate๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ด ๊ฐ์ props๋ก CalendarItem์ ์ ๋ฌ๋๋ค.
interface CalendarItemProps {
currentDate: Date
}
const CalendarItem: React.FC<CalendarItemProps> = ({ currentDate }) => {
const viewTask = useViewTaskModalState()
// const editTask = useEditTaskModalState()
// const deleteTask = useTaskStore(state => state.deleteTask)
const { changeModalState } = useModalActions()
const fetchTaskSelected = useTaskStore(state => state.fetchTaskSelected)
const fetchTaskMonth = useTaskStore(state => state.fetchTaskMonth)
const tasks = useTaskStore(state => state.tasks)
const [selectedTask, setSelectedTask] = useState<Task | null>(null)
const dateData = takeMonth(currentDate)()
const [selectedDate, setSelectedDate] = useState(currentDate)
const selectDateItem = useCallback(
(date: Date) => {
setSelectedDate(date)
if (!viewTask) {
changeModalState('view', true)
}
},
[viewTask, changeModalState, fetchTaskSelected],
)
useEffect(() => {
fetchTaskMonth(currentDate)
}, [currentDate, fetchTaskMonth])
useEffect(() => {
if (viewTask || editTask) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
return () => {
document.body.style.overflow = ''
}
}, [viewTask, editTask])
const getTaskCountForDate = (date: Date) => {
return tasks.filter(task => {
const taskDate = new Date(task.due_date)
return (
taskDate.getDate() === date.getDate() &&
taskDate.getMonth() === date.getMonth() &&
taskDate.getFullYear() === date.getFullYear()
)
}).length
}
return (
<div>
{dateData.map((week, index) => (
<div key={index}>
{week.map(day => {
const taskCount = getTaskCountForDate(day)
const isCurrentMonth = isSameMonth(day, currentDate)
return (
<div
onClick={() => selectDateItem(day)}
key={format(day, 'yyyy-MM-dd')}>
<div>
<button>{format(day, 'd')}</button>
{taskCount > 0 && (
<div>
{taskCount}
</div>
)}
</div>
</div>
)
})}
</div>
))}
<Modal>
{viewTask && selectedTask ? (
<div>
{editTask ? ( // edit ์์ง์๋จ
<TaskForm
initialTitle={selectedTask.todo_title}
initialDetail={selectedTask.todo_detail}
taskId={selectedTask.todo_id}
dueDate={selectedTask.due_date}
/>
) : (
<div>
<div>
<div>{selectedTask.todo_title}</div>
</div>
<div>
<div>{selectedTask.todo_detail}</div>
</div>
<div>
<button
onClick={() => deleteTaskBtn(selectedTask.todo_id)}>
<Image
src={RemoveIcon}
alt="delete button" />
</button>
<button
onClick={editTaskBtn}>
<Image src={EditIcon} alt="edit button" />
</button>
</div>
</div>
)}
</div>
) : (
<TaskForm dueDate={selectedDate} />
)}
</Modal>
</div>
)
}
export default CalendarItem
์๋์๋ํ๋ฉด ์บ๋ฆฐ๋ ํ์ด์ง์์ ํ์คํฌ ๋ฑ๋ก์ด ๊ฐ๋ฅํ๊ณ , ์ผ์๋ณ ํ์คํฌ ๊ฐฏ์๋ฅผ ํ์ธํ ์ ์๋ค.
์ง๊ธ ํ๋ฉด์ ๋์ถฉ ์ด๋ฐ ๋๋..
์ผ์๋ณ ํ์คํฌ ๊ฐฏ์ ๋์ฐ๋๊ฑฐ ในใ ์ด๋ ค์ ๋ค ใ ๋จธ๋ฆฟ์์๋ ์ด๋ค ์์ผ๋ก ํํฐ๋งํ๋ฉด ๋๊ฒ ๋ค๊ณ ์๊ฐ์ ๋๋๋ฐ ์ ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ด ์๊ฐ์ด ์๋์ docs์ ์ด๊ฑฐ ํํฐ๋ง ํ๋ ๋ฐฉ๋ฒ ์๋ ๋ค์ง๊ณ ......... ๋ฉฐ์น ์ ๋๊ณ ์๋ค๊ฐ ๊ตฌ๊ธ๋งํด์ ํํฐ๋งํจ ๊ทผ๋ฐ ์ ๊ฑฐ ์ซ์ ๋ง๊ณ ๊ฐ์ฑ ๋ง์ผํ ์ผ๋ก๊ทธ๋ฅ ์ ์ผ๋ก ํ๊ธฐํ ๊น๋ ์๊ฐ์ค์ด๋ค ์ซ์?...์ง๊ด์ ์ด์ผ
ํ์ด์ง๊ฐ ์์ง ์์ฑ๋ ๋ถ๋ถ์ด ์๋๋ผ์ ์ดํ์ ๊ณ์ ๋ณํ ๊ฑฐ ๊ฐ์๋ฐ ์ด์จ๋ ์ด๋ฐ ์์ผ๋ก ์์ฑํ๋ฉด ๋๋ ๊ตฌ๋~๋ฅผ ๋ฐฐ์ ๋ค.
@date-fns๋ ์ด๋ค ๋ ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํ ๊น ์์นํ๋ค๊ฐ ๋ค๋ฅธ ์ ๋ช ํ ๋ ์์ ๋์ด์ ์ ๋ฐ์ดํธ ์ํ๋ค๊ณ ํ๊ธธ๋ ์ด ๋ ์์ผ๋ก ๊ณจ๋๋๋ฐ, ์ฌ์ฉํ๊ธฐ ํธํ๋ค! ์ข์ ๊ฒฝํ์ผ๋ก ๋จ์์ ๋ค์์๋ ๊ธฐํ๊ฐ ์๋ค๋ฉด ์ฌ์ฉํ ๋ฏ..
์ฌ๊ธฐ์ ๋ ํด์ผ ํ๋ ๊ฑฐ๋ ์ผ์ ํด๋ฆญํ๋ฉด ๋ชจ๋ฌ์ด ์ด๋ฆฌ๋๋ฐ
- ๋ฑ๋ก๋ ํ์คํฌ๊ฐ ์์ผ๋ฉด create modal >
- ๋ฑ๋ก๋ ํ์คํฌ๊ฐ ์์ผ๋ฉด view modal >
๋ฑ๋ก๋์ด ์๋ ํ์คํฌ๋ค์ (๋ชจ๋ฌ ๋ด์์) ์ ๋ชฉ+๋ด์ฉ์ผ๋ก ์ฝ์ ์ ์๊ณ , ์ค๋ฅธ์ชฝ ์ฌ์ด๋์ ์ ์ธ๊ฐ ์์ด์ฝ ๋๋ฅด๋ฉด edit, ํ๋จ์ select delete ๋ฒํผ ์ถ๊ฐํด์ ์ ํ ์ญ์ ๊ธฐ๋ฅ
์๋ ๊ฒ ํ๋ฉด ์ํ๋ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ๋๋๋ค. ๋ชจ๋ฌ ์๊น์ ์ ๋ ์ข ํ๊ณ ํ๊ทธ ๊ธฐ๋ฅ ์ถ๊ฐํ๋ฉด ๋ด๊ฐ ์ํ๋ ๋ ์๋ค์ ๋ค ๋ฃ๋ ๊ฒ... ํต๊ณ ์