현재 상황
캔버스 데이터 저장 형식을 .svg에서 .json으로 변경하면서1 캔버스 렌더링 속도가 눈에 띄게 느려지는 문제가 발생했습니다. 기존에도 완벽하진 않았지만, 이제는 불편할 정도로 캔버스 랜더링이 느려졌습니다.
저장 형식을 포함한 저장 로직의 대대적인 수정을 제가 담당했습니다. 그래서 더 성능이 낮아진 결과가 나왔을 때 너무나도 당황스러웠으나, 어떻게든 수습하고자 방법을 찾았습니다.
개선 방향 1: 로딩
처음에는 부끄럽게도, ‘렌더링’ 속도가 느려졌다고는 생각하지 못했습니다. 대신 캔버스 데이터를 가져오는 ‘로딩’ 속도를 줄여야겠다는 생각을 우선적으로 하였습니다.
이때 렌더랑과 로딩의 차이도 제대로 알게 되었는데요. 저의 경우에 로딩과 렌더링을 아래와 같이 정리해볼 수 있을 것 같습니다.
- 로딩: 캔버스 데이터를 서버에서부터 받아오는 작업
- 렌더링: 가져온 캔버스 데이터를 사용자가 볼 수 있도록 실제 캔버스에 그리는 작업
로딩 속도를 올리기 위해 떠올린 것은 아주 유명하고도 대표적인 방법인 ‘캐싱’이었습니다. 그래서 저는 차후 폴리싱 단계의 과업으로 미뤄두었던 IndexedDB를 사용하기로 했습니다.
기존 방식: Network + 브라우저 캐시
기존의 방식은 서버 요청을 통해 데이터를 직접 받아오거나, 브라우저에서 캐시해 둔 데이터를 받아오는 방식입니다. 브라우저는 방문했던 웹 페이지의 이미지, 스크립트 등 리소스를 로컬 컴퓨터에 임시 저장(캐시)하고, 다시 방문할 때 서버에서 모두 다운로드하는 대신 이 캐시를 사용합니다.
수정 방식 1: + Indexed DB
웹 브라우저 저장소 기술 중 하나이며, 비슷한 기술로 localStorage 같은 것이 있습니다. localStorage 대신에 IndexedDB를 사용한 이유는 아주 많습니다.
| LocalStorage | IndexedDB | ||
|---|---|---|---|
| 용량 | 5-10MB | < | 디스크의 50~80% (브라우저별 상이) |
| 처리 방식 | 동기 + 메인 스레드 | 비동기 + 별도 스레드 | |
| 트랜잭션 | X | O | |
| 사용 | 쉬움 | 복잡함 |
더 다양한 특징이 있겠지만, 주요 선택 이유는 위의 내용들입니다. 사용 난이도가 복잡한 것을 제외하고는 무조건 IndexedDB를 써야만하는 스펙이었어요.
가장 큰 문제는 용량입니다. 변환을 완료한 캔버스는 기본 설정상으로 약 1000개의 도트 객체를 가지게 됩니다. 그보다 더 많아질 수도 있고요. 1개의 도트 객체 데이터가 약 1KB의 용량을 차지합니다.
처리방식도 경우 동기 방식이면 큰일이 납니다. 자동저장을 구현하게 될 텐데, 메인 스레드에서 동기 방식으로 처리되면 자동 저장 시마다 캔버스 편집이 먹통이 될테니까요. 한 캔버스를 저장하는데 1MB의 용량이 든다면 localStorage에는 많아봤자 10개의 씬밖에 저장하지 못할 겁니다.
일반적으로 대용량 데이터, 문자열을 제외한 다양한 타입의 데이터를 저장할 때는 IndexedDB를 사용하는 것이 일반적이라는데 위의 특징들 때문인 듯 싶습니다.2
결과
IndexedDB를 사용했을 때, 캔버스 데이터 로딩 속도는 약 90% 단축 됩니다.
테스트 환경은 다음과 같습니다.
- 컴퓨터: 제 노트북
- 캔버스 객체 수: 5000개의 Circle 객체
결과는 다음과 같습니다
- IndexedDB: 21.5-23.7ms (90% 향상)
- Network: 153.6-362.3ms (기준)
수정 방식 2: + Memory Cache
이래도 사용자 불편감이 심해서 LRU방식의 메모리 캐시를 추가했습니다.
결과
IndexedDB에 캐시를 추가했을 때, 캔버스 데이터 로딩 속도는 전체적으로 약 99% 단축됩니다.
테스트 환경은 다음과 같습니다.
- 컴퓨터: 제 노트북
- 캔버스 객체 수: 5000개의 Circle 객체
결과는 다음과 같습니다
- Memory Cache: 0.2-0.3ms (99.9% 향상)
- IndexedDB: 21.5-23.7ms (90% 향상)
- Network: 153.6-362.3ms (기준)