[React] 자바스크립트로 달력을 구현해보자 🗓

SeonDal·2022년 11월 26일
5

🕊️ 기록기룩 

목록 보기
5/7
post-thumbnail

3일동안 열심히 구현한 달력 ...
자세한 과정은 나중에 언젠가 쓸 예정.....
일단 코드만 냅다 투척.....! 하 하 !

import moment from "moment";
import Link from "next/link";
import React, { useState } from "react";

interface DateData {
  full: string;
  date: string;
}

export default function Calendar() {
  const [selected, setSelected] = useState(moment());

  function moveNextMonth() {
    setSelected(selected.clone().add(1, "month"));
  }
  function movePrevMonth() {
    setSelected(selected.clone().subtract(1, "month"));
  }

  return (
    <div className="container">
      <div className="header">
        <button onClick={movePrevMonth}>이전달</button>
        <span>{selected.format("MM월")}</span>
        <button onClick={moveNextMonth}>다음달</button>
        <MonthCalendar selected={selected} />
      </div>
    </div>
  );
}

export const MonthCalendar = ({ selected }: { selected: moment.Moment }) => {
  let date = selected.clone().startOf("month");

  let dates: DateData[] = [];

  // 1일 이전의 날짜들 처리
  for (let i = date.day(); i > 0; i--) {
    dates.unshift({ full: `${i}`, date: `none` });
  }

  // 날짜 데이터 넣기
  for (; date < selected.clone().endOf("month"); date.add(1, "day")) {
    const data: DateData = {
      full: date.format("YYYY-MM-DD"), // href 쿼리에 전달할 연도-월-날짜
      date: date.format("DD"), // 화면에 표시되는 날짜
    };
    dates = dates.concat(data);
  }

  // [주][날짜] 형태의 이중배열로 저장
  let calendar: DateData[][] = Array.from(Array(6), () => new Array(7));
  dates.forEach((date, index) => {
    calendar[Math.floor(index / 7)][index % 7] = date;
  });

  return (
    <div>
      <table border={1}>
        <thead>
          <tr>
            <th>Sun</th>
            <th>Mon</th>
            <th>Tue</th>
            <th>Wed</th>
            <th>Thu</th>
            <th>Fri</th>
            <th>Sat</th>
          </tr>
        </thead>
        <tbody>
          {calendar.map((week, index) => (
            <tr key={index}>
              {week.map((day) => (
                <td key={day.full}>
                  {day.date !== "none" ? (
                    <Link href={`/main?date=${day.full}`}>
                      <span>{day.date}</span>
                    </Link>
                  ) : (
                    <></>
                  )}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

참고1 : 본 달력을 구현하기 위해 moment 를 설치하였습니다.

참고2 : 본 달력을 사용하는 프로젝트는 리액트 기반 NextJS와 타입스크립트를 사용하고있습니다.
...만 달력 자체의 기능은 위 코드만을 살짝 변형하여 바닐라 자바스크립트만을 이용해도 구현 가능합니다 !

  • 날짜를 눌렀을 때 페이지 이동하는 기능(프로젝트에서 필요)을 위해 Link를 사용한 점을 제외하면 next 의 기능은 사용하지 않음.
  • 추후 주간 캘린더 기능 구현을 위해 미리 월간캘린더(MonthCalendar) 부분의 컴포넌트를 분리하여 props를 전달하고 useState로 상태를 관리하였는데, 해당 부분을 제외하면 React 없이도 구현 가능

구현 과정 소중하게 담아놓은 풀리퀘와 커밋내역
https://github.com/seondal/Sobok-WebApp/pull/1

  • refact : react-moment 설치 및 적용
  • feat : 달력 날짜 누르면 해당 날짜에 해당하는 페이지로 이동
  • fix : key props
  • feat : <table />
  • feat : 달력에서 1일 이전의 날짜들은 none 처리
  • feat : 날짜 데이터 calendar 이중배열에 저장
  • feat : 달력 표현 !!!!!
  • edit : 가독성 좋게 코드 위치 변경
  • fix : <table> 구조 정확하게 알고 쓰자
    - Warning: validateDOMNesting(...): <th> cannot appear as a child of <table>
  • fix : map 에서 key prop 전달
    - Warning: Each child in a list should have a unique "key" prop
profile
김선달 개발블로그

0개의 댓글