프로젝트 협업 도구를 만드는 과정에서, 리스트나 리스트 안의 카드의 순서를 바꿀 수 있도록 만들어줘야 하는데, 프론트는 건들지 않고 백엔드로만 작업을 해주려니 문제가 조금 있었다.
순서가 2번에서 7번으로 바뀐다고 치면 2번 카드가 7번으로 이동함과 동시에 원래 위치하고있던 7번부터 3번까지는 한 칸씩 앞으로 밀려 이동해야 하는 것이다.
그러면 DB에 저장되어있던 순서(position) 컬럼의 정보를 하나하나 바꿔주게 되는 문제가 생긴다.
그래서 다르게 접근하면, linked list방식으로 prev와 next 컬럼을 만들어주어 한 카드에게 이전 순서와 다음 순서의 정보를 부여하는 것이 하나 있다.
그러나 이는 card를 불러올 때 Db에서 불러오며 배열로 정렬된 데이터를 한번 더 정렬시키는 작업이 필요하다.
하나는 부동 소수점을 이용해서 JS에서 지원하는 가장 큰 수인 Number.MAX_VALUE를 가지고 순서 컬럼에 큰 수부터 2씩 나누어 저장해준 뒤, 자리를 바꿔주려하면 바꾸고자 하는 자리와 그 앞의 자리 순서값의 합을 2로 나누어 바꾸려는 카드의 순서 값에 저장해주기만 하면 중간값이 되어 원하는 자리에 들어갈 수 있다.
그러나 이는 mysql의 double로 사용되어 8byte가 필요하고, 순서 값이 3.48721832e+307과 같은 큰 값으로 들어가게 된다.
순서를 바꾸면 결국 저 값은 점점 작아지며 더 이상 값을 표현할 수 없는 상태가 되는데,
그 경우는 대략 1000번 이상의 순서 바꾸는 작업을 진행해야 하기에 충분하다 판단되었고,
표현할 수 없는 상태의 경우는 예를 들어 (A + B) / 2로 진행하다보면(A가 작은수)
반복해서 계속 나누었을 경우 작은 수인 A로 수렴하게 되어 값이 바뀌지 않게 된다.
그래서 결국 (A + B) / 2 가 A가 되었을 때 카드의 현재 순서를 가지고 순서 값을 초기화 시켜준 뒤 진행하면 된다.
그래서 본인은 부동 소수점을 활용한 순서 변경 방식을 사용했고,
간단하게 코드를 보면 아래와 같다.
// 저장은 아래와 같이 한다. 카드가 없을 경우만 최대값에서 2를 나눈다.
console.log(cardList);
if (cardList.length === 0)
nextCardPosition = Number.MAX_VALUE / 2;
else
nextCardPosition = cardList[0].position / 2;
이후에 카드 순서를 바꾸면
const selectPosition = selectCard[changePositionNumber - 1].position;
// 첫 번째 자리로 옮기는 경우
if (changePositionNumber === 1) {
await this.cardRepository.update({ id: cardId }, { position: selectPosition / 2 });
return { message: `${selectPosition / 2}로 이동` }
}
// 마지막 자리로 옮기는 경우 기존 마지막녀석의 position을 그 앞으로 옮기게 값을 바꾸고
// 이후에 우리가 선택한 녀석을 마지막자리로 보내준다(Number.MAX_VALUE/2가 마지막 수 고정)
else if (changePositionNumber === selectCard.length) {
await this.cardRepository.update({ id: selectCard[selectCard.length-1].id }, { position: (selectPosition + selectCard[selectCard.length - 2].position) / 2 });
await this.cardRepository.update({id: cardId}, {position: Number.MAX_VALUE/2})
return { message: `${(selectPosition + selectCard[selectCard.length - 1].position) / 2}로 이동` }
}
// 그 외
const nextPosition = selectCard[changePositionNumber].position;
const changeValue = (selectPosition + nextPosition) / 2;
if (changePositionNumber >= selectCard.length || changePositionNumber <= 0)
throw new BadRequestException('요청한 position은 card의 범위를 넘어갔습니다.')
await this.cardRepository.update({id: cardId}, {position: changeValue});
생각나는대로 작성하고 테스트해본거라 코드가 많이 지저분하지만,
이런 방식을 사용해서 카드 순서를 바꿔보았다.
효율적이지 못한 방법일 수도 있다.
리팩토링 하면서 좀 더 효율적인 방법이 있을지 생각해봐야겠다.