안녕하세요. Unno FE 노수민입니다.🌸 웹 개발에서 브라우저 호환성 문제는 늘 도전적인 과제입니다. 최신 웹 API를 사용하고 싶지만 모든 브라우저에서 동작하지 않는 경우, 개발자는 대안을 모색해야 합니다. 이번에는 색상 스포이드 기능을 구현하면서 겪었던 기술적 고민과 해결 과정을 공유하고자 합니다.
프로젝트에서 사용자가 화면 어디서든 색상을 추출할 수 있는 스포이드 기능이 필요했습니다. 가장 먼저 떠올린 방법은 최신 웹 기술인 EyeDropper API
였습니다.
const handlePickColor = async () => {
if (!window.EyeDropper) {
sweetAlertUtil.error(
'현재 브라우저에서는 스포이드 기능을 사용할 수 없어요.',
'원활한 사용을 위해 크롬, 엣지, 오페라 브라우저를 추천드려요.'
);
return;
}
try {
const eyeDropper = new window.EyeDropper();
const result = await eyeDropper.open();
onColorChange(result.sRGBHex);
} catch (e) {
console.error('스포이드 취소됨 or 에러 발생:', e);
}
};
이 구현은 간결하고 효과적이었으나, 큰 제약이 있었습니다. 2023년 기준으로 EyeDropper API는 Chrome, Edge, Opera 등 Chromium 기반 브라우저에서만 지원됩니다. Safari, Firefox 등 다른 주요 브라우저에서는 작동하지 않는 것이 문제였습니다.
브라우저 호환성 문제를 해결하기 위해 html2canvas 라이브러리를 활용한 대안적 접근법을 모색했습니다. 이 라이브러리는 DOM 요소를 캔버스로 변환하여 렌더링하는 기능을 제공합니다.
그러나 html2canvas를 활용한 구현 과정에서 여러 기술적 장벽에 직면했습니다:
보안 정책으로 인한 제약:
SecurityError: The operation is insecure.
Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
웹 보안 정책(CORS, Same-Origin Policy)으로 인해 외부 리소스가 포함된 요소의 픽셀 데이터를 추출할 수 없는 문제가 발생했습니다.
좌표 계산 오류:
TypeError: Value Infinity is outside the range [-2147483648, 2147483647]
캔버스 내 좌표 계산 과정에서 예상치 못한 값이 생성되어 오류가 발생했습니다.
html2canvas의 다양한 옵션을 조정하여 문제 해결을 시도했습니다:
const canvas = await html2canvas(viewportElement, {
logging: false,
backgroundColor: null,
scale: 1,
useCORS: true,
allowTaint: true,
foreignObjectRendering: false,
width: window.innerWidth,
height: window.innerHeight,
x: window.scrollX,
y: window.scrollY,
windowWidth: window.innerWidth,
windowHeight: window.innerHeight,
ignoreElements: (element) => {
return element.tagName === 'IMG' ||
element.tagName === 'IFRAME' ||
element.tagName === 'VIDEO' ||
element.style.backgroundImage !== '';
}
});
특히 ignoreElements
옵션을 통해 외부 리소스가 포함된 요소를 렌더링에서 제외함으로써 tainted canvas 문제를 해결할 수 있었습니다. 또한 좌표 계산 문제는 다음과 같이 간소화하여 해결했습니다:
const x = event.clientX;
const y = event.clientY;
if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
const imageData = ctx.getImageData(x, y, 1, 1);
const r = imageData.data[0];
const g = imageData.data[1];
const b = imageData.data[2];
const color = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
onColorChange(color);
}