<aside> 💡 API 연결 없이 임시 데이터로 화면만 구현했습니다

</aside>

메인 페이지

화면 캡처

숏츠 드래그 구현

  1. 모바일 기기일 때와 아닐 때에 event가 다르기 때문에 구분해주어야 한다. 아래 코드는 웹 기준으로 작성했다.

    const isTouchScreen =
        typeof window !== "undefined" && window.matchMedia("(hover: none) and (pointer: coarse)").matches;
    
  2. 사용자가 마우스를 눌렀을 때

    1. 드래그가 시작했다고 보고 현재 드래그 상태인지를 저장하는 state isDragging의 값을 true로 바꾼다.
    2. 사용자의 마우스가 움직인 만큼 숏츠 위치를 이동 시켜 주어야 하기 때문에 드래그 시작 위치를 positionYRef에 저장한다.
      • useState를 사용했을 경우엔 리렌더링이 일어나기 때문에 리렌더링 없이 값을 관리할 수 있는 useRef를 대신 사용했다.
    3. 마우스가 움직이는 동안 일어날 일과 마우스를 땠을 때 일어날 일이 다르기 때문에 각각 event listener를 추가한다.
      • 움직일 때 event listener를 window에 등록한 이유는 사용자의 드래그 위치가 footer로 갈 경우 드래그를 멈춰주기 위해 숏츠 부분이 아닌 window 부분에 등록했다.
    const handleMouseDown = (clickEvent) => {
      clickEvent.preventDefault();
      const carouselItems = carouselItemsRef.current;
    
    	// a
      setIsDragging(true);
    	// b
      positionYRef.current = clickEvent.pageY;
    	
    	// c
      window.addEventListener("mousemove", handleMouseMove);
      carouselItems?.addEventListener("mouseup", handleMouseUp, { once: true });
    };
    
  3. 사용자의 마우스가 움직이는 동안

    1. 사용자 마우스를 따라 숏츠도 움직여야 하므로 마우스의 모든 y 좌표에서 드래그를 시작한 y좌표를 뺀 deltaY 변수를 만들었다.
    2. 마우스가 움직이는 동안 숏츠 부분을 벗어나서 footer로 이동하거나, 최상단을 넘어간다면 드래그를 강제로 종료시켰다.
    3. 마우스가 움직인 총 길이를 transY state에 저장한다.
    const handleMouseMove = (moveEvent) => {
    	// a
      const deltaY = positionYRef.current - moveEvent.pageY;
    	
    	// b
      if (moveEvent.clientY > ITEM_HEIGHT || moveEvent.clientY < 0) {
        window.removeEventListener("mousemove", handleMouseMove);
        setTransY(0);
        setIsDragging(false);
        return;
      }
    
    	// c
      setTransY(inRange(deltaY, -ITEM_HEIGHT, ITEM_HEIGHT));
    };
    
  4. 사용자가 마우스를 뗐을 때

    1. 사용자가 끝까지 움직이지 않아도 숏츠를 넘겨주기 위해 index를 조작해야 한다.
    2. 사용자가 조금이라도 움직인 방향으로 index를 변경해준다.
      • 숏츠 리스트의 처음이거나 끝일 때는 인덱스를 변경해주면 안 되기 때문에 범위 안의 값을 반환해주는 함수를 따로 작성했다.
    3. 드래그가 끝났으므로 이동 거리를 0으로 바꿔주고 드래그 상태가 아님을 저장한다.
    4. CF 알고리즘에 해당 숏츠 시청 시간이 필요해서 현재 숏츠와 다음 숏츠가 다르면 시청 시간을 계산한다.
      • 임시로 생각한 방법이라 더 나은 방법이 있을지 고민해야 한다.
    const handleMouseUp = (moveEvent) => {
      const deltaY = positionYRef.current - moveEvent.pageY;
    	// a
      let nextIndex = currentIndex;
    
    	// b
      if (deltaY < -150) {
        nextIndex = inRange(currentIndex - 1, 0, maxLen - 1);
        setCurrentIndex(nextIndex);
      }
      if (deltaY > 150) {
        nextIndex = inRange(currentIndex + 1, 0, maxLen - 1);
        setCurrentIndex(inRange(nextIndex));
      }
    
    	// c
      setTransY(0);
      setIsDragging(false);
    
    	// d
      if (currentIndex !== nextIndex) {
        console.log(currentIndex, "번 쇼츠 ", (new Date() - startTime) / 1000, "초 봤음");
        setStartTime(new Date());
      }
    
      window.removeEventListener("mousemove", handleMouseMove);
    };
    
  5. 모바일에서도 동작은 비슷하나 event를 touchstart touchmove touchend 로 등록했다.

드래그 관련 useEffect

  1. 화면에서 보여줄 숏츠 리스트의 개수이다. 사용자가 처음 숏츠에서 이전으로 넘길 때, 마지막 숏츠에서 다음으로 넘길 때를 처리해주기 위해 저장했다.
useEffect(() => {
  maxLen.current = shortList.length;
}, [shortList.length]);
  1. 각 숏츠의 시청 시간을 측정하기 위해 숏츠의 index가 바뀔 때마다 시청 시작 시간을 저장한다. 후에 드래그로 index가 바뀌면 그 시간과 이 시간의 차이로 시청 시간을 계산한다.
useEffect(() => {
  setStartTime(new Date());
}, [currentIndex]);