
최근 광고 상품 개발 중, 사용자가 화면을 닦는 행위를 통해 선명해지는 이미지를 구현했습니다. 목표는 사용자가 닦을 때마다 이미지가 점차 선명해지는 효과를 자연스럽게 표현하는 것이었습니다. 그리고 50%가 닦였을때 전체가 닦여지는 기능을 구현하는 것 입니다.

아이디어는 토스의 복권 긁기에서 나온 듯 한데 내가 개발 할줄이야 하며 찾아봤었습니다. 레퍼런스를 조사하던 중, 흔히 "wipe"라는 용어 대신 "scratch"라는 용어가 더 널리 사용되는 듯 했습니다.
그러나 우리는 기획상 "wipe"으로 클리닝이나 초기화 기능을 강조하는 느낌을 주려고 채택 했다고 했습니다.
이를 구현하기 위해 layer + 닦이는 느낌의 처리를 자연스럽게 할 수 있는 방법인 Canvas API를 활용하기로 결정했습니다.
Canvas API를 사용하면서 발생한 주요 문제는 Cross-Origin 에러였습니다. 이를 해결하기 위해 다음 두 가지 방법을 시도했지만, 여전히 문제가 발생했습니다.
Access-Control-Allow-Origin: *img.crossOrigin = "anonymous"이미지를 그린 후 그 위에 Canvas로 블러 처리된 이미지를 덮어 씌워 닦는 효과를 구현하려 했습니다.
crossOrigin 설정 없이 구현 시:crossOrigin을 "anonymous"로 설정 시:img.onerror 이벤트가 발생하여 이미지가 정상적으로 렌더링되지 않았습니다.결국, 원본 이미지를 렌더링한 후 블러 처리된 이미지를 Canvas에 올려 닦는 효과를 구현하려 했으나, crossOrigin 설정으로 인해 이미지가 제대로 렌더링되지 않는 문제가 발생했습니다.
img 태그img 태그는 HTML에서 이미지를 삽입할 때 사용하는 기본적인 요소입니다. 단순히 이미지를 표시하는 기능에 초점을 맞추고 있으며, 브라우저가 자동으로 CORS 정책을 관리하여 외부 출처의 이미지 로딩 시 보안 문제를 최소화 하기 때문에 개발자는 별도의 CORS 설정 없이도 이미지를 손쉽게 삽입할 수 있습니다.
<canvas> 요소와 JavaScript를 사용하는 Canvas API는 동적이고 세밀한 표현을 가능하게 합니다. 픽셀 단위로 그릴 수 있어 닦기와 같은 동적 효과를 구현할 수 있습니다. 그러나 이미지 데이터를 직접 조작할 수 있는 만큼, 보안상의 위험이 있습니다.
Cross-Origin 에러는 주로 외부 도메인에서 로드된 이미지나 리소스를 Canvas에 그리려고 할 때 발생합니다.
저의 경우, 백엔드에서 제공한 URL을 통해 이미지를 로드하려 했으나, CloudFront 설정으로 인해 접근이 제한되어 있었기 때문에 발생했습니다.
즉, 퍼블릭한 URL(예: https://picsum.photos/200/300)은 가능하지만 특정 설정으로 인해 문제가 발생하는 것 입니다.
"tainted 상태"란?
Canvas가 외부 출처(다른 도메인, 프로토콜, 또는 포트)의 리소스를 로드하면서 발생하는 보안 제한 상태 입니다.
그렇기 때문에 Canvas에 그려진 이미지를 JavaScript로 조작하고 분석하는 것이 불가능해집니다. 결과적으로, 사용자가 이미지를 조작한 행위(예: 닦는 행위)의 퍼센티지를 계산할 수 없게 된 것 입니다.
img 태그는 브라우저가 자동으로 CORS 정책을 관리하여 외부 출처의 이미지 로딩 시 보안 문제를 최소화하기 때문에, 개발자가 별도의 CORS 설정 없이도 이미지를 삽입할 수 있습니다. 반면, Canvas API는 외부 출처의 이미지를 사용할 경우 보안 위험이 증가하여 브라우저가 이를 "tainted" 상태로 처리하고 추가적인 데이터 접근을 제한합니다. 따라서 Canvas API를 사용시 CORS에 신경써야 합니다.
Access-Control-Allow-Origin: * 헤더를 추가하여 모든 도메인에서의 접근을 허용합니다.결국, 신속한 해결을 위해 첫 번째 방법을 선택했습니다.
일부 브라우저(사파리 및 Arc)에서는 여전히 렌더링 문제가 발생했습니다. (크롬과 파이어폭스에서는 정상)
음... CloudFront 설정을 변경하고 캐시를 무효화했음에도 불구하고 Cloudflare에서 4시간 동안 캐시를 유지하고 있었기 때문입니다.
이를 일시적으로 우회하기 위해 이미지 URL의 끝에 쿼리 파라미터를 추가하여 캐시를 회피하는 방식으로 최신 CORS 설정이 반영된 이미지를 성공적으로 로드할 수 있었으나, 이는 근본적인 해결책이 아닌 임시방편에 불과하여 추가적인 방법을 고민중에 있습니다.
++) new Date() 로 했더니 Arc 에서는 정상적으로 렌더링 되는 것을 확인했으나, 사파리에서는 정상동작이 되지 않았습니다. -> Cloudflare 캐싱문제 해결
알고보니,,, HTMLCanvasElement 으로 style 에 접근해야했습니다!
기존에는 CanvasRenderingContext2D으로 접근해서 블러처리를 했었는데 이때 사파리에서는 렌더링은 되었으나 블러처리가 되지 않았던 것이었습니다.
Cloudflare에서 4시간 동안 캐시 -> 쿼리파라미터의 Date.now() 으로 캐싱 우회 => 모든 브라우저에서 canvas 렌더링 잘됨!
사파리에서 블러처리 이슈 -> HTMLCanvasElement 의 style 로 접근해 블러를 설정
...
private readonly canvasEle: HTMLCanvasElement;
private readonly canvasRander: CanvasRenderingContext2D;
...
this.canvasEle.style.filter = "blur(5px)";
this.canvasRander.filter = "blur(5px)";
...