라이브러리 의존성 없애기

전해림·2024년 2월 8일
7
post-thumbnail

글을 시작하기 전 썸네일 이거 제가 만들었어요 ~~😏
썸네일 메이커가 궁금하다면 ?

저는 라이브러리를 정말 좋아합니다. 왜냐구요? 개발하기 편하니까요..

하지만 우연히 링크드인에서 백혜인님의 라이브러리에 의존하면 안되는 이유를 보게 되었습니다.
라이브러리는 언제든지 사라질 수 있고 유지보수 적으로도 좋지 않다는 것을 알게되었습니다.
개발하는 그 잠시 편하려고 라이브러리를 쓰면 배보다 배꼽이 커지는 일이 발생할 수 있다는것을요...
하지만 그렇다고 해서 라이브러리가 "나쁘다"가 아닌 잘~ 사용하자 라는 생각으로 글을 쓰게 되었습니다.

이번에 뿌실 라이브러리는 react-calendar입니다.

import Calendar from "react-calendar";
import "react-calendar/dist/Calendar.css";
import moment from "moment";

type ValuePiece = Date | null;

type Value = ValuePiece | [ValuePiece, ValuePiece];

interface CalendarProps {
  setUploadDate: Dispatch<SetStateAction<string>>;
}

const Calendars = ({ setUploadDate }: CalendarProps) => {
  const [value, onChange] = useState<Value>(new Date());

  const handleDateChange = (newValue: Value) => {
    if (newValue instanceof Date) {
      const formattedDate = new Date(
        newValue.getTime() - newValue.getTimezoneOffset() * 60000
      )
        .toISOString()
        .split("T")[0];

      setUploadDate(formattedDate);
      onChange(newValue);
    }
  };

  return (
    <S.CalendarContainer>
      <Calendar
        onChange={handleDateChange}
        value={value}
        className="Calender"
        formatDay={(locale, date) => moment(date).format("DD")}
        minDetail="month"
        maxDetail="month"
        showNeighboringMonth={false}
      />
      <div style={{ display: "flex", justifyContent: "flex-end" }}>
        <button
          style={{
            position: "absolute",
            top: "37.5%",
            width: "50px",
            border: 0,
            zIndex: 999,
          }}
        >
          X
        </button>
      </div>
    </S.CalendarContainer>
  );
};

export default Calendars; 

원래는 react-calendar를 사용하여 이런식으로 달력을 사용했습니다. 하지만 css 커스터마이징에 제약사항이 존재해 개발자가 대충 프로젝트에 갖다 쓰는 용도로는 적합하지만 디자이너가 있는 저희 팀에서는 부적합했습니다.

디자이너님께 달력 디자인을 바꿔달라 할까 고민을 많이 했지만 라이브러리도 뿌실겸 직접 만들어 보게 되었습니다.

아 그런데요 저는 캘린더 라이브러리를 쓰지 않겠다고 했지 날짜 라이브러리를 쓰지 않겠다고는 안했습니다..ㅋ😏

또한 자바스크립트의 Date객체가 있지만 동작을 쉽게 예측할 수 없으니 검증된 라이브러리를 쓰는것이 더 옳다고 생각했습니다!!

// useCalendars.ts
import { useState } from "react";
import { getDaysInMonth } from "date-fns";

const DATE_MONTH_FIXER = 1;
const CALENDER_LENGTH = 35;
const DEFAULT_TRASH_VALUE = 0;
const DAY_OF_WEEK = 7;
const DAY_LIST = ["일", "월", "화", "수", "목", "금", "토"];

const useCalendars = () => {
  const [currentDate, setCurrentDate] = useState(new Date());
  const totalMonthDays = getDaysInMonth(currentDate);

  const prevDayList = Array.from({
    length: Math.max(0, currentDate.getDay()),
  }).map(() => DEFAULT_TRASH_VALUE);
  const currentDayList = Array.from({ length: totalMonthDays }).map(
    (_, i) => i + 1
  );
  const nextDayList = Array.from({
    length: CALENDER_LENGTH - currentDayList.length - prevDayList.length,
  }).map(() => DEFAULT_TRASH_VALUE);

  const currentCalendarList = prevDayList.concat(currentDayList, nextDayList);
  const weekCalendarList = currentCalendarList.reduce(
    (acc: number[][], cur, idx) => {
      const chunkIndex = Math.floor(idx / DAY_OF_WEEK);
      if (!acc[chunkIndex]) {
        acc[chunkIndex] = [];
      }
      acc[chunkIndex].push(cur);
      return acc;
    },
    []
  );

  return {
    weekCalendarList: weekCalendarList,
    currentDate: currentDate,
    setCurrentDate: setCurrentDate,
  };
};

export default useCalendars;

이렇게 커스텀 훅을 만들었습니다. 핵심은 달력의 시작과 끝에 적절한 트래쉬값을 붙여주고
이를 reduce를 이용하여 달력으로 렌더링하기 편한 2중배열 형태로 변환해주는 작업이라고 할 수 있습니다.

useState의 반환값과 현재 위치한 날짜의 달력정보를 담고있는 2차원 배열
weekCalendarList가 반환되고 길이 7인 배열 5개를 튜플로 가지는 2차원 배열이 담겨있습니다.

차례대로 일,월,화 .... 이고 만약 차례대로 날짜가 채워지지 않는다면 트래쉬값 0이 들어가게 됩니다.

저는 월요일이 시작이 아닌 일요일을 시작으로 잡았기 때문에 prevDayList에서 getDay() - 1을 해주지 않고 getDay()만 불러왔습니다. 만약 월요일부터 시작하려면 getDay() - 1을 해주면 날짜가 월요일 부터 시작 할 수 있습니다.

또한 위에 있는 constants는 렌더링 또는 함수 호출 시 다시 계산될 여지가 있어 따로 선언해서 import 해오시기 바랍니다.

import * as S from "./style";
import useCalendars from "hooks/common/Calendars/useCalendars";
import { isToday, subMonths } from "date-fns";
import RightButton from "assets/img/common/R.svg";

const Calendar = () => {
  const { weekCalendarList, currentDate, setCurrentDate, DAY_LIST } =
    useCalendars();

  return (
    <S.CalendarContainer>
      <S.CalendarTitleBox>
        <이전버튼
          onClick={() => {
            setCurrentDate(subMonths(currentDate, 1));
          }}
        >
          이전으로
        </이전버튼>

        <년도>
          {currentDate.toLocaleDateString("ko-KR", {
            year: "numeric",
            month: "long",
          })}
        </년도>

        <다음버튼
          src={RightButton}
          onClick={() => {
            setCurrentDate(subMonths(currentDate, -1));
          }}
        >
          다음으로
        </다음버튼>
      </S.CalendarTitleBox>

      <>
        {DAY_LIST.map((day, index) => (
          <th key={index}>{day}</th>
        ))}
      </>

      <날짜>
        {weekCalendarList.map((week, weekIndex) => (
          <tr key={weekIndex}>
            {week.map((day, dayIndex) => (
              <div
                key={dayIndex}
                className={
                  isToday(
                    new Date(
                      currentDate.getFullYear(),
                      currentDate.getMonth(),
                      day
                    )
                  )
                    ? "today"
                    : ""
                }
              >
                {day > 0 ? day : ""}
              </div>
            ))}
          </tr>
        ))}
      </날짜>
    </S.CalendarContainer>
  );
};

export default Calendar;

여러분이 이해하기 쉽도록 쓰는 곳에서는 이렇게 사용을 해봤습니다. 가져와서 쓰는 코드가 복잡해보이지만 생각보다 간단합니다. date-fns 라이브러리의 isToday를 사용하여 오늘의 날짜에 색을 입혔고, 지금은 딱 레이아웃만 잡은 상태이며 styled-component를 사용해서 디자인을 조금 더 입히면 완성입니다.

마치며

많은 양을 구글링과 함께해 잘짠 코드라고 할 순 없지만 이렇게 또 라이브러리를 떄려봤습니다.
제가 작성한 코드가 여러분에게 큰 영향을 미칠 수 있을때 까지 열심히 하겠습니다 🙇‍♀️

참고 :
https://xionwcfm.tistory.com/420
https://www.linkedin.com/feed/update/urn:li:activity:7160305839780589568/

profile
프론트엔드 개발자 전해림입니다

6개의 댓글

comment-user-thumbnail
2024년 2월 8일

이제 툰게더에 적용만 하면 되겠네요^^

1개의 답글
comment-user-thumbnail
2024년 2월 8일

좋내요 잘읽었습니다!!

1개의 답글
comment-user-thumbnail
2024년 2월 8일

오타 잇어요 떄 -> 때 👍

1개의 답글

관련 채용 정보