[POM]PDF Viewer 개선하기

서연주·2021년 8월 11일
1

PeaceOfMood

목록 보기
1/2

PieceOfMood에서 일한지 한달 차! 처음으로 맡게된 프로젝트를 마무리하며 기록을 한 번 해볼까 한다..

오늘은 무슨 일이 있었나요

pdf로 공유되는 콘텐츠 특성상 pdf viewer는 굉장히 핵심적인 기능 중 하나였다. 뷰어 구현에 사용하는 라이브러리는 react-pdf.

기존 코드에서는 첫 페이지를 기준으로 그 높이와 너비에 맞추어 모든 페이지를 같은 판형으로 표현하고 있었다. 그러다 첫번째 페이지에 비해 터무니없이 긴 페이지가 있는 자료의 등장으로 pdf viewer 개선의 필요성이 크게 드러났다.

결론적으로 목적은 다양한 판형의 pdf를 자유롭게 표현할 수 있도록 pdf viewer를 개선하는 것! 원래 기한은 일주일이었다.

개발을 시작하기 전...

개발에 들어가기 전, 다양한 pdf의 모양에 맞추어 제대로 동작해야하는 기능들을 정리했었다.


  • 페이지 넘버링
  • 스크롤에 따른 페이지 이동
  • 상단 navigator를 통한 페이지 이동
  • 줌인, 줌아웃
  • 마지막으로 읽던 페이지로 이동

고쳐보자!

우선 일괄적으로 정해져서 렌더링되던 페이지 높이가 다양하게 표시되어야했다. 그러기 위해 백엔드에서 페이지별 가로:세로 비율을 받아와 첫번째 페이지 너비를 이용하여 페이지 높이를 화면에 알맞게 표시하기로 하였다.

페이지 넘버링

기존 코드에서는 각 페이지별 높이를 통일하고 있었기에 단순한 연산으로 현재 페이지 넘버를 표시할 수 있었다.
그러나 개선된 pdf viewer는 그렇지 않으므로 각 페이지까지의 누적 페이지 길이를 계산한 배열을 만들고 그에 따라 페이지 넘버를 파악할 수 있도록 했다.
자꾸 NaN이 나와서 그 데이터를 빼내느라 애를 먹었다.

const calcTotalPageSizeArr = page => {
  //페이지별 길이
  const width = Math.floor(page.width);
  const height = Math.floor(page.height);
  let addedTotalPageSizeArr = totalPageSizeArr;
  if (!Number.isNaN(height) && Math.round(page.height + PAGE_OFFSET + totalPageSizeArr[page.pageIndex]) !== PAGE_OFFSET)
  for (let i = 0; i < numOfPages; i++) {
    addedTotalPageSizeArr.splice(i + 1, 1, Math.round(page.width / pageRatioData[i + 1] + PAGE_OFFSET + totalPageSizeArr[i]));
  }
  setTotalPageSizeArr(addedTotalPageSizeArr);
};

스크롤에 따른 페이지 이동

해당 배열을 이용하면 스크롤에 따른 페이지 이동은 현재 스크롤 위치를 파악하는 api를 사용해서 쉽게 구현이 가능하다.

window.scrollTo(0, posY);

상단 navigator를 통한 페이지 이동

pdf viewer 상단에는 navigator가 있다.

해당 navigator 양옆의 chevron을 누르면 위아래로 페이지가 이동해야하고 중간의 textbox에 원하는 페이지 넘버를 입력하면 해당하는 페이지로 이동해야한다. 이 문제도 누적 페이지 길이를 담은 배열을 이용하면 쉽게 해결할 수 있다.

  • 위아래 이동
const goNextPage = () => {
  if (pageNumber > numOfPages - 1) return;
  setPageNumber(pageNumber + 1);
  window.scrollTo(0,totalPageSizeArr[pageNumber]);
};

const goPrevPage = () => {
  if (pageNumber === 1) return;
  setPageNumber(pageNumber - 1);
  window.scrollTo(0,totalPageSizeArr[pageNumber - 2]);
};
  • 입력 값에 따라 이동
const scrollToPageNumber = (pageNumberInput, e) => {
  const posY = totalPageSizeArr[pageNumberInput - 1]);
  setPageNumber(pageNumberInput);
  setTimeout(() => {
    window.scrollTo(0,posY);
  }, 300);
};

처음에 입력 값에 따른 스크롤 이동을 지시했을 때 자꾸 스크롤이 posY값까지 가지 못하는 문제가 있었다. 이는 setTimeout으로 이동할 시간을 벌어주면 쉽게 해결된다.

줌인, 줌아웃

줌인, 줌아웃 관련 코드는 기존의 코드를 그대로 사용하여 기존과 같이 동작했다.

마지막으로 읽던 페이지로 이동

const onLoadTracePage = () => {
  let posY = 0;
  for (let i = 1; i < pageNumber; i++) {
    posY += Math.floor(pageSize.width / pageRatioData[i] + PAGE_OFFSET);
  }
  setDisableTrace(true);
  setTimeout(() => {
    window.scrollTo(0, posY);
  }, 200);
  setDisableTrace(false);
};

페이지 입력을 통한 스크롤 이동과 동일한 문제가 발생하길래 동일한 해결책으로 해결했다.

이슈 발생!

퇴근이 얼마남지 않은 시간...4명이서 진행한 QA 테스트 당시 디자이너님의 휴대폰에서만 pdf가 로딩이 잘 되지 않는 현상이 있었지만 test 서버가 느려서 그럴 수 있다는 생각으로 회의실을 나왔다. 그래도 혹시나 하는 마음으로 다른 휴대폰으로 확인한 결과...
iPhoneXR에서만 안된다!

부랴부랴 팀원분들의 휴대폰을 총동원해서 검증을 해본 결과...정말 XR에서만 안된다. 사파리에서 개발자도구까지 켜가며 확인해본결과...동시에 너무 많은 것을 로드해서 터졌다!

이슈 해결

한꺼번에 많은 것을 로드해서 문제가 생겼으니 나눠서 렌더링을 하면 될 일이다. 하지만 기존에 사용하던 현재 페이지와 앞뒤의 2페이지만 로드되는 것은 계속해서 오류를 발생시켰고...시간 상 절반씩 나눠서 렌더링이 되도록 했다.

이 덕분에 이슈를 해결하긴 했지만 절반을 넘긴 페이지까지 페이지 넘버가 바뀌기 전까지 하얀 화면을 봐야하는 사용자 경험은 너무 별로였다!
그래서 다시 집으로 돌아가 더 나은 부분 렌더링 방법은 없을지 찾아봤고...결국 찾았다!

기존 페이지 렌더링 방식이 문제를 일으켰던 건 이미 로드됐던 페이지에서 멀어지면 해당 페이지는 null처리 되어 뜨지 않도록 하는 것이 그 이유였는데...
해당 페이지를 기준으로 이미 렌더링된 페이지는 그대로 두고 이후 두 페이지까지 야금야금 렌더링해오는 방식으로 개선했다. 덕분에 사용자는 하얀 화면을 볼 필요가 없어졌다!

const isRender = _pN => {
  //_pN은 현재 페이지 넘버이고 pageNumber는 가장 최근까지 머무른 페이지 넘버이다.
  if (_pN < pageNumber + 3) return true;
  return false;
};

프로젝트는 마무리 되었지만...

프로젝트를 진행하며 기존 pdf viewer의 문제점을 추가적으로 발견하게 되었고 그 중 대부분이 줌인, 줌아웃 기능과 관련된 문제였다.
줌인을 하더라도 원하는 부분을 콕집어 보기가 힘들었고, 왜인지 줌아웃을 여러번 하고 난 뒤에는 줌인은 한 번밖에 할 수 없었다.
pdf viewer에 가로스크롤을 도입한다면 전자는 조금 더 개선할 수 있지 않을까.

고민도 코딩도 야근도 열심히 했던 보람찬 첫 프로젝트였다! 오래도 말썽을 부리던 페이지 넘버링 부분을 해결했을 때, 그리고 렌더링 부분을 해결했을 때 정말 기뻤다. 특히 후자는 그 구리구리한 사용자 경험을 경험한 실사용자가 아무도 없었다는 점에서 특히나 기뻤다! 다음 프로젝트도 더 나은 사용자 경험을 위해 화이팅~!!

profile
pizz@ttang

0개의 댓글