스크린샷 실시간 미리보기, Canvas 합성 성능 잡기
미리보기가 버벅일 때 Canvas를 매번 통째로 다시 그리지 않고 비트맵 캐싱·rAF·devicePixelRatio 보정으로 성능을 잡은 1인 개발 빌드로그예요.
앱 스크린샷을 언어별로 만들다 보면, 문구 하나 바꿀 때마다 미리보기가 한 박자씩 늦게 따라오는 순간이 옵니다. 혹시 슬라이더를 움직였는데 화면이 뚝뚝 끊긴 적 없나요? LocaleShots라는 스크린샷 도구를 직접 만들어 보니, 이 버벅임의 원인은 대부분 Canvas를 매 입력마다 통째로 다시 그리는 데 있었어요.
왜 Canvas였나
배경 이미지 위에 기기 목업, 제목 텍스트, 로고를 겹쳐서 한 장으로 합성해야 했어요. 이런 레이어 합성은 여러 DOM 요소를 쌓는 것보다 Canvas 한 장에 drawImage로 그리는 편이 내보내기까지 깔끔했죠. 문제는 미리보기였어요. 입력창에 글자를 칠 때마다 전체 캔버스를 다시 그리면, 배경 이미지 디코딩까지 매번 반복돼서 눈에 보일 만큼 밀렸어요.
프레임 예산부터 계산
60fps를 목표로 하면 한 프레임에 쓸 수 있는 시간은 약 16.7ms(1000 나누기 60)뿐이에요. 배경을 매번 새로 디코딩하면 이 예산을 훌쩍 넘겨서, 입력이 끊겨 보였어요.
세 가지만 손봤어요
먼저 이미지 캐싱부터 손봤어요. 배경과 목업처럼 자주 안 바뀌는 이미지는 createImageBitmap으로 한 번만 디코딩해 두고, 그릴 때는 그 비트맵을 재사용했어요. 적용해 봤더니 텍스트만 바꿀 때 배경을 다시 읽는 낭비가 사라졌어요.
그다음은 그리기 묶기예요. 입력 이벤트마다 바로 그리지 않고 requestAnimationFrame으로 다음 프레임에 한 번만 그리게 했어요. rAF는 화면 주사율에 맞춰 초당 최대 60회로 호출을 눌러 주니까, 빠르게 타이핑해도 그리기 횟수가 프레임 수를 넘지 않았어요. 돌려 보니 연속 입력에서 확실히 부드러워졌어요.
마지막은 선명도였어요. 레티나 화면은 devicePixelRatio가 2라, 캔버스 픽셀 크기를 CSS 크기의 2배로 잡고 컨텍스트를 scale(2, 2)로 맞춰야 글자가 안 뭉개졌어요. 처음엔 이걸 빼먹어서 미리보기가 흐릿했는데, 고쳐 보니 내보낸 결과물과 미리보기가 같은 선명도로 맞았어요.
- 비트맵 캐싱
- rAF로 그리기 묶기
- devicePixelRatio 보정
정리하면
언어 5개에 화면 6장이면 30장을 뽑아야 하는데, 미리보기가 매끄러워지니 문구를 바꿔 가며 확인하는 반복이 훨씬 줄었어요. 큰 라이브러리를 붙이기 전에, 다시 그리는 범위를 줄이고 그리기를 프레임에 묶는 이 3단계만으로도 체감이 달라졌어요.
저는 플랜씨(plan-c)나 플랜비(plan-b) 같은 앱을 앱스토어에 올릴 때 쓰려고 이 도구를 만들었는데, 스크린샷을 여러 언어로 준비하는 반복을 조금이라도 줄이고 싶었어요. 작은 도구라도 미리보기가 손에 붙는 느낌이면, 만드는 사람이 지치지 않더라고요.
테스트해 보면 알겠지만, 성능 문제는 대개 "무엇을 안 그려도 되는가"에서 답이 나와요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.