어제 만든 프레젠테이션 모드를 모바일에서 실제로 써보니 문제가 한두 개가 아니었다. 퀴즈, 코드블록, 이미지 같은 인터랙티브 요소들이 슬라이드 높이를 제대로 계산하지 못해서 잘리거나 겹쳤고, 롱프레스 UX도 어색했다. 하나씩 잡아나갔다.
퀴즈 컴포넌트는 React 상태 기반이라 DOM 클로닝 방식의 프레젠테이션 모드에서는
동작할 수가 없다. ArticleQuiz에 data-article-quiz 속성을 추가하고,
extractSlides()에서 감지하면 안내 메시지로 교체하도록 했다.
"프레젠테이션 모드에서는 퀴즈를 지원하지 않습니다. 프레젠테이션 모드를 종료 후 이용해 주세요." 라는 플레이스홀더가 표시된다.
코드블록도 높이 계산이 안 돼서 슬라이드를 넘쳐버리는 문제가 있었다. 코드블록을 만나면 무조건 새 페이지로 분기하고, 빈 페이지에서도 높이가 넘치면 "코드 전체 보기" 버튼으로 교체하는 방식으로 해결했다.
버튼을 누르면 기존의 FullscreenView와 동일한 전체화면 코드 뷰어가 열린다.
DOM 클로닝 환경이라 React 이벤트를 쓸 수 없어서, data-code-html과
data-code-language 속성에 원본 코드를 저장해두고, 슬라이드 렌더 후
addEventListener로 클릭 리스너를 직접 부착하는 방식을 사용했다.
네비게이션 오버레이가 버튼 클릭을 가로채는 문제도 있었다. 부모에
pointer-events-none을 걸고 양쪽 1/4 영역만 pointer-events-auto로 설정해서,
가운데 영역의 버튼이 클릭되도록 수정했다.
이미지도 코드블록과 같은 문제가 있었다. 두 이미지가 한 슬라이드에 겹쳐 나오는
문제. 이미지(figure 요소)를 만나면 무조건 새 페이지로 분기하고,
max-height를 슬라이드 가용 높이에서 캡션 높이를 뺀 값으로 제한해서 비율을
유지하며 축소되도록 했다.
글 하단의 PostExplorer 컴포넌트가 프레젠테이션 슬라이드에 포함되면서
모바일에서 잘리는 문제가 있었다. 프레젠테이션에서 보여줄 필요가 없는 요소라
data-presentation-skip 속성을 추가해서 슬라이드 추출 시 제외시켰다.
마지막 슬라이드에서 오른쪽을 누르면 바로 프레젠테이션이 종료되도록
goToNext에 종료 로직을 추가했다. 이 과정에서 두 가지 버그를 잡았다.
setCurrentSlide updater 안에서 onClose()를 호출하면 렌더 중 부모
컴포넌트의 setState가 트리거되는 React 경고가 발생한다. updater 밖에서
currentSlide 값을 직접 비교하는 방식으로 수정. - 프레젠테이션이 닫히면서
포탈이 제거되면, 같은 이벤트 사이클에서 아래 DOM의 링크나 버튼이 눌려버리는
버블링 문제. requestAnimationFrame으로 다음 프레임에서 종료하도록 지연시켜
해결.모바일에서 화면을 탭할 때마다 "놓지 마세요..."가 잠깐 깜빡이는 문제가 있었다. 롱프레스 판단 전에 최소 300ms의 threshold를 두어, 빠른 탭에서는 메시지가 아예 표시되지 않도록 수정했다.
하단 바도 손봤다. 페이지 카운터를 absolute left-1/2 -translate-x-1/2로
고정해서 좌측 텍스트 길이와 무관하게 항상 중앙에 위치하도록 했고, 모바일 하단
바 높이를 48px에서 36px로 축소해서 콘텐츠 영역을 넓혔다.
모바일 브라우저에서 프레젠테이션 모드를 열면 주소창과 하단 네비게이션 바 뒤로 콘텐츠가 가려지는 문제가 있었다. 원인은 두 가지였다.
첫째, 컨테이너 크기에 100vh/100vw를 사용하고 있었는데, 모바일 브라우저에서
100vh는 브라우저 UI를 포함한 전체 뷰포트 높이다. 실제로 보이는 영역보다
크다. 100dvh/100dvw(dynamic viewport height)로 교체해서 브라우저 UI가
차지하는 영역을 제외한 실제 보이는 높이만 사용하도록 수정했다.
둘째, 슬라이드 높이 계산에서 window.innerWidth를 쓰고 있었는데, 이것도
브라우저 UI를 고려하지 않는다. window.visualViewport을 우선 참조하도록
변경해서 실제 가용 영역을 정확하게 반영하도록 했다.
뷰포트 문제를 수정한 뒤에도 콘텐츠가 하단 바에 너무 붙어 보이는 문제가 있었다.
getFillRatio()의 비율을 전체적으로 낮춰서 여유 공간을 확보했다.
| 화면 높이 | 이전 | 변경 |
|---|---|---|
| 600px 이하 (모바일) | 0.92 | 0.82 |
| 601~900px (노트북) | 0.62 | 0.55 |
| 901~1200px (데스크탑) | 0.72 | 0.60 |
| 1200px 초과 (대형) | 0.65 |
스터디 자료처럼 Paragraph와 ul이 번갈아 나오는 구조에서 슬라이드가 넘치는
문제가 있었다. 기존에는 리스트 전체 높이가 availableHeight를 초과할 때만
li 단위 분할이 동작했는데, 앞에 다른 콘텐츠가 있어서 합산 높이가 넘치는
경우에는 리스트를 통째로 넣어버렸다.
분할 조건을 currentHeight + elementHeight > availableHeight로 완화해서,
현재 슬라이드에 넣으면 넘치는 경우에도 li 단위로 분할되도록 수정했다.
리스트가 가용 높이의 50% 이상일 때만 기존 슬라이드를 먼저 flush하는 가드도
추가했다.
포스트 탐색기에서 "Day 10"이 "Day 1" 바로 뒤에 정렬되는 문제가 있었다.
localeCompare가 문자열 사전순 정렬을 하기 때문에 "1" 다음에 "10"이 오고 그
다음 "2"가 오는 순서였다. localeCompare에 numeric: true 옵션을 추가해서
숫자 부분을 수치 기준으로 비교하도록 수정했다. Day 1, Day 2, ..., Day 9, Day
10 순서로 정렬된다.
| 0.55 |