본문 바로가기

TIL

캘린더 일정관리 - 일정 시간 중복 체크 유효성 ②

기존 등록된 일정과 추가/수정할 일정의 시간이 겹치지 않도록 로직을 짰는데, 수정모드일 경우 현재 수정중인 데이터는 비교할 데이터에서 제외해야 한다는걸 깜빡했다.

 

생각정리

  • 수정모드와 등록모드에서 비교할 배열을 각각 따로만들기 (수정모드만 만들면됨)
  • Edit의 부모쪽에서 데이터 가공해서 보내주기(calendarData fiter로 !== 해서 내려줘서)
  • 수정 등록 모드별로 다르게 적용하면 안될까? → 근데 이 수정/등록 모드를 어떻게 구별해서 나눌건데? (같은 컴포넌트 사용하는 중)
  • 부모에서 각각 mode=“create” / mode=“edit” 모드를 다르게 줘서 조건문 처리 해보자

 

구현과정

① 수정모드에서 시간 비교할 데이터 배열 만들기

수정과 등록 두 부모컴포넌트 모두 calendarData 를 가지고있고 수정모드에서는 선택된 데이터도 이미 가지고 있으므로 filter 메서드로 선택된 데이터를 제외한 일정 리스트를 만들주었다.

  // 전체일정에서 수정중인 일정 제외한 리스트
  const existTimeWithoutEdit = calendarData?.filter(
    (data) => data.calendar_id !== event.calendar_id,
  );

 

 

② 수정 / 등록 부모컴포넌트에서 props로 모드를 전달

일정등록 폼인 CreateEventForm 에 mode="create" / mode="edit" props 각각 전달

// 일정 등록 부모 컴포넌트
<CreateEventForm
          studyId={studyId}
          eventDate={eventDate}
          deleteForm={closeForm}
          calendarData={data}
          mode="create"
        />
        
// 일정 수정 부모 컴포넌트
<CreateEventForm
          studyId={event.study_id}
          eventDate={event.event_date}
          deleteForm={handleDelete}
          setIsEdit={setIsEdit}
          initialData={{
            calendarId: event.calendar_id,
            description: event.event_description,
            startTime: event.start_time,
            endTime: event.end_time,
          }}
          calendarData={calendarData}
          withoutEditData={existTimeWithoutEdit}
          mode="edit"
        />

 

CreateEventForm 컴포넌트는 모드를 mode: "create" | "edit" 받아서 selectTime 컴포넌트로 전달

// CreateEventForm 컴포넌트
interface CreateEventFormProps {
  studyId: string;
  eventDate: string;
  deleteForm: () => void;
  setIsEdit?: React.Dispatch<React.SetStateAction<boolean>>;
  initialData?: {
    calendarId: string;
    description: string;
    startTime: string;
    endTime: string;
  };
  calendarData: Tables<"calendar">[] | undefined;
  withoutEditData?: Tables<"calendar">[];
  mode: "create" | "edit";
}

 <SelectTime
          onTimeSelect={handleTimeSelect}
          onClose={() => {
            setIsModalOpen(false);
            setActiveInput(null);
          }}
          eventStart={eventStart}
          eventEnd={eventEnd}
          selectingType={activeInput === "start" ? "start" : "end"}
          calendarData={calendarData}
          withoutEditData={withoutEditData}
          mode={mode}
        />

 

 

③ create / edit 조건 별로 데이터 가공 및 적용할 함수 생성

 // 기존 일정의 시간데이터 가공(등록 모드)
  const existTimeRanges = calendarData?.map((event) => ({
    start: convertTimeToMinutes(event.start_time.slice(0, -3)),
    end: convertTimeToMinutes(event.end_time.slice(0, -3)),
  }));

  // 기존 일정의 시간데이터 가공(수정 모드)
  const existTimeRangesWithoutEdit = withoutEditData?.map((event) => ({
    start: convertTimeToMinutes(event.start_time.slice(0, -3)),
    end: convertTimeToMinutes(event.end_time.slice(0, -3)),
  }));

  // 선택된 시간이 이미 존재하는지 확인하는 함수(등록 모드)
  const checkTimeExist = (selectedMinutes: number) => {
    for (const timeRange of existTimeRanges!) {
      if (selectingType === "start") {
        if (
          selectedMinutes === timeRange.start ||
          (selectedMinutes > timeRange.start && selectedMinutes < timeRange.end)
        ) {
          return true;
        }
      } else {
        if (
          selectedMinutes > timeRange.start &&
          selectedMinutes < timeRange.end
        ) {
          return true;
        }
      }
    }
    return false;
  };

  // 선택된 시간이 이미 존재하는지 확인하는 함수(등록 모드)
  const checkTimeExistWithoutEdit = (selectedMinutes: number) => {
    for (const timeRange of existTimeRangesWithoutEdit!) {
      if (selectingType === "start") {
        if (
          selectedMinutes === timeRange.start ||
          (selectedMinutes > timeRange.start && selectedMinutes < timeRange.end)
        ) {
          return true;
        }
      } else {
        if (
          selectedMinutes > timeRange.start &&
          selectedMinutes < timeRange.end
        ) {
          return true;
        }
      }
    }
    return false;
  };

 

 

④ 조건별 유효성 적용

// 확인&유효성(시작시간 < 종료시간)
  const handleConfirm = () => {
    const selectedTime = `${selectedHour}:${selectedMinute}`;
    const selectedMinutes = convertTimeToMinutes(selectedTime);

    if (mode === "create" && checkTimeExist(selectedMinutes)) {
      alert("등록된 일정 중 겹치는 시간이 있습니다! 확인해주세요!");
      return;
    }

    if (mode === "edit" && checkTimeExistWithoutEdit(selectedMinutes)) {
      alert("등록된 일정 중 겹치는 시간이 있습니다! 확인해주세요!");
      return;
    }

    if (selectingType === "start" && eventEnd) {
      const endMinutes = convertTimeToMinutes(eventEnd);
      if (selectedMinutes >= endMinutes) {
        alert("시작 시간이 종료 시간보다 늦을 수 없습니다! 다시 확인해주세요!");
        return;
      }
    }

    if (selectingType === "end" && eventStart) {
      const startMinutes = convertTimeToMinutes(eventStart);
      if (selectedMinutes <= startMinutes) {
        alert("종료 시간이 시작 시간보다 빠를 수 없습니다! 다시 확인해주세요!");
        return;
      }
    }

    onTimeSelect(selectedTime);
    onClose();
  };

 

 

 

결과

이제 수정모드일때는 수정중인 일정을 제외한 일정들과 비교를 한다!

 

 

 

🧐 느낀점

여러 유효성들을 적용하면서 처음에는 이것들을 어떻게 다 처리해줘야하지.. 감이 안잡혔다. 시간이 좀 걸렸지만 어떻게든 구현하고 하다보니 오늘같이 이런 추가적인 부분은 생각보다 금방 처리했다. 작업을 어떻게 해줄지 하나하나 생각을 정리하고 그에맞게 로직 작성과 테스트를 반복했다. 중복되는 코드들은 리팩토링해야하지만, 의도한대로 잘 적용되는 모습을 보니 꽤 뿌듯했다.

앞으로 더 효율적인 방향으로 생각하고 코드에도 적용하고 싶다. 결국 많이 많이 경험해보는게 중요한 것 같다.