[로데시] 개발

[대학전쟁3] 양면빙고 웹 개발 02 - 타일 list 내 이동 본문

개인 개발/대학전쟁

[대학전쟁3] 양면빙고 웹 개발 02 - 타일 list 내 이동

로데시 2026. 1. 14. 19:02
반응형

 

0️⃣ 들어가기 전에..

대학전쟁에 나온 '양면빙고 (double sided bingo)' 게임을 보고
 
이 게임을 웹으로 만들면 재밌겠다는 생각이 들어 양면빙고 웹 개발을 시작했다.
 

 
지난 글에서는 나의 타일 9개가 있는 타일 list와 이를 뒤집을 수 있는 기능을 만들었다.
 
오늘은 이어서 타일 list의 순서를 바꿀 수 있는 기능을 구현해보고자 한다.
 

 

 

 

 

1️⃣ @dnd-kit 설치

📌 npm install @dnd-kit
 
먼저 드래그로 위치를 변경하려면 @dnd-kit의 설치가 필요하다.
 
@dnd-kit/core @dnd-kit/sortable @dnd-kit/modifiers 를 설치해준다.
 
npm @dnd-kit 설치
 
npm @dnd-kit 설치
 

 

 

 

2️⃣ 코드 작성 - 타일 이동

설치가 끝났으면 코드를 작성해준다.
 

 
📌 TileList.js 코드
 

// import 추가
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
import { arrayMove, SortableContext  } from "@dnd-kit/sortable";

// initialTiles에 key 값 추가
  { id: 1, key: "rb1" }, { id: 1, key: "rb2" }, { id: 1, key: "rb3" },
  { id: 3, key: "by1" }, { id: 3, key: "by2" }, { id: 3, key: "by3" },
  { id: 5, key: "yr1" }, { id: 5, key: "yr2" }, { id: 5, key: "yr3"  },

  const TileList = () => { 
  
  // [tiles, setTiles] 추가 -변경될 list update
  const [tiles, setTiles] = useState(initialTiles);
  
  ...

  // index -> key
  const handleFlip = (key) => {
    setFlippedMap((prev) => ({
      ...prev,
      [key]: !prev[key],
    }));
  };

  // sensors 추가 - 타일list 내 타일 이동 감지
  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance : 5 } })
  )

  // handleDragEnd - 이동 감지 -> 새로운 list로 update
  const handleDragEnd = (event) => {
    const { active, over } = event;
    if (!over || active.id === over.id) return;

    const oldIndex = tiles.findIndex((t) => t.key === active.id);
    const newIndex = tiles.findIndex((t) => t.key === over.id);
    setTiles((items) => arrayMove(items, oldIndex, newIndex));
  };

  return (
    {/* DndContext 추가 - 전반적인 드래그 제어 */}
    <DndContext 
      sensors={sensors} 
      collisionDetection={closestCenter} 
      onDragEnd={handleDragEnd}
    >

    {/* DSortableContext - 순서 정렬 */}
    <SortableContext items={tiles}>
      <div className="TileListWrapper">
        <div className="TileList">
          {tiles.map((tile) => (
            <TileItem
              key={tile.key}
              tile={tile}
              isFlipped={!!flippedMap[tile.key]}
              onClick={() => handleFlip(tile.key)}
            />
          ))}
        </div>
      </div>
    </SortableContext>

    </DndContext>
  );
};

 

 
📌 TileItem.js 코드
 


// import문 추가
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

// props 변경
const TileItem = ({ tile, isFlipped, onClick }) => {
    const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: tile.key });

    // tile 움직임을 보여줌
    const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  const displayId = isFlipped ? getPairedTileId(tile.id) : tile.id;

    return (
      <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      <div className="TileItem" onClick={onClick}>
        <img
          className="tile_img"
          src={getTileImgById(displayId)}
          alt={`tile-${displayId}`}
        />
    </div>
    </div>
  );
};

export default React.memo(TileItem);

 

 
📌 코드 결과
 
양면빙고 타일 움직임
 

 

 

3️⃣ 문제

❓ 문제 - 이동 중인 타일이 보이지 않고, 이동 결과만 보여주기 때문에 부자연스럽다.
 
💡 해결 - DragOverlay를 이용하여 이동 중인 타일을 동적으로 보여준다.
 

 

 

4️⃣ 코드 작성 - 타일 이동 (동적 / 애니메이션)

📌 TileList.js 코드
 

// import DragOverlay 추가
import { ..., DragOverlay } from "@dnd-kit/core";

  ...

  const TileList = () => { 
   const [activeTile, setActiveTile] = useState(null); // 이동중인 타일
  
  ...

  // 이동 시작 -> 이동 타일 저장 (보여주기 위함)
  const handleDragStart = (event) => {
  const { active } = event;
  const tile = tiles.find((t) => t.key === active.id);
  setActiveTile(tile);
  };

  // 이동 종료 -> 새로운 list로 update
  const handleDragEnd = (event) => {
    const { active, over } = event;

    if (over && active.id !== over.id) {
      const oldIndex = tiles.findIndex((t) => t.key === active.id);
      const newIndex = tiles.findIndex((t) => t.key === over.id);
      setTiles((items) => arrayMove(items, oldIndex, newIndex));
    }

    setActiveTile(null);
  };

  ...

  return (
    <DndContext 
      
      ...

      onDragStart={handleDragStart} {/* DndContext에 onDragStart 추가 */}
    >

    
    <SortableContext items={tiles.map((t) => t.key)}> {/* id를 넘기도록 수정 */}
      
      ...

    </SortableContext>

    {/* DragOverlay 추가 */}
    <DragOverlay>
      {activeTile ? (
        <TileItem
          tile={activeTile.id}
          isFlipped={!!flippedMap[activeTile.key]}
        />
      ) : null}
    </DragOverlay>

    </DndContext>
  );
};

 

 
📌 코드 결과
 
양면빙고 타일 움직임 (동적 기능 추가)
 
정상적으로 동작함을 확인할 수 있다!!!
 

 

 

5️⃣ 앞으로...

양면 빙고지만 빙고판은 만들지도 않았다..
 
이제 빙고판을 만들고, 양면빙고의 핵심 기능인 '착수, 이동, 뒤집기'를 구현할 것이다.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형