MapleStory Finger Point

๐Ÿ”˜ ํ”„๋กœ์ ํŠธ

[Project] React Calendar ํ™œ์šฉํ•˜๊ธฐ (Date, Time, disabled)

HYEJU01 2025. 2. 7. 21:57

 

 

 

 

 

 

์œ„์™€ ๊ฐ™์ด ๋ฆฌ์•กํŠธ ์บ˜๋ฆฐ๋”๋ฅผ ์ด์šฉํ•ด์„œ ๋‚ ์งœ๋ฅผ ๋ฐ›์•„์˜ค๊ณ  ์‹œ๊ฐ„์„ ๋ฐ›์•„ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

ํŠน์ • ๋‚ ์งœ๋Š” ๋น„ํ™œ์„ฑํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ๋„ ์žˆ๋‹ค.

(์šฐ๋ฆฌ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์˜ˆ์•ฝ์ด ๊ฝ‰์ฐจ๊ฑฐ๋‚˜, ํœด๋ฌด์ผ์ด๊ฑฐ๋‚˜ , ์ง€๊ธˆ์œผ๋กœ๋ถ€ํ„ฐ 3๊ฐœ์›” ๋‚ด ์ด์™ธ์˜ ๊ฐ’์€ disabled ๋œ๋‹ค.)

 

 

 

 


1) ๋ฆฌ์•กํŠธ ์บ˜๋ฆฐ๋” ์„ค์น˜ (install)

 

npm install react-calendar

 

 


 

2) ๋ฆฌ์•กํŠธ ์บ˜๋ฆฐ๋” ์‚ฌ์šฉ (import)

 

import Calendar from 'react-calendar';

 

import "./Calendar.css";

 

 

react-calendar ์™€ css import ์‹œ์ผœ์ฃผ๊ธฐ

 

 

 


 

 

3) ๋‚ ์งœ ๊ฐ€์ ธ์˜ค๊ธฐ (Date seleted)

 

<Calendar
onChange={handleDateChange}
value={date}
onClickDay={handleDateClick}
locale="en-US" // ๋ฏธ๊ตญ ๋กœ์ผ€์ผ ์„ค์ • (์ผ์š”์ผ๋ถ€ํ„ฐ ์‹œ์ž‘)
next2Label={null}
prev2Label={null}
tileDisabled={tileDisabled}
tileClassName={getTileClass}
formatDay={(locale, date) => moment(date).format('D')}
formatShortWeekday={(locale, date) =>
['์ผ', '์›”', 'ํ™”', '์ˆ˜', '๋ชฉ', '๊ธˆ', 'ํ† '][date.getDay()]
}
formatMonthYear={(locale, date) =>
`${moment(date).format('YYYY๋…„ MMM')}`
}
/>

 

Calendar ์ปดํฌ๋„ŒํŠธ์— ์œ„์™€ ๊ฐ™์ด ๊ธฐ๋ณธ Props ์†์„ฑ๋“ค์„ ์ฃผ๋ฉด ๋˜๋Š”๋ฐ

 

  • value ์„ ํƒ๋œ ๋‚ ์งœ ๋˜๋Š” ๋‚ ์งœ ๋ฐฐ์—ด์„ ์„ค์ •
  • onChange ๋‚ ์งœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  • onClickDay ํŠน์ • ๋‚ ์งœ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  • locale ๋กœ์ผ€์ผ ์„ค์ • (์˜ˆ: "ko-KR" ํ•˜๋ฉด ํ•œ๊ธ€๋กœ ํ‘œ์‹œ๋จ)
  locale="en-US" // ๋ฏธ๊ตญ ๋กœ์ผ€์ผ ์„ค์ • (์ผ์š”์ผ๋ถ€ํ„ฐ ์‹œ์ž‘)

 

  • next2Label ๋‹ค์Œ 2๋…„ ์ด๋™ ๋ฒ„ํŠผ (๊ธฐ๋ณธ๊ฐ’: ยป, null๋กœ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Œ)
  • prev2Label ์ด์ „ 2๋…„ ์ด๋™ ๋ฒ„ํŠผ (๊ธฐ๋ณธ๊ฐ’: ยซ, null๋กœ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Œ)

 

  • formatDay , formatShortWeekday , formatMonthYear, formatYear ๋“ฑ์œผ๋กœ ์ปค์Šคํ…€ ํฌ๋งท์œผ๋กœ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
formatDay={(locale, date) => moment(date).format('D')}
// date ๊ฐ์ฒด๋ฅผ moment.js ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ”ใ…“์„œ 1,2,3 (01,02 x ) ํ˜•ํƒœ๋กœ ํ‘œ๊ธฐ
formatShortWeekday={(locale, date) =>
['์ผ', '์›”', 'ํ™”', '์ˆ˜', '๋ชฉ', '๊ธˆ', 'ํ† '][date.getDay()]
} // ์š”์ผ์„ ํ•œ๊ตญ์–ด๋กœ ํ‘œ์‹œ
formatMonthYear={(locale, date) =>
`${moment(date).format('YYYY๋…„ MMM')}`
} // ์›”์„ ํ•œ๊ตญ์–ด๋กœ ํ‘œ์‹œ

 

  • tileDisabled, tileClassName, tileContent ์œผ๋กœ ํŠน์ • ๋‚ ์งœ๋ฅผ ์Šคํƒ€์ผ๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค. 
tileDisabled={tileDisabled}
tileClassName={getTileClass} // ๋‚ ์งœ ์Šคํƒ€์ผ๋ง

 

 

์ด์™ธ์—๋„

  • onClickMonth ํŠน์ • ์›”์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  • onClickYear ํŠน์ • ์—ฐ๋„๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  • onActiveStartDateChange ํ˜„์žฌ ๋ณด์ด๋Š” ๋‚ ์งœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜ 
  • minDate ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์†Œ ๋‚ ์งœ
  • maxDate ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ๋‚ ์งœ
  • view ๋ชจ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. month, year, decade, century
  • selectRange ๋‹ค์ค‘ ๋‚ ์งœ ์„ ํƒ๋„ ๊ฐ€๋Šฅ  selectRange={true}

 

 

 

 


 

4) ๋‚ ์งœ ๋ง‰๊ธฐ (Date disabled)

 

๋‚ ์งœ๋ฅผ ๋ง‰๋Š” ๋ฐฉ๋ฒ•์€ ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ

 

 

 

1. ํ•˜๋‚˜๋Š” ์˜ค๋Š˜๋กœ๋ถ€ํ„ฐ 3๊ฐœ์›” ๋’ค์˜ ๋‚ ์งœ๋งŒ ๋ณด์—ฌ์ฃผ๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹ โ†’  tileDisabled ์†์„ฑ ์‚ฌ์šฉ

// ๋‚ ์งœ ๋น„ํ™œ์„ฑํ™”
const tileDisabled = ({ date }) => {
const today = new Date();
const threeMonthsLater = new Date();
threeMonthsLater.setMonth(today.getMonth() + 3); // ์ง€๊ธˆ๋ถ€ํ„ฐ 3๊ฐœ์›” ๋’ค
// ์˜ค๋Š˜๊ณผ 3๊ฐœ์›” ํ›„์˜ ๋‚ ์งœ ์‚ฌ์ด์˜ ๋‚ ์งœ๋Š” ๋น„ํ™œ์„ฑํ™” ํ•˜์ง€ ์•Š์Œ
return date < today || date > threeMonthsLater;
};

 

 

 

 

2. ์˜ˆ์•ฝ์ด ๊ฝ‰์ฐฌ ๊ฒฝ์šฐ disabled ์‹œํ‚ค๋Š” ๊ฒฝ์šฐ โ†’  tileClassName ์†์„ฑ ์‚ฌ์šฉ

 

 

tileClassName={getTileClass}

 

className ์„ ์ถ”๊ฐ€ํ•ด์„œ css ๋จน์ด๊ฒŒ ํ•ด์„œ ์˜ˆ์•ฝ์ด ๋ง‰ํžŒ ๊ฒƒ ์ฒ˜๋Ÿผ ํ‘œํ˜„ํ•จ.

 

const getTileClass = ({ date }) => {
const formattedDate = date.toLocaleDateString('en-CA'); // 'yyyy-mm-dd' ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ (๋กœ์ปฌ ์‹œ๊ฐ„ ๊ธฐ์ค€)
// (... ๊ณ ์ •ํœด๋ฌด์— ๋Œ€ํ•œ ์ฝ”๋“œ)
// ์˜ˆ์•ฝ ์ •๋ณด ํ™•์ธ
const slot = dateTime2.find((slot) => slot.reservationSlotDate === formattedDate); // ํ•ด๋‹น ๋‚ ์งœ์˜ ์˜ˆ์•ฝ ์ •๋ณด ์ฐพ๊ธฐ
if (slot) {
if (slot.slotStatusCount === slot.slotCount) {
return 'reserved'; // ์˜ˆ์•ฝ์ด ๋ชจ๋‘ ์ฐผ๋‹ค๋ฉด 'reserved'
}
}
return ''; // ์˜ˆ์•ฝ์ด ์ฐผ์ง€ ์•Š๊ฑฐ๋‚˜ slot์ด ์—†์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด ๋ฐ˜ํ™˜
};

 

 

 

 

 



5) ์‹œ๊ฐ„ ๊ฐ€์ ธ์˜ค๊ธฐ (Time Selected)

 

 

1. ์˜ˆ์•ฝ ๊ฐ€๋Šฅ  (์‹œ๊ฐ„ ์ถœ๋ ฅ)

 

{dateTime.map((slot) => (
!noSlotsMessage ? (
<div key={slot.reservationSlotKey}>
<div className="user-reserve-date-time">
{timeSlots.map((timeSlot, index) => (
<button
key={index}
type="button"
onClick={() => {
handleSlotClick(index); // ์Šฌ๋กฏ ์„ ํƒ ๊ด€๋ฆฌ
handleSlotClick2(timeSlot.time); // ์Šฌ๋กฏ ์‹œ๊ฐ„ ์ถœ๋ ฅ
handleSlotClick3(slot.reservationSlotKey);
}}
// ์•„๋ž˜ ์‹œ๊ฐ„ ๊ด€๋ จ ์†์„ฑ์€ ์ด์ œ ์•ˆ์”€.. (์‹œ๊ฐ„ ๊ด€๋ฆฌ๊นŒ์ง„ ์—†์•ฐ)
//disabled={disabledTimes.includes(timeSlot.time)} // ๋น„ํ™œ์„ฑํ™” ์กฐ๊ฑด ์ถ”๊ฐ€
//style={{
//backgroundColor: selectedSlot === index ? '#fd8517' : 'transparent',
//color: selectedSlot === index ? 'white' : 'black',
//</div></div> opacity: disabledTimes.includes(timeSlot.time) ? 0.5 : 1, // ๋น„ํ™œ์„ฑํ™”๋œ ์Šฌ๋กฏ์˜ ํˆฌ๋ช…๋„ ์กฐ์ ˆ
//}}
>
{timeSlot.time} {/* ์Šฌ๋กฏ ์‹œ๊ฐ„ ํ‘œ์‹œ */}
</button>
))}
</div>
</div>
) : (
<div className='msg1'>
์˜ˆ์•ฝ ๊ฐ€๋Šฅํ•œ ์‹œ๊ฐ„์ด ์—†์Šต๋‹ˆ๋‹ค. <br /> ๋‹ค๋ฅธ ๋‚ ์งœ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.
</div>
)
))}

 

 

ํƒ€์ž„๋‹น ์˜ˆ์•ฝ์ด ์ด๋ฏธ ๊ฑธ๋ ค์žˆ๋Š” ๊ฒฝ์šฐ ์˜ˆ์•ฝ์ด ๋ถˆ๊ฐ€ํ•˜๋„๋ก (1ํƒ€์ž„ = 1์˜ˆ์•ฝ) ํ•˜๋ ค๊ณ  ํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ํŠน์ • ํƒ€์ž„์— ์—ฌ๋Ÿฌ ์˜ˆ์•ฝ์„ ๋ฐ›๊ฒŒํ•˜๊ณ  ์˜ˆ์•ฝ์ด ๋ชฐ๋ฆฌ๋Š” ๊ฒฝ์šฐ์—๋Š” ๊ฐ€๊ฒŒ์‚ฌ์žฅ๋‹˜์ด ์ฃผ๋ฌธ์ทจ์†Œ๋กœ ์ž˜ ๋Œ€์‘ํ•˜๋Š” ์ชฝ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ฒŒ๋˜์—ˆ๋‹ค.

(์‹œ๊ฐ„์„ ๋ง‰๋Š” ๊ธฐ๋Šฅ์€ ๋บ์ง€๋งŒ ์•„๋ฌดํŠผ ๊ตฌํ˜„ ํ•ด๋ณธ ๊ฒฐ๊ณผ ์‹œ๊ฐ„์„ ๋ง‰์„ ์ˆ˜๋„ ์žˆ์Œ)

 


 

 

 

2. ์˜ˆ์•ฝ ๋ถˆ๊ฐ€ (์˜ˆ์•ฝ ๋ถˆ๊ฐ€ ๋ฉ”์„ธ์ง€ ๋„์›€)

 

 onClickDay={handleDateClick}
const handleDateClick = (selectedDate) => {
setDate(selectedDate);
const isReserved = getTileClass({ date: selectedDate }) === 'reserved';
setNoSlotsMessage(isReserved); // 'reserved' ๋‚ ์งœ๋ฅผ ํด๋ฆญํ•˜๋ฉด ์˜ˆ์•ฝ ๋ถˆ๊ฐ€ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
if (isReserved) {
console.log('์˜ˆ์•ฝ๋ถˆ๊ฐ€');
}
};

 

์บ˜๋ฆฐ๋” ์ปดํฌ๋„ŒํŠธ์— Day Click ์ด๋ฒคํŠธ์— ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๊ฑธ์–ด์ค€๋‹ค.