[React] 인프라 설정으로 CORS 이슈 피하고 화면 캡쳐하기 (html2canvas, puppeteer, AWS S3)
리액트에서 화면 캡쳐하기
제가 받은 요구 사항은 아래와 같습니다.
1. 화면에 랜더링된 부분을 캡쳐하여 이미지로 저장
2. 해당 화면에 접속하지 않아도 이미지 저장하는 기능
위 개발을 진행하면서 화면 캡쳐 시 발생할 수 있는 오류 상황과 해결방안을 알아보겠습니다.
1. html2canvas
랜더링 된 화면을 캡쳐하고자 할 때 가장 간단하게 사용할 수 있습니다.
import { saveAs } from 'file-saver';
import html2canvas from 'html2canvas';
const downloadImage = async () => {
const modalElement = document.getElementById('your-element-id');
if (modalElement) {
const canvas = await html2canvas(modalElement, { useCORS: true });
canvas.toBlob((blob) => {
if (blob) saveAs(blob, 'image.png');
});
}
};
하지만 아래와 같은 이슈가 있습니다.
- 만일 스캔하는 영역에 AWS S3 이미지가 포함되어있다면 빈 화면으로 캡쳐됨.
WHY? 캡쳐 시 브라우저에 랜더링 된 그대로 캡쳐하는 것이 아닌 내부적으로 재요청 및 재구성하여 이미지를 가져오며 S3 이미지는 CORS 이슈로 불러올 수 없어 빈 화면이 캡쳐된다.
- 제가 이용한 해결 방법 : CloudFront를 붙인다.
- 위치 : CloudFront -> 배포 -> 동작 -> 이미지 경로 있으면 편집 없으면 새로 추가(ex. /images/*) -> 캐시 키 및 원본 요청
- Cache policy and oirgin request policy는 드롭다운에서 S3 찾아서 선택
- 하단 사진은 Legacy cache settings 방법. Access-Control-Allow-Origin헤더를 추가.
나중에 안 사실이지만 S3에서도 해당 헤더를 설정해줄 수 있다고 합니다. CF가 필요 없는 분은 S3에서 설정해서 사용하시면 될 것 같습니다.
2. puppeteer
해당 화면에 접속하지 않고 이미지로 다운 받기 위해서는, 서버에서 Headless 브라우저를 띄워서 화면을 캡쳐합니다.
const capturePage = async (url: string): Promise<string> => {
try {
// puppeteer 로딩
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
executablePath: '/usr/bin/chromium-browser',
});
// 브라우저를 열어 url로 이동
const page = await browser.newPage();
await page.goto(url);
await page.setViewport({ width: 500, height: 740 });
// puppeteer의 screenshot 기능을 이용하여 캡쳐합니다
const screenshot = await page.screenshot({ encoding: 'base64' });
await browser.close();
return screenshot;
} catch (e) {
console.error('Error in capturePage', e);
return '';
}
};
이 때 주의할 점은, 배포시 실행 서버 환경, Docker 등에 브라우저가 설치되어 있어야한다는 점입니다. (Docker등)
Dockerfile에서 아래와 같이 추가합니다.
...
FROM node:18-alpine
# 아래 코드를 추가합니다.
RUN apk add --no-cache udev ttf-freefont chromium
...
이외 이슈는 다음과 같이 해결할 수 있습니다.
- 화면의 일부가 잘려서 출력되는 문제
해결 방법 : 뷰포트 설정으로 출력 화면 크기를 늘린다. - HTTP 환경에서 이미지가 빈화면으로 다운로드 되는 문제 (localhost 해당x)
TLS(흔히 말하는 SSL) : 개발 서버가 https가 아닐 경우 이미지 다운로드를 브라우저 레벨에서 차단함. (ex. 저의 경우는 테스트 서버라 EC2의 IP 주소를 그대로 사용하였더니 발생하였습니다.)
해결방법 : route53 등으로 도메인 연결하여 TLS 추가
(+)2024.10.2 추가
어느날 갑자기 기능이 작동하지 않는 문제가 있었습니다.
알고보니 executablePath: '/usr/bin/chromium-browser' 가 아닌 '/usr/bin/chromium' 경로에 설치가 되어있었습니다. 이전에는 왜 됬던건지 모르겠지만...puppeteer의 실행 경로를 한 번 더 살펴보면 좋을 것 같습니다.
참고 링크
S3 CORS 헤더 관련 이슈 해결방법 (html2canvas, lottie)
AWS S3는 Simple Storage Service를 줄여 S3라고 부른다.이 S3는 여러 용도로 쓰이는데 대표적으로 아래 세가지 용도로 주로 사용된다.정적 리소스 저장용 (웹 하드 느낌?)정적 웹 페이지 및 콘텐츠 호스팅
velog.io
AWS CORS 이슈 대응하기 feat. html2canvas
오늘은 최근 진행한 프로젝트에서 겪었던 AWS C3, CloudFront 관련 CORS 이슈와 html2canvas 라이브러리의 캐시 문제로 인한 CORS 이슈에 대해 이야기하겠습니다.
medium.com
3. https://ingnoh.tistory.com/87
[Node.js] puppeteer docker 관련 issue
참고 https://pptr.dev/ pptr.dev Troubleshooting | Tools for Web Developers | Google Developers Troubleshooting Guide developers.google.com puppeteer로 크롤링을 연습하던 중 다음과 같은 이슈가 있었다. 1. docker-compose up시 node main.
ingnoh.tistory.com