Skip to main content
← 블로그

화면 끝이 내일이다 — 일간 뷰에서 끌어다 놓아 날짜를 옮기기

VauDium·

스레드에서 온 의견 하나로 시작했습니다. 모바일 일간 시간표와 리스트에서 할 일을 끌어 화면 왼쪽 끝에 놓으면 어제로, 오른쪽 끝에 놓으면 내일로 갑니다. 만들고 나서 낙관 반영이 안 붙던 버그와 되돌아갔다 사라지던 깜빡임을 잡은 이야기입니다.

화면 끝이 내일이다 — 일간 뷰에서 끌어다 놓아 날짜를 옮기기

이 기능은 스레드에서 받은 의견 하나에서 시작했습니다. 페칫 모바일의 달력에서 날짜를 누르면 그날의 일간 뷰가 열립니다. 시간표 모드에서는 블록을 끌어 시각을 옮길 수 있고, 위아래 핸들로 길이를 바꿀 수 있습니다. 그런데 날짜는 못 옮깁니다. 오늘 못 한 일을 내일로 미루려면 상세를 열고 날짜를 고쳐야 했습니다. 주간 시간표에서는 옆 요일 컬럼으로 끌면 되는데, 일간 뷰엔 컬럼이 하나뿐이니까요.

1. 컬럼이 하나면, 화면 끝이 이웃이다

주간 시간표의 날짜 이동은 가로 이동량을 컬럼 폭으로 나눠 며칠 옮길지 셉니다. 일간 뷰에는 나눌 폭이 없습니다. 그래서 규칙을 바꿨습니다. 손가락이 화면 왼쪽 끝 48pt 안에서 놓이면 어제, 오른쪽 끝 48pt 안이면 내일. 블록의 위치가 아니라 손가락의 위치로 판정합니다. 하루 종일 걸치는 블록은 폭이 화면과 같아서, 블록 가장자리로 판정하면 들자마자 이미 “내일” 영역에 들어가 버리거든요.

세로 이동은 그대로 살아 있습니다. 끌어서 시각도 바꾸고 화면 끝에 놓으면 시각 변경과 날짜 이동이 함께 적용됩니다. 주간 시간표와 같은 문법입니다.

2. 끝까지 가지 않아도 보인다

처음엔 손가락이 가장자리에 들어왔을 때만 그 쪽에 파란 띠를 띄웠습니다. 결정은 반대였습니다. “끝까지 가지 않아도 가장자리에 어제/내일 표시를 띄워 놓자. 그래야 거기에 놓을 수 있다는 걸 알 테니까.”

맞는 말이었습니다. 존재를 모르는 드롭 영역은 없는 것과 같습니다. 이제 블록을 드는 순간 양쪽 끝에 옅은 띠와 “어제” “내일” 라벨이 함께 뜹니다. 손가락이 들어온 쪽만 진해지고 가벼운 진동이 한 번 옵니다. 라벨은 세로 띠 안에 90도 돌려 넣었는데, 첫 버전에서 “다음 날로”가 “다음…“으로 잘렸습니다. 회전은 레이아웃이 끝난 뒤에 적용되는 변환이라, 글자 상자가 40pt 폭 안에서 먼저 잘리고 나서 돌아간 겁니다. 상자를 띠 밖으로 넉넉히 넓혀서 풀었고, 문구도 “어제/내일”로 줄였습니다.

3. 화면은 머물고, 토스트가 말한다

놓은 뒤 화면이 그 날짜로 따라갈지도 정해야 했습니다. 머물기로 했습니다. 오늘 못 한 일 여러 개를 연달아 내일로 미루는 흐름이 자연스럽고, 상단의 주간 띠에서 옮겨간 게 바로 보이니까요. 블록은 사라지고, “다음 날로 옮겼어요” 토스트가 뜹니다. 토스트의 “보기”를 누르면 그 날짜로 갑니다.

4. 낙관 반영이 안 붙었다

기기에서 처음 굴려 본 반응은 “옵티미스틱 드롭이 아니네?“였습니다. 놓으면 서버 응답이 올 때까지 블록이 그 자리에 남아 있었습니다.

드롭 코드는 캐시에 새 날짜를 먼저 써 놓고 있었습니다. 문제는 그 다음이었습니다. 캐시가 바뀌면 화면은 재조회 결과를 이전 모델과 합치는데, 이때 revision이 같으면 옛 모델을 그대로 재사용하는 가드가 있습니다. 불필요한 재렌더를 막는 장치죠. 낙관 쓰기는 revision을 올리지 않으니, 새 날짜를 가진 모델이 버려지고 옛 날짜 모델이 남았던 겁니다. 서버 응답이 revision을 올려 줄 때까지요.

세로 시각 드래그도 같은 문제를 안고 있었는데 티가 안 났습니다. 블록이 손가락 위치에 그대로 붙어 있어서, 데이터가 늦게 와도 화면은 이미 옮겨진 것처럼 보였거든요. 날짜 이동은 블록이 사라져야 하니 숨길 수 없었습니다. 칸반 순서와 생성일이 이미 같은 이유로 별도 비교되고 있었고, 시작일과 마감을 그 목록에 더했습니다.

5. 돌아왔다가 사라진다

고치고 나니 다음 반응은 “원래 자리로 돌아왔다가 사라져”였습니다. 이건 스레드 두 개의 시차였습니다. 가장자리 밖에 놓았을 때 블록을 제자리로 되돌리는 애니메이션은 UI 스레드에서 즉시 시작됩니다. 캐시 반영으로 블록을 지우는 건 JS 스레드에서 한 박자 뒤에 옵니다. 그 사이에 블록이 돌아가는 게 보였던 겁니다.

가장자리 드롭이면 되돌리지 않고 UI 스레드에서 그 자리에서 바로 숨기도록 바꿨습니다. 지우는 건 뒤따라 오는 캐시 반영이 합니다. 혹시 캐시에 반영이 막혀 블록이 남아 있으면 1.5초 뒤 다시 보이게 하는 안전장치도 넣었습니다. 보이지 않는 채로 남는 블록이 가장 나쁜 결과니까요.

6. 리스트 뷰도

일간 뷰에는 시간표 말고 리스트 모드도 있습니다. “리스트에서는 드래그가 안 되네?“가 마지막 의견이었습니다. 리스트는 시작 시각 순으로 정렬돼 있어 순서를 바꾸는 드래그는 의미가 없습니다. 그래서 순서 변경 없이 가장자리 드롭만 넣었습니다. 메모 목록의 수동 순서 드래그에 쓰던 롱프레스 행 컴포넌트를 그대로 가져와, 삽입선과 슬롯 판정을 빼고 손가락의 가로 좌표만 더했습니다. 행을 들면 같은 띠가 뜨고, 같은 규칙으로 옮겨지고, 같은 토스트가 말합니다.

이틀에 걸친 블록과 루틴의 가상 발화, 실제 시각 기록 블록은 원래 드래그가 막혀 있어 이 기능에서도 빠집니다. 데스크톱에는 아직 없습니다. 의견 하나에서 시작해 결정 넷과 버그 둘을 지나온 하루였습니다.