logo

Blog

swiper 캘린더 최적화 하기

#swiper#calendar#optimization#lazy loading
ryxxn profileryxxn
2024.02.11
thumbnail

인턴십 중 날짜별 예약 기능을 구현해야 하는 상황이 생겼다.
디자이너의 퍼블리싱 화면대로 구현을 해야 했다.
주마다 슬라이드로 캘린더를 구현하고, 예약 가능 목록을 구현해야 했다.

사내에서 슬라이드 라이브러리를 swiper를 사용하고 있었다.

처음 로드시 몇 주치 데이터를 가져와야 하는 기준의 모호함과
한 번에 많은 주차를 불러온다면 효율성이 떨어진다고 생각했다.

그래서 3주치씩 데이터를 동적으로 로드하는 방식을 생각했다.
슬라이드가 완료되면 현재 슬라이드의 앞뒤 주만을 가지고 데이터를 다루는 것이다.

example-lazy-sample

위 그림과 같이 3주치 데이터를 가지고 동적으로 로드하기로 했다.


코드 작성

우선 useDateSelector 훅을 작성했다.

typescript
// use-date-selector.tsimport { addWeeks, eachDayOfInterval, endOfWeek, startOfWeek } from 'date-fns';import { useState, useCallback } from 'react';
// input date에 해당하는 1주일치 날짜 목록 생성 함수const generateWeek = (date: Date) => {  const startOfThisWeek = startOfWeek(date, { weekStartsOn: 0 });  const endOfThisWeek = endOfWeek(date, { weekStartsOn: 0 });  return eachDayOfInterval({ start: startOfThisWeek, end: endOfThisWeek });};
// 초기 3주치 날짜 데이터const initWeeks = (initDate: Date, buffer: number = 2) => {  const weeks = [];  let currentWeekStart = startOfWeek(initDate, { weekStartsOn: 0 });
  for (let i = 0; i < buffer; i++) {    weeks.push(generateWeek(currentWeekStart));    currentWeekStart = addWeeks(currentWeekStart, 1);  }
  return weeks;};
export const useDateSelector = (initDate: Date) => {  const [selectedDate, setSelectedDate] = useState<Date>(new Date());  const [weeks, setWeeks] = useState<Date[][]>(initWeeks(initDate, 3));  const [currentSlide, setCurrentSlide] = useState<number>(0);
  const fetchWeeks = (date: Date) => {    setWeeks((prevWeeks) => [...prevWeeks, generateWeek(date)]);  };
  const handleNextSlide = useCallback(() => {    setCurrentSlide((prev) => prev + 1);    const lastWeek = weeks[weeks.length - 1];    fetchWeeks(addWeeks(lastWeek[0], 1));  }, [weeks, fetchWeeks]);
  const handlePrevSlide = useCallback(() => {    setCurrentSlide((prev) => Math.max(0, prev - 1));  }, []);
  return {    selectedDate,    setSelectedDate,    weeks,    fetchWeeks,    handleNextSlide,    handlePrevSlide,    currentSlide,  };};

그리고 DateSelector 컴포넌트를 만들었다.

tsx
// /date-selector.tsximport { Swiper, SwiperSlide } from 'swiper/react';import { format, isPast } from 'date-fns';import { ko } from 'date-fns/locale';
// ...
export const DateSelector = ({  weeks,  selectedDate,  setSelectedDate,  currentSlide,  handleNextSlide,  handlePrevSlide,}: Props) => {
  return (    <Swiper      pagination={{        type: 'fraction',        clickable: true,      }}      scrollbar={{ draggable: true }}      onSlideNextTransitionEnd={handleNextSlide}      onSlidePrevTransitionEnd={handlePrevSlide}      initialSlide={currentSlide}    >      {weeks.map((week, i) => (        <SwiperSlide key={i}>          <div>            {week.map((d: Date) => (              <div key={d.toString()}>                <div className="day">{format(d, 'E', { locale: ko })}</div>                <div onClick={() => setSelectedDate(d)}>{format(d, 'dd')}</div>              </div>            ))}          </div>        </SwiperSlide>      ))}    </Swiper>  );};

결과

result-screen

커스텀 훅을 제대로 분리해서 사용한 첫 경험이었다.
예약 화면을 구성하면서 팀장님께 코드를 깔끔하게 짜고 모듈화를 잘 한다는 칭찬을 받았다.
퇴근길에 아이디어가 생각나 바로 카페로 달려가 작업했는데
결과가 좋아 상당히 뿌듯하다.

Related Articles