
웹 서비스에서 '내 결과물 공유하기', '영수증 이미지 다운로드', '포토 카드 저장' 같은 기능을 구현해야 할 때가 있습니다. 보통은 서버에서 이미지를 생성(렌더링)해서 클라이언트에게 내려주는 방식을 생각하기 쉽습니다.
하지만 이 방식은 치명적인 단점이 있는데,
- 서버 부하: 이미지 렌더링은 CPU와 메모리를 크게 소모함,,
- 비용 증가: 트래픽이 몰린다면? 서버 비용과 데이터 전송 비용이 기하급수적으로 늘어남
"이 작업을 서버가 아니라 사용자 브라우저에서 직접 하게 만들면 어떨까?" 이때 떠올려볼수있는 라이브러리는 html2canvas!입니다. 사용자의 기기 자원을 활용하여 화면의 DOM 요소(HTML/CSS)를 캔버스(Canvas)로 캡처해 줍니다.
즉, 트래픽이 100만이 몰려도 이미지 생성에 드는 서버 비용은 단돈 0원!!!!!!!
설치 방법
프로젝트 환경에 맞춰 설치해 줍니다.
NPM / Yarn을 사용하는 환경 (React, Vue 등)
# 쉘 bash zsh etc...
npm install html2canvas
# 또는
yarn add html2canvas
일반 HTML 환경 (CDN 방식)
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
기본 사용법: 화면 캡처하기
가장 기본적인 원리는
- 캡처하고 싶은 HTML 요소를 선택
- html2canvas 함수에 해당 요소를 넘겨주기
- 결과물로 나온 canvas 객체를 다루기~
HTML 예시
<!-- 캡처할 영역 (id="capture-area" 지정) -->
<div id="capture-area" style="padding: 20px; background-color: #f0f0f0; border-radius: 10px;">
<h2>나의 멋진 포토 카드</h2>
<p>이 영역이 그대로 이미지로 변환됩니다!</p>
</div>
<!-- 실행 버튼 -->
<button id="capture-btn">이미지 캡처하기</button>
<!-- 결과물을 보여줄 빈 공간 -->
<div id="result-area"></div>
JavaScript
// NPM 환경이라면 상단에 import 필요: import html2canvas from 'html2canvas';
const captureBtn = document.getElementById('capture-btn');
const captureArea = document.getElementById('capture-area');
const resultArea = document.getElementById('result-area');
captureBtn.addEventListener('click', () => {
// html2canvas는 Promise를 반환합니다.
html2canvas(captureArea).then((canvas) => {
// 캡처된 canvas를 결과 영역에 추가하여 화면에 보여줌
resultArea.appendChild(canvas);
});
});
React에서 사용한다면, 돔말구 useRef를 활용하기
React는 가상 DOM을 사용하기 때문에, 특정 요소를 직접 가리키기 위해서는 useRef를 사용해야 합니다.
가장 표준적인 구현 패턴을 소개합니다.
/// 카드 캡쳐 js
import React, { useRef } from 'react';
import html2canvas from 'html2canvas';
const CardCapture = () => {
// 1. 캡처하고 싶은 영역을 지정할 ref 생성
const printRef = useRef(null);
const handleDownload = async () => {
const element = printRef.current;
if (!element) return;
try {
// 2. html2canvas로 캔버스 생성 (옵션 추가 가능)
const canvas = await html2canvas(element, {
scale: 2, // 화질을 2배로 높임
useCORS: true, // 외부 이미지 로드 허용
});
// 3. 이미지 데이터 URL로 변환
const data = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = data;
link.download = 'my-awesome-card.png';
link.click();
} catch (error) {
console.error('이미지 생성 실패:', error);
}
};
return (
<div>
{/* 캡처하고 싶은 영역에 ref 연결 */}
<div ref={printRef} style={{ padding: '20px', border: '1px solid #ccc', backgroundColor: '#fff' }}>
<h1>나만의 포토카드</h1>
<p>이 영역의 내용이 이미지로 저장됩니다.</p>
<img src="https://example.com/profile.jpg" alt="프로필" style={{ width: '100px' }} />
</div>
<button onClick={handleDownload} style={{ marginTop: '20px' }}>
이미지로 저장하기
</button>
</div>
);
};
export default CardCapture;
캡처 후 이미지 파일로 다운로드하기
보통 화면에 캔버스를 띄우는 것보다, 사용자 기기에 이미지 파일(.png)로 저장해 주는 것이 최종 목적입니다.
이를 위해서는 캔버스를 데이터 URL로 변환하고, 보이지 않는 <a> 태그를 만들어 클릭 이벤트를 발생시켜야 합니다.
captureBtn.addEventListener('click', async () => {
try {
// 1. 캡처할 영역을 캔버스로 변환
const canvas = await html2canvas(captureArea);
// 2. 캔버스를 Base64 형태의 이미지 데이터(URL)로 변환
const image = canvas.toDataURL('image/png');
// 3. 다운로드를 위한 가상의 <a> 태그 생성
const link = document.createElement('a');
link.href = image;
link.download = 'my-card-image.png'; // 다운로드될 파일 이름 지정
// 4. 강제로 클릭 이벤트 발생시켜 다운로드 실행
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('이미지 캡처 중 오류 발생:', error);
}
});
위에는 다운로드 기능 코드입니다
실무에서 꼭 알아야 할 꿀팁과 주의사항
막상 적용해 보면 깨지거나 흐리게 나오는 등 예상치 못한 문제를 만날 수 있습니다.
1. "이미지가 너무 흐리게 나와요" (고화질 캡처 방법)
모바일 기기나 레티나 디스플레이에서는 기본 캡처 시 이미지가 흐려질 수 있습니다.
이때 scale 옵션을 주어 해상도를 높여야 합니다.
// js 코드
html2canvas(captureArea, {
scale: 2, // 기본값 1에서 2~3으로 올리면 화질이 좋아집니다. (단, 용량과 시간 증가)
// window.devicePixelRatio 를 사용하면 기기 환경에 맞게 자동으로 최적화됨
// scale: window.devicePixelRatio
}).then(canvas => { ... });
2. 외부 이미지(프로필 사진 등)가 엑스박스로 떠요 (CORS 문제)
내 도메인이 아닌 외부 서버의 이미지(예: AWS S3, 카카오 프로필 등)가 캡처 영역에 포함되어 있으면, 브라우저 보안 정책(CORS) 때문에 캔버스가 오염(Tainted) 처리되어 캡처가 실패합니다.
이 경우 외부 이미지 서버에서 CORS 허용 헤더를 열어주어야 하며, html2canvas 옵션에 useCORS: true를 추가해야 합니다.
// js 코드
html2canvas(captureArea, {
useCORS: true, // 외부 이미지 로드 허용
allowTaint: false // 교차 출처 이미지 오염 방지
}).then(...)
3. 지원하지 않는 CSS 속성이 있어요
html2canvas는 브라우저의 렌더링 엔진을 100% 똑같이 구현한 것이 아니어서 최신 CSS 속성(복잡한 box-shadow, filter, grid의 일부 속성 등)은 무시되거나 깨질 수 있습니다.
- 해결책: 캡처해야 하는 영역은 가급적 안전한 CSS (Flexbox, 기본 속성 등) 위주로 스타일링하는 것이 좋습니다.
4. 이미지가 다 로드되기 전에 캡처돼버려요
영역 안에 큰 이미지가 렌더링 중인데 캡처 버튼을 누르면 이미지가 빠진 채로 캡처됩니다.
웹 폰트가 늦게 로딩되는 경우에도 폰트가 깨집니다.
- 해결책: 모든 이미지와 웹 폰트가 완전히 로드된 것을 보장한 후에 html2canvas를 실행해야 합니다.
html2canvas는 프론트엔드 레벨에서 리소스를 아끼면서도 사용자에게 멋진 경험(포토카드, 결과 저장 등)을 제공할 수 있는 방법이죠.
CORS 이슈와 지원하지 않는 CSS 한계만 명확히 인지하고 설계한다면, 서버 개발자의 개입 없이, 비용 0원으로 수백만 트래픽의 이미지 생성 기능을 처리 할 수 있슴다.
'Frontend Note' 카테고리의 다른 글
| [FE] 스크롤 한번에 4초 멈춤 대참사..?! 소개페이지 성능 최적화를 해보자 #Layout Thrashing #Forced Reflow #브라우저 렌더링 파이프라인 (1) | 2026.07.14 |
|---|