320x100
반응형
dev.note / WebView / image-capture
html2canvas
modern-screenshot
iOS WebView
android
saveBase64Data
앱인토스
WebView 이미지 캡처 & 저장 완전 정복
html2canvas vs modern-screenshot 비교, canvas 태그 호환성, 폰트 CORS, iOS 권한 처리까지 — WebView 환경에서 이미지를 저장하는 과정에서 마주치는 모든 함정을 정리했습니다.
1. 전체 흐름 — 3단계로 이해하기
WebView에서 화면을 이미지로 저장하는 과정은 크게 3단계로 나뉩니다. 각 단계마다 별개의 기술적 문제가 존재하기 때문에, 어느 단계에서 오류가 났는지를 먼저 파악하는 것이 중요합니다.
STEP 01
화면 캡처
HTML → Canvas
→
STEP 02
이미지 데이터
Base64 / Blob
→
STEP 03
기기에 저장
Native Bridge
01
캡처 라이브러리 선택
canvas 태그 포함 여부에 따라 라이브러리가 달라집니다. 잘못 선택하면 iOS에서 빈 화면이 나옵니다.
02
폰트 CORS 처리
외부 CDN 폰트는 캡처 시 CORS 오류로 기본 글꼴로 대체됩니다. 로컬 폰트가 유일한 해결책입니다.
03
플랫폼별 저장 처리
iOS는 권한 요청이 먼저 필요합니다. Android는 브리지 함수를 바로 호출해도 됩니다.
2. 캡처 라이브러리 비교 — html2canvas vs modern-screenshot
브라우저는 "화면을 스크린샷으로 찍는" 네이티브 API를 제공하지 않습니다. 때문에 라이브러리가 HTML 구조를 읽어 직접 이미지로 변환합니다. 방식의 차이가 iOS 호환성을 크게 좌우합니다.
| 라이브러리 | 변환 방식 | 장점 | 주의사항 |
|---|---|---|---|
| html2canvas | HTML을 읽어서 <canvas>에 직접 재드로우 | 레퍼런스 풍부 | 내부 iframe 생성 방식 사용. 기존 <canvas> 태그(rough.js 등)가 있으면 iOS에서 빈 화면 렌더링 |
| modern-screenshot | HTML → SVG → 이미지 변환 | iframe 미사용 canvas 호환 | 외부 폰트(Google Fonts) CORS로 읽기 실패. 로컬 폰트 필수 |
// 어떤 라이브러리를 선택해야 하나
프로젝트에 <canvas> 태그가 존재하는가?
(rough.js, chart.js, Three.js 등 포함)
(rough.js, chart.js, Three.js 등 포함)
YES
modern-screenshot 사용
NO
html2canvas 사용 가능
CAUTION
html2canvas는 내부적으로 대상 DOM을 iframe에 복제한 뒤 캔버스로 그립니다. 이 과정에서 이미 존재하는 <canvas> 요소의 픽셀 데이터를 iOS 보안 정책상 읽지 못하는 경우가 발생합니다. 결과는 해당 영역이 완전히 비어있는 이미지입니다.
3. 폰트 CORS 문제 — 글씨체가 캡처에서 깨지는 이유
캡처 라이브러리는 HTML 구조와 함께 CSS에 지정된 폰트도 이미지에 포함시키려 합니다. 이때 폰트 파일의 출처(origin)가 결정적입니다.
CASE 1 — Google Fonts (실패)
@import url(fonts.googleapis.com)
↓
캡처 라이브러리: 외부 서버 요청 시도
↓
CORS 오류: 폰트 파일 로드 실패
↓
결과: 기본 글꼴(serif/sans-serif)로 대체
CASE 2 — 로컬 폰트 (성공)
@font-face { src: /fonts/xxx.woff2 }
↓
캡처 라이브러리: 같은 origin 요청
↓
CORS 제한 없음: 파일 정상 로드
↓
결과: 지정 글꼴 정상 렌더링
KEY POINT
이미지 캡처 기능이 있는 프로젝트는 폰트 파일을 반드시 public/fonts/ 디렉토리에 직접 포함시키세요. 런타임에 CDN에서 로드하는 방식은 캡처 시 동작하지 않습니다.
4. 저장 처리 — iOS와 Android가 다른 이유
웹 페이지는 보안 정책상 사용자 기기의 파일 시스템에 직접 접근할 수 없습니다. 앱인토스 같은 네이티브 WebView 환경은 JavaScript Bridge를 통해 이 제약을 우회합니다. 그러나 iOS와 Android의 권한 모델이 달라 처리 순서가 다릅니다.
Android — 바로 저장 가능
1 saveBase64Data() 호출
2 사진첩 저장 완료
iOS — 권한 요청 선행 필수
1 requestPermission('photos') 호출
2 시스템 권한 다이얼로그 표시
3 사용자 허용
4 saveBase64Data() → 저장 완료
CAUTION
iOS에서 권한 요청 없이 saveBase64Data()를 바로 호출하면 저장이 무음 실패합니다. 에러도 없이 아무 일도 일어나지 않는 것처럼 보여 디버깅이 어렵습니다. 반드시 권한 획득 후 저장 함수를 호출하세요.
5. 핵심 체크리스트
WebView 이미지 저장 기능을 구현할 때 놓치기 쉬운 항목들을 단계별로 정리했습니다.
STEP 01
canvas 태그 유무 확인 후 캡처 라이브러리 선택
canvas 있음 → modern-screenshot / canvas 없음 → html2canvas 가능
STEP 02
폰트 파일을 로컬에 직접 배치
public/fonts/ 경로에 woff2 파일 포함. Google Fonts CDN은 캡처 시 동작 안 함
STEP 03
플랫폼 분기 처리로 저장 로직 구현
iOS: requestPermission('photos') 먼저 → 허용 후 saveBase64Data() / Android: 바로 saveBase64Data()

320x100
'Tech Notes' 카테고리의 다른 글
| Google 발송 메일 SPF 실패 원인 분석 및 해결 (0) | 2026.02.25 |
|---|---|
| Docker란? 언제 쓰는 걸까? 🐳 (0) | 2026.02.20 |
| HTML5란? 웹에서 게임까지, 플러그인 없이 다 되는 시대 (0) | 2026.02.15 |
| [앱인토스] 앱 테스트 시 localStorage 삭제 방법 (0) | 2026.02.14 |
| [앱인토스] Error: java object gone (0) | 2026.02.14 |