드래그 문제 -> 해결..? 덜됨

최수민·2023년 8월 10일
0

TIL

목록 보기
10/41

드래그 문제

제가 프론트를 작업하는데 있어 드래그를 처음 사용하기때문에 참고자료에 있는 것을 그대로 긁어다 좀 수정해놓았습니다. 이부분을 진택님이 연결하기로 해서 수정이 이뤄졌습니다.

<script>
  const columnDragLogic = (boardId) => {
      const columnDraggables = document.querySelectorAll('.column');
      columnDraggables.forEach(draggable => {
        draggable.addEventListener("dragstart", (event) => {
          draggable.classList.add("dragging");
        });

        draggable.addEventListener("dragend", (event) => {
          draggable.classList.remove("dragging");
          const columnLists = document.querySelector('#columnLists').children;
          Object.values(columnLists).forEach(async (column, index) => {
            const columnId = column.getAttribute('data-columnId');
            try {
              const response = await fetch(`/api/boards/${boardId}/columns/${columnId}/order`, {
                method: 'PUT',
                headers: {
                  'Content-Type': 'application/json',
                },
                body: JSON.stringify({ order: index + 1 })
              });
              const data = await response.json();
            } catch (err) {
              console.error(err);
            }
          });
        });
      });

      const boardContainer = document.querySelectorAll('.columnBox');
      boardContainer.forEach(container => {
        container.addEventListener("dragover", e => {
          e.preventDefault();
          const draggable = document.querySelector(".dragging");
          if (draggable) {
            const afterElement = getDragAfterElement(container, e.clientX);
            if (afterElement === undefined) {
              container.appendChild(draggable);
            } else {
              container.insertBefore(draggable, afterElement);
            }
          } else {
            console.error("draggable element not found");
          }
        });
      });

      function getDragAfterElement(container, x) {
        const draggableElements = [
          ...container.querySelectorAll(".column:not(.dragging)"),
        ];

        return draggableElements.reduce(
          (closest, child) => {
            const box = child.getBoundingClientRect();
            const offset = x - box.left - box.width / 2;

            if (offset < 0 && offset > closest.offset) {
              return { offset: offset, element: child };
            } else {
              return closest;
            }
          },
          { offset: Number.NEGATIVE_INFINITY },
        ).element;
      }
      // columnDraggables.forEach((column) => {
      //   new Sortable(column, {
      //     group: "shared",
      //     animation: 100,
      //     ghostClass: "blue-background-class",
      //     draggable: ".column",
      //     distance: 1
      //   });
      // });
    };
</script>

위와 같이 카드 부분도 드래그를 생성하였는데 드래그 작업시 너무 빨리 버벅거리는 문제가 있어 Sortable을 넣어주셨습니다. 그리고 처음엔 함수로 묶지 않고 실행해보았는데 db에 있는 데이터가 들어가기도 전에 실행이 되서 움직이지 않았습니다.
원인을 찾아보니 아래와 같이 db데이터를 가져올때 async와 await으로 fetch를 해서 가져오기때문에 함수로 묶지않은 부분은 그것들보다 먼저 실행이 되서 난 오류였습니다.

<script>
  async function getColumns(boardId) {
      try {
        const response = await fetch(`/api/boards/${boardId}/columns`);
        const data = await response.json();
        const columns = data.columns;
        let cards;
        const columnLists = document.getElementById('columnLists');
        columnLists.innerHTML = '';

        for (const column of columns) {
          cards = await getCardsByColumnId(column.columnId);
          const cardsTags = cards.map((card) => `
                            <div draggable="true" class="card"token interpolation">${column.columnId},${card.cardId})">
                              <input type="text" readonly class="form-control-plaintext" id="cardName" value="${card.name}">
                            </div>
                          `).join('');

          columnLists.innerHTML += `
        <div draggable="true" class="column m-9" data-columnId=${column.columnId}>
          <div> ...`
        }
        columnDragLogic(boardId); //컬럼 드래그
        cardDragLogic(cards); //카드 드래그

      } catch (error) {
        console.error(error);
      }
    }
</script>

위와 같이 작업하고 나니 카드는 움직이지 않고 컬럼만 움직이는것을 확인했습니다. 뭐가 문제일까 고민하다가 계속 오류가 나는 Sortable 부분을 지우고 다시실행하니 카드도 움직이는것을 확인했습니다.

하지만 카드가 움직일때 너무 오래 들고있으면 컬럼도 같이 움직여서 문제가 발생하였고, 해당 컬럼 내에 카드의 위치를 원하는곳에 들어가는것이아닌 제일 위이나 제일 아래로만 움직여서 문제였습니다.

드래그가 작동하기는하나 오류를 다 잡은것은 아니고 이부분만 반나절을 잡고있었기 때문에 그냥 넘어가도록 했습니다.

그리고 작업을 async와 await으로 인해 오류가 났어서 잠시 들어본바로는 해당 async를 만나는 순간 후순위로 물러난다고 들었는데 오늘 다시 확인해주시고 알려준 바로는 async는 상관없이 해당 코드는 계속 잘 작동하다가 await을 만나는 순간 async 함수를 나와서 나머지 부분을 해결한 다음 다시 await부분으로 돌아와 해나가는것이었습니다.

// 초급

async function fetchAndPrint() {
  console.log(2);
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  console.log(7);
  const result = await response.text();
  console.log(8);
  console.log(result);
}

console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);

/*
fetchAndPrint()는 비동기 함수 await가 포함되어 있다.  
해당 함수는 await 코드를 만나면 나중에 처리하기 위해 특정한 장소(여기서는 queue라고 부르겠다.)에
보관해 둔다(등록해 둔다).  
그리고 나서 비동기 함수를 빠져나와 함수 바깥에 있는 일반 코드를 전부 실행한 뒤  
queue에 등록해둔 await(비동기) 코드를 실행한다.
정리하자면 await(비동기) 코드는 일반 코드 실행을 모두 마친 뒤에 실행된다.
*/

// 중급

function fetchAndPrint() {
  console.log(2);
  const response = fetch('https://jsonplaceholder.typicode.com/users');
  console.log(response); // pending 상태
  console.log(7);
  console.log(8);
}

console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);

// 결과: 1, 2, pending, 7, 8, 3, 4, 5, 6, fetch함수 실행

// 위 코드에서는 await를 사용하지 않았다. 그래서 일반적인 코드 실행 순서를 갖는다.
// 그리고 가장 마지막에 fetch함수가 실행된다.
// await를 붙이는 이유는 단순히 promise 객체를 꺼내기 위함에서 더 나아가
// await가 붙은 코드 아래에(다음에) 위치한 코드의 실행을 잠시 대기시키기 위함이다.
// 아래 코드를 대기시키는 이유는 await의 결과물을 활용해야 때문이다.

// 고급

let array = [];

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('7');
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('8');
  }, 3000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('9');
  }, 1000);
});

const p4 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('10');
  }, 3000);
});

async function promiseFunc1() {
  console.log(2);
  array.push(await p1);
  array.push(await p2);
  console.log('F1:', array);
}

async function promiseFunc2() {
  array.push(await p3);
  array.push(await p4);
  console.log('F2:', array);
}

queue1 = [p1, p2];
queue2 = [p3, p4];
// 시작
console.log(1);
promiseFunc1();
promiseFunc2();
console.log(3);
console.log(4);
console.log(5);
console.log(6);

// 결과: 1, 2, 3, 4, 5, 6, F1: [ '7', '9', '8' ], F2: [ '7', '9', '8', '10' ]
/*
함수 내 await를 만나면 해당 await 1를 등록한 뒤 함수를 빠져나감
나머지 동기 코드를 모두 실행한 뒤 아까 등록했던 await 1를 실행한다.
만약 await 2가 await 1 아래에 있었다면 await 2는 1이 처리된 이후에 등록된다. 
* 여기서 등록은 queue에 비동기 실행 목록을 순서대로 보관해둔다는 의미
* 서로 다른 비동기 함수는 서로 다른 queue를 갖는다.

* 총 4개의 promise 객체를 상단에 전역변수로 선언했는데 신기한 점은 선언한 순서대로 우선권을 가진다는 점이다.
(동일한 처리 시간을 가졌다는 가정 하에) -> 이 부분은 아직 잘 모르겠다.
*/

/*
fetchAndPrint()는 비동기 함수 await가 포함되어 있다.
해당 함수는 await 코드를 만나면 나중에 처리하기 위해 특정한 장소(여기서는 queue라고 부르겠다.)에
보관해 둔다(등록해 둔다).
그리고 나서 비동기 함수를 빠져나와 함수 바깥에 있는 일반 코드를 전부 실행한 뒤
queue에 등록해둔 await(비동기) 코드를 실행한다.
정리하자면 await(비동기) 코드는 일반 코드 실행을 모두 마친 뒤에 실행된다.
*/

// 고급

let array = [];

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('7');
}, 1000);
});

const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('8');
}, 3000);
});

const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('9');
}, 1000);
});

const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('10');
}, 3000);
});

async function promiseFunc1() {
console.log(2);
array.push(await p1);
array.push(await p2);
console.log('F1:', array);
}

async function promiseFunc2() {
array.push(await p3);
array.push(await p4);
console.log('F2:', array);
}

queue1 = [p1, p2];
queue2 = [p3, p4];
// 시작
console.log(1);
promiseFunc1();
promiseFunc2();
console.log(3);
console.log(4);
console.log(5);
console.log(6);

/*
함수 내 await를 만나면 해당 await 1를 등록한 뒤 함수를 빠져나감
나머지 동기 코드를 모두 실행한 뒤 아까 등록했던 await 1를 실행한다.
만약 await 2가 await 1 아래에 있었다면 await 2는 1이 처리된 이후에 등록된다.
* 여기서 등록은 queue에 비동기 실행 목록을 순서대로 보관해둔다는 의미
* 서로 다른 비동기 함수는 서로 다른 queue를 갖는다.

* 총 4개의 promise 객체를 상단에 전역변수로 선언했는데 신기한 점은 선언한 순서대로 우선권을 가진다는 점이다.
(동일한 처리 시간을 가졌다는 가정 하에) -> 이 부분은 아직 잘 모르겠다.
*/

프로젝트 소감

erd 다이어그램을 작업하는데 관계설정을 하는데 다대다인지 일대다인지 헷갈리는 경우가 많았고, api를 작업하는데 작성할 것도 많고 작업할수록 헷갈리게 되어 힘들었습니다.

코드로는 프론트 작업 중 보드아이디로 여러 유저정보들을 찾아내야해서 고민하다 백엔드에서 아래와 같이 작성해주었고, 불러와 사용했습니다.

 getJoinUser = async (boardId) => {
    let users = await UserBoard.findAll({ where: { boardId }, attributes: [], include: [{ model: User }] });
    if (!users) {
      throw new CustomError(404, '참여자가 없습니다.');
    }
    users = users.map((user) => user.User);
    return users;
  };

그리고 모달창에 데이터들을 뿌려주는데 저번처럼 버튼이 먹히지않을까 두려워 값이 필요한 곳만 아래와 같이 넣어줬습니다.

<script>
document.getElementById(
        'nickname'
      ).innerHTML = `<input type="text" readonly class="form-control-plaintext" value="${data.nickname}">`;
//또는
document.getElementById('backgoundColor').value = data.color;

참고자료
드래그 참고

0개의 댓글