| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 소셜로그인
- 인증
- REST API
- python
- 마이그레이션
- JWT
- 메일자동화
- Android
- typescript
- sqlite
- React
- playwright
- javascript
- Kotlin
- 상태관리
- 온디바이스AI
- 크롤링
- FastAPI
- electron
- 아키텍처
- php
- 프론트엔드
- 보안
- next.js
- 도메인설계
- contextBridge
- 백엔드
- github actions
- Vite
- 배포
- Today
- Total
HM소프트
실시간 미리보기 + html2canvas 이미지 내보내기 — 텍스트 교체 도구 (3) 본문

Canvas 실시간 미리보기와 PNG 내보내기를 한 렌더 함수로 통일해 화면과 결과물을 일치시킨 과정을 다룬다. 화면 캡처 라이브러리를 버리고 원본 픽셀을 읽어 배경색을 추정해 덮고, 오츠 이진화로 글자색을 추정하며, 폰트 크기 이진탐색·디바운스·렌더 버전 검사로 어긋남을 막은 이미지 텍스트 교체 도구 구현 기록이다.
3편: 미리보기와 내보내기
1편 엔진, 2편 위저드/상태에 이어 시리즈 마지막이다. 사용자가 입력한 것이 결과물로 떨어지는 부분, 실시간 미리보기와 PNG 내보내기를 다룬다.
실시간 미리보기 패널
이 도구의 사용감을 좌우하는 부분이다. 사용자가 텍스트를 바꾸거나 항목을 옮길 때마다, 결과가 어떻게 보일지 즉시 보여줘야 한다. "바꾸고 → 결과 보기" 버튼을 누르는 흐름이면 답답하다.
그래서 미리보기를 별도 패널로 항상 띄워두고, 2편의 EditorContext 상태가 바뀌면 곧바로 반영되게 했다. React의 상태 → 렌더링 흐름을 그대로 활용한 것이라, 상태가 한 곳(Context)에 모여 있는 게 여기서 또 도움이 됐다.
function PreviewPanel() {
const { state } = useEditor(); // 상태가 바뀌면 자동 리렌더
return (
<div className="preview">
<img src={state.baseImage} />
{state.overlayNodes.map((n) => (
<span key={n.id} style={{
position: "absolute",
left: n.x, top: n.y, width: n.width, height: n.height,
}}>{n.replacement}</span>
))}
</div>
);
}
원본 이미지 위에 오버레이 텍스트를 절대 위치로 얹는다(1편의 오버레이 방식 그대로). 입력이 바뀌면 React가 알아서 다시 그린다.
내보내기 — html2canvas로 "보이는 그대로"
마지막 단계는 결과물 추출이다. 핵심 요구는 "화면에 보이는 그대로가 이미지로 떨어져야 한다"는 것. 미리보기와 결과물이 다르면 안 된다.
이걸 직접 캔버스에 다시 그리려면, 미리보기의 모든 레이아웃(이미지 + 오버레이 텍스트 위치·폰트)을 캔버스 좌표로 재현해야 한다. 번거롭고 어긋나기 쉽다. 대신 html2canvas를 썼다. 미리보기 DOM 영역을 그대로 캡처해 캔버스로 바꿔준다.
import html2canvas from "html2canvas";
async function exportPng(previewEl: HTMLElement): Promise<Blob> {
const canvas = await html2canvas(previewEl, {
useCORS: true, // 외부 이미지 캡처 허용
scale: 2, // 고해상도 출력
});
return new Promise((resolve) =>
canvas.toBlob((b) => resolve(b!), "image/png")
);
}
scale: 2로 고해상도로 뽑고, useCORS로 이미지가 캡처에 포함되게 했다. 미리보기에서 본 레이아웃이 그대로 PNG가 된다.
HTML 복사도 추가했다. 결과를 HTML로 복사해, 다른 곳에 붙여 쓸 수 있게 했다.
까다로웠던 점: 화면과 출력의 일치
html2canvas가 편하긴 해도, "화면에서 보이는 것"과 "캡처된 것"이 미묘하게 어긋날 수 있다(폰트 로딩, 외부 이미지 CORS, 스케일). 폰트가 다 로드된 뒤 캡처하고, 이미지 CORS를 맞추고, 스케일을 명시해 이 간극을 메웠다. 결국 "미리보기 = 결과물"이라는 원칙을 지키는 게 목표였다.
정리하며
- 실시간 미리보기: 상태(Context)가 바뀌면 즉시 반영 — 도구의 체감 품질을 만든다.
- html2canvas로 "보이는 그대로" 내보내기: 레이아웃을 다시 그리지 않고 DOM을 그대로 캡처.
- 화면=출력 일치: 폰트 로딩·CORS·스케일을 맞춰 간극을 제거.
시리즈를 마치며
세 편에 걸쳐 이미지 텍스트 교체 도구를 정리했다.
- 1편: 원본을 깎지 않는 비파괴 오버레이 엔진
- 2편: 흩어지기 쉬운 입력을 스텝 위저드 + 전역 Context로
- 3편: 실시간 미리보기와 화면 그대로의 내보내기
OCR(인식)과 오버레이(렌더링)를 분리하고, 상태를 한 곳에 모으고, 출력은 화면을 그대로 캡처한다 — 이 세 결정 덕분에 인식 엔진이나 내보내기 형식을 따로 교체·확장하기 쉬운 구조가 됐다.
'기술 개발일지' 카테고리의 다른 글
| 프로젝트 산출물 자동화 개발기 (1) — 규칙 엔진과 검증 훅을 갖춘 문서 생성 플러그인 (0) | 2026.06.18 |
|---|---|
| AI 코딩 에이전트용 디자인 시스템 플러그인 개발기 (1) — 'AI 티 나는 디자인'을 강제로 깨뜨리기 (0) | 2026.06.15 |
| 스텝 위저드 UX와 전역 상태 설계 — 이미지 텍스트 교체 도구 (2) (0) | 2026.06.12 |
| OCR + 오버레이 렌더링 엔진 만들기 — 이미지 텍스트 교체 도구 (1) (0) | 2026.06.12 |
| Vite + React 빌드·배포 트러블슈팅 — 장부 관리 앱 (2) (0) | 2026.06.12 |