[TIL] wanted pre-onboarding 일정 등록 중복 방지

soob·2022년 8월 20일
0

react

목록 보기
2/2

원티드 프리온보딩 프론트엔드 개발자 교육과정 중 일정 등록할 때 이미 등록된 일정이 있을 경우 중복된 일정인지 검사해주는 구현 사항이 있었다. 프로젝트 진행 과정 중에는 구현하지 못했지만 과정이 끝나고 리팩토링을 진행하면서 중복 방지 기능 구현을 해보았다.

구현조건

  • 수업 일정 추가 버튼을 누르면 수업 일정 추가
  • 수업 일정 추가 페이지에서 시작 시간을 선택 할 수 있다.
  • 수업 시간은 40분이다.
  • 기존에 등록된 일정이 있을 경우 모달을 띄어주고 추가가 되지 않는다.

화면

일정 리스트 UI

일정 추가 form UI

  • 시는 AM/PM 으로 구분되어 있음
  • 요일 중복 선택 가능

로직

시간을 숫자로 변경하고 중복된 시간의 수 인지 비교하여 검사하는 로직으로 구현

AddForm.tsx 저장 버튼 눌렀을 때 실행되는 함수

  • 입력 받은 시간을 Date 객체를 이용하여 시간 변환 함수에 파라미터로 넣어주고 am/pm 값도 함께 파라미터로 넘겨준다. 리턴된 시작 시간과 끝 시간 값을 가져올 수 있다.
  • days에는 체크된 요일들이 들어있다. 반복문으로 돌려주고 입력 받은 요일에 대한 데이터(기존에 등록되어있는 데이터에서 추출)를 필터링하여 받아온다.
  • duplicateTime에 추가될 일정 데이터와 입력받은 요일 데이터를 파라미터로 넘겨준다.
  • 중복 검사 함수를 통해 리턴 받은 boolean 값으로 조건문으로 중복 처리를 해준다.
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    const startTimeStr = `${times.hour}:${times.minute}`;
    const submitDate = new Date(`${today} ${startTimeStr}`);
    const { startTime, endTime } = submitTime(submitDate, times.meridiem);

    days.map((day: string) => {
      const newSchedule: ScheduleTypes = {
        day: day,
        start: `${startTime} ${times.meridiem}`,
        end: `${endTime} ${times.meridiem}`,
      };

      // TODO: any 제거
      const yoilData: any = data?.filter(
        (day: any) => day.day === newSchedule.day
      );

      const isDuplicate: boolean = duplicateTime(newSchedule, yoilData);

      if (isDuplicate) {
        mutate(newSchedule);
        alert("일정이 추가되었습니다.");
        navigate("/view");
      } else {
        setPopupOpen(true);
      }
    });
  };

시간 변환 파일 분리

  • select box로 제출된 am/pm 값에 따라 pm일 경우 시에 12를 더해준다.
  • 마지막 시간은 수업시간인 40을 더해준다.
  • 그렇게 구해진 수업 시작 시간과 끝 시간을 리턴해준다.
import { add, format } from "date-fns";

export const submitTime = (time: Date, meridiem: string) => {
  const startAddTime = add(time, {
    hours: meridiem === "pm" ? 12 : 0,
  });

  const endAddTime = add(time, {
    hours: meridiem === "pm" ? 12 : 0,
    minutes: CLASSTIME,
  });

  const startTime = format(startAddTime, "HH:mm");
  const endTime = format(endAddTime, "HH:mm");

  return { startTime, endTime };
};

중복 검사 훅으로 분리
param - 등록할 시간, yoilData(체크된 요일에 대한 기존 데이터)

  • 파라미터로 등록할 시간과 기존 데이터를 넘겨받습니다.
  • 기존 데이터를 반복문을 돌려 등록한 시간이 있는지 체크합니다.
  • timeToNumber 함수로 시간을 숫자 타입으로 변경해줍니다.
  • 변경된 숫자로 비교해 줍니다.
  • 시작 시간에서 40분을 뺀 값보다 입력 받은 시작 시간이 크고, 끝 시간에서 40분 더한 값이 입력 받은 끝 시간보다 작으면 중복 시간이다.
  • 중복 시간 조건일 경우, isDuplicate을 false로 넣어줍니다.
    => isDuplicate false면 중복, true면 중복되지 않음
export const duplicateTime = (
  input: ScheduleTypes,
  times: ScheduleTypes[]
) => {
  let isDuplicate = true;

  const inputMridiem = getMridiem(input.start);
  const inputStartTime = timeToNumber(input.start, inputMridiem);
  const inputEndTime = timeToNumber(input.end, inputMridiem);

  times &&
    Object.values(times).map((time: ScheduleTypes) => {
      const timeMridiem = getMridiem(time.start);
      const startTime = timeToNumber(time.start, timeMridiem);
      const endTime = timeToNumber(time.end, timeMridiem);

      if (
        inputMridiem === timeMridiem &&
        inputStartTime > Math.floor(startTime - CLASSTIME) &&
        inputEndTime < Math.floor(endTime + CLASSTIME)
      ) {
        isDuplicate = false;
      }
    });

  return isDuplicate;
};

사용된 함수

  • 시간을 숫자로 변경 (11:00 => 1100)
  • pm일 경우 시에 12를 더해줘야하기 때문에 1200을 더해 주었다.

function timeToNumber(time: string, mridiem: string) {
  const str = Number(time.split(" ")[0].replace(":", ""));
  const result = mridiem === "pm" ? Math.floor(str + 1200) : str;

  return result;
}

function getMridiem(time: string) {
  const str = time.split(" ")[1];

  return str;
}

보완할 점

hook, typescript any로 넣어진 부분 수정, 중복 검사 다른 로직 고민

0개의 댓글