[TIL] JQueryUI를 사용한 드래그앤드랍 구현 23.08.14

이상훈·2023년 8월 14일
0

[내일배움캠프]

목록 보기
51/68

보드 순서 변경 및 컬럼 순서 변경 로직

// 보드(카드) 동일 컬럼 내 이동
  async orderBoard(body: orderBoardDto, projectId: number, boardId: number): Promise<IResult> {
    const { newBoardSequence } = body;
    const entityManager = this.boardRepository.manager;

    const findBoard = await this.boardRepository.findOne({ where: { id: boardId, project: { id: projectId } } });

    if (!findBoard) throw new HttpException('해당 보드를 찾을 수 없습니다', HttpStatus.NOT_FOUND);

    await entityManager.transaction(async (transactionEntityManager: EntityManager) => {
      const targetBoard = await this.boardRepository.findOne({ where: { boardSequence: newBoardSequence } });

      if (targetBoard) {
        const changeSequence = findBoard.boardSequence;
        findBoard.boardSequence = targetBoard.boardSequence;
        targetBoard.boardSequence = changeSequence;
        await transactionEntityManager.save(Board, [findBoard, targetBoard]);
      }

      findBoard.boardSequence = newBoardSequence;
      await transactionEntityManager.save(Board, findBoard);
    });

    return { result: true };
  }

  // 보드(카드) 다른 컬럼으로 이동
  async moveBoard(projectId: number, boardId: number, columnId: number): Promise<IResult> {
    const targetBoard = await this.boardRepository.findOne({ where: { id: boardId, project: { id: projectId } } });
    const targetColumn = await this.boardColumnRepository.findOne({ where: { id: columnId }, relations: ['boards'] });
    const entityManager = this.boardRepository.manager;

    if (!targetBoard || !targetColumn) throw new HttpException('해당 보드 또는 컬럼을 찾을 수 없습니다.', HttpStatus.NOT_FOUND);

    await entityManager.transaction(async (transactionEntityManager: EntityManager) => {
      const maxSequence = targetColumn.boards.reduce((max, b) => Math.max(max, b.boardSequence), 0);

      targetBoard.boardColumn = targetColumn;
      targetBoard.boardSequence = maxSequence + 1;
      await transactionEntityManager.save(Board, targetBoard);
    });

    return { result: true };
  }

같은 컬럼 내의 보드 이동을 진행할 때는 클라이언트가 선택한 보드를 findBoard에 담아 존재하는지 검색하고 변경하고자 하는 보드를 targetBoard로 잡아 해당 보드의 sequence번호와 교환해주는 방식을 채택

다른 컬럼으로 이동하는 경우 이동하고자 하는 보드를 targetBoard에 담고 map()reduce()를 통해 해당 보드의 maxSequence값을 구해준 뒤 + 1하여 맨 뒤에 들어갈 수 있도록 로직을 구성하였음


썬더클라이언트로 정상작동 확인 후 프론트 연결 작업 시작
JQueryUI라이브러리 중 .sortable을 사용하여 drag & drop을 구현하려고 함

오류가 생겼던 부분의 정확한 코드가 기억이 나진 않지만 구글링을 통해 드래그앤드랍을 어떻게 하는지 찾아보던 중 prev()next()라는게 있어서 적용 후 테스트

-> DB에 있는 시퀀스값이 1, 2, 3, 4, 5 라한다면 1번을 선택후 5번으로 이동했을때 prev()는 5번에 있는 시퀀스값을 읽어오지 못하고 undefined 에러가 발생
-> 반대로 next()의 경우 5번에서 1번으로 이동 시 undefined 발생

정확한 원인은 모르겠어서 콘솔을 찍어보면서 확인한 결과 첫 번째 카드 이동할 때는 숫자가 정상적으로 바뀌다가 두번 째 이동할 때 시퀀스번호가 +1되는 현상이 있어 번호가 불규칙적으로 바뀌는 것을 확인

    // 컬럼 아래에 보드 카드 추가
        const columnElement = document.querySelector(`[data-column-id="${columnId}"]`);

        if (columnElement) {
          columnElement.innerHTML = columnHtml;

          $(columnElement).sortable({
            handle: '.card-title',
            connectWith: `.card-body[data-column-id]`,
            opacity: 0.5,
            update: function (event, ui) {
              const targetBoard = ui.item;
              const boardId = targetBoard.find('.card-title').attr('data-board-id');
              const columnId = targetBoard.closest('.card-body').attr('data-column-id');
              // 엘리먼트 순서가 0부터시작하므로 +1하여 DB와 동기화
              const newBoardSequence = targetBoard.index() + 1;

              orderBoardSequence(boardId, newBoardSequence);
              moveBoard(boardId, columnId);
            },
          });
        }

columnId를 받아오는 방식을 targetBoard의 부모요소의 columnId값을 가져오는것으로 변경하고 시퀀스 번호는 인덱스번호와 일치시키는 것으로 문제를 해결하였음

DB에서 보드나 컬럼이 생성될 때 번호를 1부터 생성하도록 로직을 구성하였기에 인덱스에 +1을 하여 순서를 일치시킨 후 매개변수로 전달

// 보드 동일 컬럼 내 이동
async function orderBoardSequence(boardId, newBoardSequence) {
  await $.ajax({
    method: 'PATCH',
    url: `/projects/${projectId}/boards/${boardId}/order`,
    headers: {
      Accept: 'application/json',
    },
    beforeSend: function (xhr) {
      xhr.setRequestHeader('Content-type', 'application/json');
      xhr.setRequestHeader('authorization', accessToken);
    },
    data: JSON.stringify({ newBoardSequence }),
    success: () => {},
    error: (error) => {
      console.error(error);
    },
  });
}

// 보드 다른 컬럼으로 이동
async function moveBoard(boardId, columnId) {
  await $.ajax({
    method: 'PATCH',
    url: `/projects/${projectId}/boards/${boardId}/${columnId}/move`,
    headers: {
      Accept: 'application/json',
    },
    beforeSend: function (xhr) {
      xhr.setRequestHeader('Content-type', 'application/json');
      xhr.setRequestHeader('authorization', accessToken);
    },
    success: () => {},
    error: (error) => {
      console.error(error);
    },
  });
}

전달 된 매개변수는 아래의 함수에서 각자에 맞는 API를 호출

profile
코린이

0개의 댓글