현재 상황
캔버스 데이터 저장 형식을 .svg에서 .json으로 변경하면서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 (기준)
회고
로딩 속도는 많이 개선을 했지만, 그것이 엄청 유의미한 영향을 미치지는 못했습니다. 나중에서야 알게 되었지만, 데이터 로딩보다는 프론트 단에서 객체들을 캔버스에 띄우는 작업이 유의미하게 오래 걸리고 있었습니다.
이 경험을 통해 문제의 원인을 제대로 파악하는 것 문제 해결에 얼마나 중요한 지를 알게되었습니다.