[next.js] Drag And Drop

채연·2023년 10월 5일
0

GLOG

목록 보기
1/1

Drag And Drop

이번 프로젝트에서 좀 재밌게 웹페이지를 구현해보고 싶어서 UI 대한 고민을 많이 하였다.

그 중 사이드 네비게이션이 좀 맘에 안 들었고, 어떻게 재밌게 할 수 없을까? 라는 생각에 드래그 앤 드롭으로 화면 전환을 해보자! 라는 생각에 바로 구현을 들어가게 되었다.

라이브러리 선정하기

제일 처음에 아무것도 모르고 한 프로젝트에서 드래그 앤 드롭 기능으로 react-beautiful-dnd 을 사용했었다.

드래그 앤 드롭 라이브러리를 여러 개 찾아본 결과, 이 라이브러리가 현재도 많이 사용하고 있는 것 같고 기능도 괜찮았기에 채택하게 되었다.

그땐 적용하는 데에 바빠 어떤 내용이 있었는지도 기억이 하나도 나지 않기 때문에 이번엔 그런 불상사를 막으려 라이브러리를 조금 파헤쳐보려한닷

라이브러리 설치

typescript를 사용하면

npm i --save-dev @types/react-beautiful-dnd

아니라면

npm i react-beautiful-dnd

으로 설치해주면 된다.

나는 타입스크립트를 사용하기 때문에 전자를 선택

라이브러리 설명

여기서 사용하는 컴포넌트는 크게 총 세 가지가 있다.

DragDropContext, Droppable, Draggable

DragDropContext : 드래그 앤 드롭을 사용하기 위해 정의
Droppable : 드롭한 요소를 놓을 수 있는 곳
Draggable : 드롭할 요소

라이브러리 적용하기

-> 피그마로 대충 짠 UI다.

발바닥을 드래그하고, 본문에 드롭하면 페이지가 넘어가도록 하는 로직을 만들 것이다.

그러려면 Droppable은 2개, Draggable은 한 개가 필요하다

라이브러리 적용하기 (코드)

DragDropContext

제일 먼저 드래그 앤 드롭을 사용하기 위하여 DragDropContext를 선언해준다.

<DragDropContext onDragEnd={}>
</DragDropContext>

-> onDragEnd는 Drop을 하였을 때 나오는 이벤트로, 원하는 로직을 넣어주면 된다.

Droppable

<DragDropContext onDragEnd={}>
  <Droppable droppableId="left-droppable">
    // provided에는 드래그앤드롭시에 필요한 데이터, snapshot엔 드래그할 때의 상태를 담고 있다.
    {(provided, snapshot) => (
      // ref와 ...provided.droppableProps는 필수적인 속성!
      <div ref={provided.innerRef} {...provided.droppableProps} >
      
      // 이것도 필수적으로 써줘야 하는 것!! 드래그 하는 동안의 공간을 만드는 역할을 함
      {provided.placeholder}
      </div>
    )}
  </Droppable>
</DragDropContext>

Draggable

<DragDropContext onDragEnd={}>
  <Droppable droppableId="left-droppable">
    {(provided, snapshot) => (
      <div ref={provided.innerRef} {...provided.droppableProps} >
      	<Draggable draggableId={id} index={index}>
          {(provided) => (
            <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} >
            // 여긴 원하는 UI (나는 발바닥을 넣었다)
            </div>
          )}
        </Draggable>
      {provided.placeholder}
      </div>
    )}
  </Droppable>
</DragDropContext>

최종 코드

'use client';

import CenterContent from '@/components/Layout/CenterContent';
import Sidebar from '@/components/Sidebar/Sidebar';
import { Stack } from '@mui/material';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';

const Home = () => {
  const router = useRouter();
  const [isBrowser, setIsBrowser] = useState(false);

  useEffect(() => {
    setIsBrowser(process.browser);
  }, []);

  const dragHandler = (result: DropResult) => {
    if (
      result.destination?.droppableId === 'right-droppable' &&
      result.source.droppableId === 'left-droppable'
    ) {
      console.log(result.source.index);
      router.push(`/${result.source.index}`);
    }
  };

  const sidebarList = {
    sidebarDto: {
      categoryId: 0,
      categoryName: '프론트엔드',
      postTitleDtos: [
        {
          postId: 0,
          postTitle: 'HTML',
        },
        {
          postId: 1,
          postTitle: 'CSS',
        },
        {
          postId: 2,
          postTitle: 'JS',
        },
      ],
    },
  };

  const writeList = sidebarList.sidebarDto.postTitleDtos;

  return (
    <>
      {isBrowser ? (
        <DragDropContext onDragEnd={dragHandler}>
          <Sidebar />
          <CenterContent>
            <Stack gap={8} width="100%" height="100%" direction="row">
              <Droppable droppableId="left-droppable">
                {(provided, snapshot) => (
                  <div
                    className="top-container"
                    style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
                    {...provided.droppableProps}
                    ref={provided.innerRef}>
                    <Stack gap={4}>
                      {writeList.map((write) => {
                        return (
                          <Draggable
                            key={write.postId}
                            draggableId={`container-${write.postId}`}
                            index={write.postId}>
                            {(provided) => (
                              <div
                                ref={provided.innerRef}
                                {...provided.draggableProps}
                                {...provided.dragHandleProps}>
                                <Stack direction="row" alignItems="center" gap={4}>
                                  <Stack>{write.postTitle}</Stack>
                                  <Stack
                                    bgcolor="#ffffff"
                                    borderRadius="50%"
                                    width="50px"
                                    height="50px"></Stack>
                                </Stack>
                              </div>
                            )}
                          </Draggable>
                        );
                      })}
                    </Stack>
                    {provided.placeholder}
                  </div>
                )}
              </Droppable>
              <Droppable droppableId="right-droppable">
                {(provided) => {
                  return (
                    <div
                      className="right-container"
                      {...provided.droppableProps}
                      style={{ width: '100%', height: '100%' }}
                      ref={provided.innerRef}>
                      <Stack width="70%" height="100%" color="black" bgcolor="#ffffff">
                        asdf
                      </Stack>
                      {provided.placeholder}
                    </div>
                  );
                }}
              </Droppable>
            </Stack>
          </CenterContent>
        </DragDropContext>
      ) : null}
    </>
  );
};

export default Home;

참고 블로그

profile
Hello Velog

0개의 댓글