빌드업 - 아마추어 풋살/축구 팀 관리 서비스 ⚽️

박상하·2024년 10월 22일

BuildUp

목록 보기
1/4

초등학생 때부터 축구를 정말 좋아했다. 그 열정은 지금까지 이어지고있다.
20살 이후 조기축구 7년, 풋살회 3년 정도 꾸준히 활동하고 있다.

개발을 시작하면서 내가 좋아하는 축구/풋살을 가지고 뭔가를 만든다면 너무 재미있겠다 라는 생각을 늘 했다. 내가 원했던 서비스는 아니지만 실제로 풋살회를 위한 커뮤니티 서비스를 만든 경험도 있다.

그러던 중에 인프런에서 아마추어 풋살/축구 팀 관리 서비스의 프로젝트의 프론트엔드 개발자 모집 공고를 보았다 ⚽️

취업을 위해 팀 프로젝트에 참여해야한다는 생각은 있었지만 꼭 하고싶은 프로젝트를 참여해야 열정을 가지고 재밌게 만들 수 있을 거 같아서 미루고 있었다..

그런데 위 프로젝트는 내가 만들어 보고싶던 주제와 많이 맞닿아있었다!
실제로 아마추어 축구/풋살을 하면서 경험했던 불편함이나 개선점들에 대해 의견을 낼 수 있을 것이라 생각했다.

무엇보다 아마추어를 위한 풋살/축구 팀관리 서비스를 실제 조기축구 회원이나, 풋살장에서 사용자들이 사용한다는 상상을 하니 참여하고 싶다는 생각이 강하게 들었다.

그렇게 지원을했고 감사히도 무경력의 필자를 뽑아주셨다..🥲

1주차 준비 - 라이브러리 알아보기

저번주 킥오프 미팅을 갖고 프론트엔드 개발자분과 React 라이브러리를 알아보기로 했다. 라이브러리를 비교하고 정리한 내용을 이 공간에 포스팅 해보자! 🧊

Drag and Drop

일단 먼저 Drag and Drop을 구현할 때 라이브러리의 사용여부를 이야기해볼 수 있을 거 같았다. Drag And Drop의 기능은 라이브러리를 사용할 수도 사용하지 않을 수도 있다.

그 차이를 직접 구현하며 비교해보자 !

라고 하기전에 기본적으로 HTML5에서 제공하는 drag and drop API는
모바일 브라우저 환경에서 작동하지 않는다 ❌

그래서 모바일 브라우저 환경은 touch event로 구분하여 이벤트를 전달해주어야한다.

중요한 점은 Drag and Drop을 구현할 때 웹과 모바일을 다르게 대응해주어야 한다는 점이다.

사용자 모바일/웹 체크 방법
드래그 앤 드롭 모바일/웹 적용 방법

그럼 이제 비교해보자

라이브러리 사용 x

HTML5는 기본적으로 drag and drop API를 지원한다.

이를 사용해서 간단한 Drag and Drop을 구현하면

function App() {
  const [players, setPlayers] = useState(["선수1", "선수2", "선수3", "선수4"]);
  const [pickedPlayerIndex, setPickedPlayerIndex] = useState(-1);
  const [selectingPlayerIndex, setSelectingPlayerIndex] = useState(-1);
  const [selectedPlayerIndex, setSelectedPlayerIndex] = useState(-1);
  const [Draging, setDraging] = useState(false);

  const onDragHandler = (e) => {
    e.stopPropagation();
    const pickedPlayerIndex = e.target.id;
    setPickedPlayerIndex(pickedPlayerIndex);
    setDraging(true);
  };

  const onDragOverHandler = (e) => {
    e.stopPropagation();
    e.preventDefault();
    const selectingPlayerIndex = e.target.id;
    setSelectingPlayerIndex(selectingPlayerIndex * 1);
  };

  const onDropHandler = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const selectedPlayerIndex = e.target.id;
    setSelectedPlayerIndex(selectedPlayerIndex);
    setSelectingPlayerIndex(-1);
    setDraging(false);

    const newPlayers = formatPlayers(pickedPlayerIndex, selectedPlayerIndex);
    setPlayers(newPlayers);
  };

  const formatPlayers = (picked, selected) => {
    let newPlayers = players;
    let temp = newPlayers[picked];
    newPlayers[picked] = newPlayers[selected];
    newPlayers[selected] = temp;

    return newPlayers;
  };

  return (
    <div className="App">
      <h1>라이브러리 테스트</h1>
      <div>
        {players.map((player, i) => (
          <div>
            <span
              id={i}
              style={{
                marginTop: "100px",
                display: "flex",
                flexDirection: "column",
                padding: "10px",
                borderRadius: "25px",
                border: i === selectingPlayerIndex ? "3px red solid" : null,
                fontSize: i === selectingPlayerIndex ? "30px" : "12px",
              }}
              draggable
              onDrag={onDragHandler}
              onDragOver={onDragOverHandler}
              onDrop={onDropHandler}
            >
              {player}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

export default App;

모듈화를 진행하지 않고 하나의 파일에서 구현을 해보았다.

일단 state가 꽤 사용이 된다. 기본적으로 드래그의 유무, 드래그중인 요소, 드랍된 요소, 전체 요소들이 state로 관리되어야할 거 같다. 라이브러리를 사용하지 않으면 위 상태를 어떻게 처리할지를 고민해야한다.
(react-dnd는 ContextAPI까지 라이브러리 내부에서 제공하는걸로 알고있다)
모듈화를 진행하게 되면 Props도 늘어나게 될텐데 이를 관리할때는 전역상태관리나 ContextAPI를 사용하는게 좋을 거 같다.

mdn 문서

react-dnd

React-dnd는 Drag and Drop을 직관적으로 구현할 수 있게 도와주는 라이브러리이다.
다운로드 횟수도 주에 200만회 정도이다.

그럼 실제로 사용해보고 그 후기를 작성해보자.

위와 같은 화면을 기준으로 작성 후에 각각의 장단점을 비교해보겠다.


//Player.js
import { useDrag, useDrop } from "react-dnd";

export const PlayerBox = ({ id,name }) => {
  const [{ isDragging }, dragRef, previewRef] = useDrag(
    () => ({
      type: "player",
      item: { id },
      collect: (monitor) => ({ isDragging: monitor.isDragging() }),
    }),
    [id]
  );

  const [{ isOver, canDrop }, dropRef] = useDrop(
    () => ({
      accept: "player", // 드롭할 수 있는 아이템 타입
      drop: (item, monitor) => {
        // 드롭할 때 실행되는 함수
      },
      canDrop: (item, monitor) => item.id !== id, // 동일한 id는 드롭할 수 없도록 설정
      collect: (monitor) => ({
        isOver: monitor.isOver(), // 드롭 영역에 아이템이 있는지 여부
        canDrop: monitor.canDrop(), // 드롭이 가능한지 여부
      }),
    }),
    [id]
  );

  return (
    <div
      ref={(node) => {
        previewRef(node);
        dropRef(node); // drop과 preview를 함께 사용
      }}
      style={{
        opacity: isDragging ? "0.3" : "1",
        border: isOver && canDrop ? "3px red solid" : null,
        fontSize: isOver && canDrop ? "30px" : "12px",
        padding: "8px",
        margin: "4px",
      }}
    >
      <div ref={dragRef}>Player {name}</div>
    </div>
  );
};
function App() {
  const players = [
  {name:"player1",id:1},
  {name:"player2",id:2},
  {name:"player3",id:3},
  {name:"player4",id:4}
  
  ]
  
  return (
    <div>
      {players.map((player)=><PlayerBox id={player.id} name={player.name}/>)}
    </div>
  );
}

react-dnd 공식문서

HTML5API(라이브러리 x) vs react dnd

HTML5 API 장점
1. 간단한 drag and drop을 구현할 경우에는 라이브러리를 굳이 사용하지 않아 용량에서의 이점이 있다.
2. 성능에 문제는 없다. (단, 웹과 모바일을 분리해서 개발할 것)
3. 추상적이지 않고 구체화(수동)적으로 개발할수있고 러닝커브가 낮다.

React dnd 장점
1. 선언적으로 개발이 가능해 재사용성이 좋다.
2. 추상화된 훅을 사용하여 코드가 간결해진다.
3. isDragging, isOver, canDrop 같은 상태는 따로 관리하지 않아도 된다.
4. 복잡한 drag and drop 기능을 구현할 경우 라이브러리에서 제공하는 기능이 많이 있다.
5. 라이브러리 추가 설치 (react-dnd-touch-backend)시 모바일 대응이 간결해진다.

HTML5 API 단점
1. 관리할 상태의 수가 react dnd에 비해 더 많다.
2. eventListener를 수동적으로 각 태그에 달아주어 코드복잡도가 올라갈 수 있다.
3. 확장성 측면에서의 단점

React dnd 단점
1. 러닝커브가 높다.(필자 기준)
2. 복잡한 로직이 아니라면 굳이 사용하지 않아도 될거 같다.(기본 html api로도 커버가능)
3. 추가적으로 설치해야되는 라이브러리가 있어, 라이브러리 의존도가 높아질 수 있다. (react-dnd-backend와 같은)

react-beautiful-dnd

이는 직접 구현하지는 않았다.(귀찮아서 그런건아님)

서칭해본결과 react-dnd와 유사하지만 좀 더 매끄러운 애니메이션을 제공한다.

출처

장점
1. 매끄러운 애니메이션
2. 중간정도의 러닝커브 (html5와 react dnd의 중간)
3. 관리할 상태 수가 적다

단점
1. dnd 라이브러리 중 가장 큰 용량
2. 중첩된 드롭존, 여러 타입의 드래그 앤 드롭 등)를 구현하는 데 제한적
3. 개발자의 낮은 자유도

결론

포메이션의 Drag and Drop은 필자가 개발을 하게 될 거 같다! (감사히 허락해주셨다 👍) 그럼에도 같이 개발하는 FE개발자분께 의견을 여쭤보고싶다. 그렇기에 개발을 하기전 스스로 좀 알아봐야 물어볼 수 있을 거 같아서 직접 개발을 하며 직접 느껴보았다.

느낀점은 구체적인 기능이 나오면 그때 정할 수 있을 거같다 !

만약 포메이션의 변화가 좀 더 역동적이고 다양한 기능이 추가가 된다면 라이브러리를 사용하는게 좋아보이는데 그렇지 않고 단순한 위치변경을 구현하게 된다면 라이브러리를 사용하지 않아도 될 거 같다.

기능이 정해지고 요구사항 분석과 개발 영역 분담이 이루어지게 되면 그때 다시 후기를 가져오겠다! 실제 프로젝트에서는 어떨지 궁금하다.

0개의 댓글