[실전 프로젝트] 달력 + 해당 월의 일수 계산하기 (+ fill : 특정 값으로 배열 채우기)
본문 바로가기
항해 중/8-13주차 실전 프로젝트

[실전 프로젝트] 달력 + 해당 월의 일수 계산하기 (+ fill : 특정 값으로 배열 채우기)

by 은돌1113 2021. 12. 28.

1. 달력을 만들기 위해서 moment 패키지를 사용했다.

yarn add moment
or
npm install moment --save

 

2. 버튼을 누르면 해당 년도와 월이 나올 수 있도록 코드 구현

더보기
더보기
// Diary.js

import React from "react";
import moment from "moment";

const Diary = () => {
  const [getMoment, setMoment] = React.useState(moment());
  const [monthDay, setMonthDay] = React.useState(0);
  const today = getMoment; // today === moment();

  React.useEffect(() => {
    const day = new Date(today);
    const days = new Date(day.getFullYear(), day.getMonth()+1, 0).getDate();
    setMonthDay(days);

    console.log(day.getFullYear())
    console.log(day.getMonth()+1);
    console.log(days)
  }, [today]);

  return (
    <>
      <div className="App">
        <div className="control">
          <button
            onClick={() => {
              setMoment(getMoment.clone().subtract(1, "month"));
            }}
          >
            이전달
          </button>
          <span>{today.format("YYYY 년 MM 월")}</span>
          {/* YYYY는 년도 MM 은 달입니다. */}
          <button
            onClick={() => {
              setMoment(getMoment.clone().add(1, "month"));
            }}
          >
            다음달
          </button>
        </div>
        <table>
          <tbody>{monthDay}</tbody>
        </table>
      </div>
    </>
  );
};

export default Diary;

https://yeolceo.tistory.com/69

 

쉽게 배우는 React 로 달력 만들기 with Moment.js -YEOL

이번엔 react를 사용한 달력 알고리즘을 만드려고 합니다. 완성형이 예쁘다기보단 구조를 보여드리기 위함을 참고해주세요!! -Moment.js 란 javaScript에서 날짜 데이터 조작을 하기 쉽게 도와주는 라

yeolceo.tistory.com

 

3. 해당 월의 일자를 구하는 기능 구현 중 0월이 나옴 → getMonth()+1 해줌.

더보기

 

4. 팀장님과 논의 해본 결과 이번달에는 오늘 날짜까지만 아이콘을 띄우고, 저번달까지의 기록은 해당 월의 일수만큼 아이콘을 띄우고, 다음달 기록은 띄우지 않아야 해서 if문을 많이 사용해서... 코드를 구현했다. 복잡하긴 하지만 최대한 이해하기 쉽도록 코드를 구현했다. (아닐지도...)

더보기
더보기
  const [getMoment, setMoment] = React.useState(moment());
  const [monthDay, setMonthDay] = React.useState(0);
  const month = getMoment; // month === moment();
  const arr = new Array(monthDay).fill(""); // 한꺼번에 배열 채우기

  React.useEffect(() => {
    const today = new Date(moment()); // 오늘 날짜
    const day = new Date(month); // 사용자가 선택한 날짜

    if (
      today.getFullYear() + today.getMonth() ===
      day.getFullYear() + day.getMonth()
    ) {
      // 오늘
      const days = new Date(today).getDate();
      setMonthDay(days);
    } else if (today.getFullYear() < day.getFullYear()) {
      // 다음년도
      setMonthDay(0);
    } else if (today.getFullYear() > day.getFullYear()) {
      // 전년도
      const days = new Date(day.getFullYear(), day.getMonth() + 1, 0).getDate(); // 사용한 선택한 날짜의 일수
      setMonthDay(days);
    } else {
      // 이번년도
      if (day.getMonth > today.getMonth()) {
        // 다음달
        setMonthDay(0);
      } else {
        // 저번달
        const days = new Date(
          day.getFullYear(),
          day.getMonth() + 1,
          0
        ).getDate(); // 사용한 선택한 날짜의 일수
        setMonthDay(days);
      }
    }
  }, [month]);

https://april.gitbook.io/learning-js/chapter-8./8.2/8.2.6

 

8.2.6 특정 값으로 배열 채우기 - Learning JS

const arr = new Array(5).fill(1); // [1, 1, 1, 1, 1] arr.fill("a"); // ["a", "a", "a", "a", "a"] arr.fill("b", 1); // ["a", "b", "b", "b", "b"] arr.fill("c", 2, 4); // ["a", "b", "c", "c", "b"] arr.fill(5.5, -4); // ["a", 5.5, 5.5, 5.5, 5.5] arr.fill(0, -3

april.gitbook.io

https://developer.mozilla.org/ko/docs/Web/CSS/flex-wrap

 

flex-wrap - CSS: Cascading Style Sheets | MDN

CSS flex-wrap property는 flex-item 요소들이 강제로 한줄에 배치되게 할 것인지, 또는 가능한 영역 내에서 벗어나지 않고 여러행으로 나누어 표현 할 것인지 결정하는 속성입니다. 만약 영역 내에서 벗

developer.mozilla.org

 

5. 서버에서 다이어리 관련 데이터를 받아 온다는 가정 하에 사용자가 평점을 남기지 않았을 경우 default 값을 띄워 주도록 처리 해야 함.

 

1) 서버에서 데이터를 가져 왔다는 가정 하에 일자에 맞춰 arr 배열에 데이터를 넣어줬다.

    // index => 0, diaryList => day랑 서로 일치를 해야 함.
    // 1) 해당 월의 일수 길이만큼의 배열에 배열 연산자 forEach를 돌린다.
    // 2) 서버에서 가져온 diaryList 배열의 길이만큼 forEach를 돌린다.
    // ※ 이중 반복문을 돌리는 것과 같다.
    // 3) diaryIndex에 있는 객체 데이터를 arr의 index에 넣는다. (이때, index는 0부터 시작하기 때문에 +1을 해줘서 일(day)와 맞춘다.)
    arr.forEach((arrItem, arrIndex) => {
      diaryList.forEach((diaryItem, diaryIndex) => {
        if (arrIndex + 1 === parseInt(diaryList[diaryIndex]?.day)) {
          arr[arrIndex] = diaryList[diaryIndex];
        }
      });
    });

 

2) 일(day) index 안에 데이터가 ""일 경우 default 이미지를 띄운다.
: 이 부분에서 시행착오가 많았다. map을 돌렸을 때 arr의 요소들이 undefined가 떴었다. 이유는 밝히기 못했지만.. arr를useState()에 넣었더니 해결 되었다.

더보기
더보기
import React from "react";
import moment from "moment";
import { useSelector } from "react-redux";
import { history } from "../redux/configureStore";
import Charater from "../elements/Charater";

const Diary = () => {
  const [getMoment, setMoment] = React.useState(moment());
  const [monthDay, setMonthDay] = React.useState(0);
  const arr = new Array(monthDay).fill(1); // 한꺼번에 배열 채우기
  const diaryList = useSelector((state) => state.diary.diaryList);
  const [test, setTest] = React.useState(arr);

  React.useEffect(() => {
    const today = new Date(moment()); // 오늘 날짜
    const day = new Date(getMoment); // 사용자가 선택한 날짜
    
    if (
      today.getFullYear() + "_" + today.getMonth() ===
      day.getFullYear() + "_" + day.getMonth()
    ) {
      // 오늘
      const days = new Date(today).getDate();
      setMonthDay(days);
    } else if (today.getFullYear() < day.getFullYear()) {
      // 다음년도
      setMonthDay(0);
    } else if (today.getFullYear() > day.getFullYear()) {
      // 전년도
      const days = new Date(day.getFullYear(), day.getMonth(), 0).getDate(); // 사용한 선택한 날짜의 일수
      setMonthDay(days);
    } else {
      // 이번년도
      if (day.getMonth > today.getMonth()) {
        // 다음달
        setMonthDay(0);
      } else {
        // 저번달
        const days = new Date(day.getFullYear(), day.getMonth(), 0).getDate(); // 사용한 선택한 날짜의 일수
        setMonthDay(days);
      }
    }

    // index => 0, diaryList => day랑 서로 일치를 해야 함.
    // 1) 해당 월의 일수 길이만큼의 배열에 배열 연산자 forEach를 돌린다.
    // 2) 서버에서 가져온 diaryList 배열의 길이만큼 forEach를 돌린다.
    // ※ 이중 반복문을 돌리는 것과 같다.
    // 3) diaryIndex에 있는 객체 데이터를 arr의 index에 넣는다. (이때, index는 0부터 시작하기 때문에 +1을 해줘서 일(day)와 맞춘다.)
    arr.forEach((arrItem, arrIndex) => {
      diaryList.forEach((diaryItem, diaryIndex) => {
        if (arrIndex + 1 === parseInt(diaryList[diaryIndex].day)) {
          arr[arrIndex] = diaryList[diaryIndex];
        }
      });
    });

    setTest(arr);
  }, [getMoment, monthDay]);

  const diaryDetail = (index) => {
    const day = new Date(getMoment);
    console.log(day.getMonth() + 1 + "월", index + "일");
  };

  return (
    <>
      <div>
        <div>
          <button
            onClick={() => {
              setMoment(getMoment.clone().subtract(1, "month"));
            }}
          >
            이전달
          </button>
          &nbsp;
          <span>{getMoment.format("YYYY 년 MM 월")}</span>
          {/* YYYY는 년도 MM 은 달입니다. */}
          &nbsp;
          <button
            onClick={() => {
              setMoment(getMoment.clone().add(1, "month"));
            }}
          >
            다음달
          </button>
        </div>
        <p>{monthDay}</p>
        <div
          style={{
            backgroundColor: "#dddddd",
            width: "50%",
            height: "80vh",
            margin: "auto",
            display: "flex",
            flexWrap: "wrap",
            padding: "10px",
          }}
        >
          {test.map((item, index) => {
            return (
              <div key={index + 1 + "days"}>
                {item.feelScore && item.sleepScore ? (
                  <Charater
                    shape="charater"
                    size="70"
                    feelNumber={item.feelScore}
                    sleepNumber={item.sleepScore}
                    _onClick={() => {
                      diaryDetail(index + 1);
                    }}
                    margin="10px"
                  />
                ) : (
                  <Charater
                    shape="charater"
                    size="70"
                    feelNumber={0}
                    sleepNumber={0}
                    _onClick={() => {
                      diaryDetail(index + 1);
                    }}
                    margin="10px"
                  />
                )}
                <div>{index + 1}</div>
              </div>
            );
          })}
        </div>
      </div>
      <button
        onClick={() => {
          history.push(`/diaryWrite/4`);
        }}
      >
        다이어리 생성,수정
      </button>
    </>
  );
};

export default Diary;
import zIndex from "@mui/material/styles/zIndex";
import { style } from "@mui/system";
import React from "react";
import styled from "styled-components";

const Charater = (props) => {
  const { shape, size, src, _onClick, margin } = props;

  const styles = {
    size: size,
    margin: margin
  };

  if (shape === "circle") {
    return <ImageCircle {...styles}></ImageCircle>;
  }

  if (shape === "charater") {
    return (
      <React.Fragment>
        <IconBox {...styles}>
          <ImgIcon
            {...styles}
            style={{ zIndex: "3" }}
            src={require(`../images/character/feel${props.feelNumber}.png`)}
            onClick={_onClick}
          />
          <ImgIcon
            {...styles}
            style={{ zIndex: "2" }}
            src={require(`../images/character/sleep${props.sleepNumber}.png`)}
            onClick={_onClick}
          />
        </IconBox>
      </React.Fragment>
    );
  }

  return <React.Fragment></React.Fragment>;
};

//-- defaultProps --
Charater.defaultProps = {
  shape: "circle",
  src: "../images/character/sleep0.png",
  size: 24,
  onClick: () => {},
};

const ImageCircle = styled.div`
  --size: ${(props) => props.size}px;
  width: var(--size);
  height: var(--size);
  border-radius: var(--size);
  background-image: url("${(props) => props.src}");
  background-size: cover;
`;

const IconBox = styled.div`
  --size: ${(props) => props.size}px;
  width: var(--size);
  height: var(--size);
  position: "relative";
  border-radius: var(--size);
  background-color: aliceblue;
  display: flex;
  align-items: center;
  justify-content: center;
  ${props => props.margin ? `margin : ${props.margin}` : `margin: 0 auto;`}
`;

const ImgIcon = styled.img`
  --size: ${(props) => props.size}px;
  width: var(--size);
  position: absolute;
`;

export default Charater;

댓글