상세 컨텐츠

본문 제목

[Next] 대충프로젝트(2) - 캘린더 만들기

프로젝트 정리/대충

by 래모 2023. 2. 4. 13:05

본문

원래는 npm 관련 모듈을 사용하려고 했으나...

css적용하기도 까다롭기도 해서 그냥 내가 만들려고 한다

도전!

 

✳️기본 달력 형태 만들기 

일단 과정은 다음과 같다

1. 오늘이 속하는 달의 첫날은 1년 52주 중 몇번째 주?

2. 오늘이 속하는 달의 마지막 날은 1년 52주 중 몇번째 주?

3. 첫 주부터 마지막 주를 +1 씩 돌아가며 날짜를 7일씩 달력에 뿌린다

 

moment 라이브러리를 사용해줄것이다.

const [date, setDate] = useState(moment());
const today = date.clone().format("YYYY-MM-DD") // 오늘 날짜 미리 저장

 

builddCalendar함수로 달력이 출력될 때 필요한 array들을 관리해준다.

const buildCalendar = () => {
    const dateStartWeek = date.clone().startOf('month').week(); 
    // date가 속하는 달의 첫번쨰 주가 이번년도의 몇번째 주인가

    const dateEndWeek = date.clone().endOf('month').week() === 1 ? 53 : date.clone().endOf('month').week();
     // date가 속하는 달의 마지막 주가 이번년도의 몇번째 주인가
     // 마지막 주가 1이 나오는 경우(12월의 마지막주에서 1월이 과반수일때)엔 53으로 수정해주어야 함 

     let calendar = [];

     for (let week = dateStartWeek; week <= dateEndWeek; week++) {
      calendar.push(
        <div key={week} className="grid grid-cols-7 items-center justify-items-center">
        {[Array(7).fill(0).map( (n,i) => {
          let current = date.clone().week(week).startOf('week').add(i, 'day');
          // 현재 상태의 날짜의 달의 첫 주부터 날짜를 세어준다

          const isMonth = current.format('MM') !== date.format('MM'); // current가 이번달이 아닌가?
          const isToday = current.clone().format(`YYYY-MM-DD`) === today; // cureent가 오늘인가?
          

          return <span key={i} className={cls("text-center", isMonth ? "text-gray-200": "", !isMonth&& isToday ? "w-5 h-5 ring-2 ring-[#C5E5E9] rounded-full" :"")} >{current.format("D")}</span>
        })]}  
        </div>
        
      )
     }
     return calendar
  }
  ...
  <div className="space-y-3" >{buildCalendar()}</div>

 

✳️ 버튼에 따라 다른 달 혹은 연도로 이동하기

이건 어떤 버튼이냐에 따라 setDate를 다시 설정해주면 되는거라 쉽다

...
const handleMonth = (num) => (num ? setDate(date.clone().add(1, 'month')) : setDate(date.clone().subtract(1, 'month')));
const handleYear = (num) =>(num ? setDate(date.clone().add(1, 'year')) : setDate(date.clone().subtract(1, 'year')));
...
<div className="flex justify-center items-center space-x-5 pt-3 pb-5">
  <span onClick={() => handleYear(0)}>
    //"<<"
  </svg>
  </span>
  <span onClick={()=>handleMonth(0)}>
    //"<"
  </span>
  <span className="w-20 text-center font-semibold" >{date.clone().format("MMMM")}</span>
  <span onClick={()=>handleMonth(1)}>
    //">"
  </span>
  <span onClick={() => handleYear(1)}>
    //">>"
  </span>

 

✳️ 연도버튼 누르면 해당 연도로 이동하기

이걸 구현하느라 꽤 애먹었는데 ....ㅜㅜ

정리하다보니까 왜 그렇게 오래 걸렸나 싶다.

 

일단 올해 연도를 별도로 변수로 저장해준다.

그리고 연도가 클릭되었는지 아닌지에 대한 변수도 지정해준다.

const [selectedYear, setSelectedYear] = useState(Number(date.clone().format("YYYY")));
const [yearClicked, setYearClicked] = useState(false);

 

이후 현재 보여지는 연도가 클릭되면 다른 연도들의 리스트를 보여준다.

const buildYear = () => {
    let years = [];


    const onClickSelectedYear = (i) => { 
      setDate(date.clone().year(i))
      setYearClicked(false);
    }// 클릭되면 date를 해당연도로 다시 설정하고 yearClicked를 false로 설정

    for (let i = selectedYear; i < selectedYear + 12 ; i++) {
      years.push(<span className={cls(i==date.clone().format('YYYY') ? "text-textPoint": "")} onClick={() =>onClickSelectedYear(i)}>{i}</span>)
      // i 가 현재 눌러지는 연도와 같다면 text 색상 변경
    }
    
    return years
  }
...
<div className="grid grid-cols-4 gap-4">
    {buildYear()}
</div>

 

연도들은 12개씩 보여줄 것인데 위아래 버튼을 통해 보여지는 숫자들을 조절한다.

const handleSelectedYear = (num) => num ? setSelectedYear(selectedYear + 12): setSelectedYear(selectedYear - 12);
...
<button onClick={()=>handleSelectedYear(0)}>
// ↑ 
</button>
<button onClick={()=>handleSelectedYear(1)}>
// ↓
</button>

 

year이 클릭되면 이 모든것을 보여줄 것이므로 삼항연산자를 이용해준다.

{yearClicked ? 보여줄 것들 : null}

 

추가적으로 해당 부분이 아닌 바깥쪽을 누르면 무조건 사라지게 하고 싶어서 div하나를 더 만들어줬다.

const onClickYear = () => {
    setYearClicked(prev => !prev)
  }
...
{yearClicked ?<div className="fixed top-0 bottom-0 z-10 w-screen h-srceen opacity-0" onClick={onClickYear}></div> : null}

관련글 더보기