어제에 이어서 카드들 사이에 옮기거나 했을 때 순서를 어떻게 만들것인가에 대한 고민을 쭉 코코와 같이 했다.
첫번째로 생각했던것은 일단 position
이라는 컬럼을 만들고 모든 Card
에 포지션이 바뀔때마다 포지션을 업데이트 해주는 방식이지만, 하나 바뀔때마다 전부 업데이트 해줘야한다는건 비효율적이라고 생각했다.
두번째로 생각했던것은 소수점을 이용하는 거다. 그냥 한 아이템을 추가할때는 마지막 포지션에 +1만 하면 되고 만약에 카드 사이에 집어넣으려고 한다면 소수점을 이용한다는 거다. 예를들어서 포지션이 1과 2인 카드가 있고 그 사이로 카드를 옮기면 1.5가 되는 방식이다.
이 방식의 문제점은 다음과 같다고 한다.
First, don't try to do anything clever with decimal numbers, because they'll spite you. REAL and DOUBLE PRECISION are inexact and may not properly represent what you put into them.
real and double precision 이 부정확해서 문제라고 하는데 아직 정확한 건 모르겠다. 일단 50명 이상이 upvote했으므로 꽤 믿을만한 것이라고 생각이 들기 때문에 어쨋든 간에 답변자의 말을 따르는 것이 좋을것으로 보인다.
이것 외에도 우려되는 문제점은 계속 1과 2사이에 카드를 넣는다고 생각해본다면, 소수점 아래로 카드 하나당 하계속해서 자릿수가 하나씩 늘어나게된다. 이게 문제인 이유는 데이터베이스에 double 타입에 최대,최소값이 존재할 것이고 그 최대,최소값이 계속해서 자릿수가 하나씩 늘어나는 거에 비해서 범위가 그리 크지 않을거라는 것이다.
질문자가 언급한 거처럼 내 생각에도 보통 투두앱이라면 기껏해봤자 몇십개 혹은 100개정도의 아이템이 등록되지 않을까? 그래서 크게 고민할 것없이 내 생각에는 List
를 사용해도 괜찮을 것 같다. 그러니까 처음에 생각한 방법 말이다.
그래도 완벽한 방법을 찾고 싶은 건 어쩔수가 없는 것 같다.
결국에 계속 코코와 토의하면서 그 방법을 찾아내긴 찾아냈다. 코드로 구현하는 것이 굉장히 어려웠긴 하지만 말이다.
처음에 그 힌트를 얻은 것은 링크의 답변들 중에서 Alexander Bird가 납긴 답변이다. 그는 세가지 방법을 소개했는데, 마지막 3번에서 큰 힌트를 얻었다.
검색 중에 이 고민을 특히 https://softwareengineering.stackexchange.com/questions/195308/storing-a-re-orderable-list-in-a-database 여기에 질문한 사람이 너무 우리와 똑같아서 놀랬다. 이 링크에서 많은 도움을 얻었고 한번 정리해보고자 한다.
1번 방법은 index가 1과 2인 아이템 사이에 넣으려면 인덱스 2부터 5까지 모두 인덱스를 증가시켜야한다. 즉, 아까 첨에 생각했던 모두 업데이트시키기. 이건 패스.
2번 방법은 꽤 괜찮다. 아이템이 하나 생길때마다 100씩 증가시키고 100과 200사이인 아이템에 넣을때는 그 사이값을 넣는거다. 아마 넣는다고 생각해보면, 101이나 199가 아니라 그 중간값인 150이 되어야 할것 같다. 101이나 199를 넣는다면 바로 앞,뒤에 있는 카드에 또 다른 카드를 넣으려고 할때 바로 문제가 생길 것이다. 그러므로 사이에 넣을때 계속 그 중간값을 넣어야할것같다.
마지막 3번에서 큰 힌트를 얻었다. Jira라고 하는 데이터베이스에서 lexorank라는 개념?방법?을 이렇게 쓴다고 한다. 이걸 보고 비슷하게 구현하면 좋겠다 라고 생각했는데 일단 이게 어떤식으로 작동하는지 분석하는데에 시간을 많이썼다.
아이템을 맨위 혹은 맨아래로 움직이거나 아니면 다른 컬럼으로 옮기거나 할때 position
(3번 방법에서는 jira_rank
)을 수정해줘야하므로 3가지 경우의 수를 따져봐야한다.
만약에 3번 예시에 나와있는대로 아이템들이 저렇게 있다면 맨위나 맨아래로 옮겼을 경우의 postion
은 어떻게 만들어줘야할까?
내가 생각했던 것은 맨위로 옮겼을 땐 맨위로 옮긴 아이템의 포지션은 0|hzztxk:
보다 우선순위가 한단계 더 높은 0|hzztxj:
가 될것이라고 생각했다. 또 그 위로 다른 카드가 올라가면 그 카드의 포지션은 마지막 알파벳 j
보다 1 작은 i
가 되어서 0|hzztxi:
가 될것이다. 그러다가 마지막 알파벳이 a
가 되면 그 앞에 있는 알파벳이 바뀌는 것이다. 그럼 x
를 바꿔야한다. x
는 당연히 y
가 된다.
아이템을 맨아래로 넣을때는 위에 했던것과 정반대로 해주면된다. 즉, 0|hzztzz:
은 0|hzzuaa:
가 될 것이다.
그럼 답변자는 왜 마지막에 0|hzztzz:i
0|hzztzz:r
이런식으로 해줬을까? 나는 이걸 3가지의 경우의 수 중에서 3번째 경우의수인 카드 사이에 집어넣을때 이것을 활용해야하겠다고 생각했다.
위에 3번예제를 간소화시켜서 아래 예시로 만들어보았다.
item | position
----------------
1 | aaa
2 | aab
3 | aac
...
...
여기 1번아이템과 2번아이템 사이에 카드를 넣어야 한다고 생각해보자. 이건 처음에 소개했던 index가 1과 2인 아이템 사이에 넣는 방식이랑 똑같다. 그렇기때문에 별다른 뾰족한 수가 없다.
처음엔 aaaa
를 넣어보자고 생각했다. 그럼 일단 정렬했을때 aaaa
가 두번째 순서로 들어가긴 한다. 그런데 aaa
와 aaaa
사이에 집어 넣으려면? 여기선 전혀 방법이 없기 때문에 계속 고민을 하다가, 아까 그 답변자가 다른 곳에서 그에 대한 해결책을 알려준 것을 발견했다.
그가 제시한 해결책은 1번 아이템과 2번아이템 사이에 aaaa
라고 넣는 대신에, aaaai
을 넣으면 해결된다고 했다. 이것을 보기좋게 나누기위해서 :
을 사용하여 aaa:ai
라고 하겠다.
item | position
----------------
1 | aaa
??? | aaa:ai
2 | aab
3 | aac
...
...
이렇게 되면 aaa
와 aaa:ai
사이에 뭔가 아이템이 옮겨진다면 자연스럽게 aaa:ah
, aaa:ag
, ..., aaa:aa
이런 식으로 나아갈수있다. 아까 말한거처럼 물론 aaa:aa
를 넣을 차례라면 그 대신에 aaa:aai
가 들어갈것이다. 이해가 안갈땐 직접 데이터베이스에 넣어보고 정렬해보면 바로 이해가간다.
반대로, aaa:ai
와 aab
사이에 넣으면? aaa:aj
, aaa:ak
, ..., aaa:az
대신에 aaa:azi
이렇게 된다.
여기서 드는 의문은 그렇다면 aaa
위에 아이템을 옮기면 어떻게 처리할것이라는거냐이다. 아까 말한대로하면 a보다 1작은 알파벳을 넣어줘야하는데 첫번째,두번째,세번째 모두가 a
이기 때문에 여기서도 다른 수가 안생긴다.
그렇기 때문에 애초에 처음 아이템을 생성할때 hzztxk
이런식으로 중간정도되는 값을 줘서 해결하는 것이라고 생각한다.
오늘 이걸 코드로 구현해보려고 해봤으나 일단 시간상 내일로 미루어봐야겠다. 이걸 어떻게 구현할 수 있을지 아니면 아직 이정도까진 무리일지..잘 되면 좋겠다.
해결될듯 안될듯한 어떤 문제를 가지고 그것을 어떻게 구현할지 집중해서 토의했던 것이 정말 즐거웠다. 실제로 코드로 옮길 수 있을지 아직은 미지수이지만 그것과 관계없이 머리맞대고 토의하는 행위 자체들이 즐거웠다.