ColorThief를 활용하여 이미지에 따라 스타일 바꾸기

지애·2025년 2월 20일
0

📌목표

배경에 들어가는 이미지의 명도에 따라 글씨색을 흰색/검은색으로 바뀌게 하고 싶었다. 아래의 사진처럼 밝은 사진에는 검은색 글씨, 어두운 사진에는 흰색 글씨를 사용하여 가독성이 좋아지도록 만들고 싶었다...!
(사진 상으로는 검은색 글씨가 오히려 잘 안 보이는 것 같지만 나중에 배경 그라데이션을 조정해 보는 것으로....ㅎㅎ)

☀️ 밝은 이미지🌜어두운 이미지

🤔 무엇을 하면 될까?

1. 이미지의 대표 색 추출하여 명도 계산하기

웹 이미지의 명도를 어떻게 계산하면 좋을까 고민하다가 ColorThief라는 모듈과 Relative luminance 계산법에 대해 알게 되었다.

ColorThief는 local 이미지나 웹 이미지의 컬러 팔레트를 추출할 수 있는 npm 모듈이다. javascript만 지원하고 있어서 typescript로 해당 모듈을 사용하려면 따로 type 명시를 해주어야 한다. 이 부분은 이후 에러 해결 파트에서 자세히 다루어보겠다!

Relative luminance는 색의 상대적인 밝기를 의미하는 것으로 해당 지표를 사용하여 색의 밝기 차이를 계산할 수 있다.

Relative luminance = 0.2126R + 0.7152G + 0.0722*B

2. 명도에 따라 적용하는 css 달라지게 하기

각 이미지 별 대표 색상에 대해 Relative luminance를 계산한 뒤에는 어두운 이미지를 결정하는 threshold를 지정해야 한다. WCAG에서 권장하는 최소 color contrast는 4.5:1 이므로 이 비율을 참고하기로 했다.

흰색의 Relative luminance를 계산하면 255이므로, 약 5:1 쯤 되는 50 이하일 때 해당 이미지를 어두운 이미지라고 분류하기로 했다. 해당 정보를 useState로 관리하고 state에 따라 적용되는 글자 색이 달라지도록 css를 작성했다.

          <div
            className={`review-top-content ${
              isDarkImage ? "light-text" : "dark-text"
            }`}
          >
            <div className="play-discription">{play.genre}</div>
            <div className="play-name">{play.title}</div>
            <div className="play-discription">{play.period}</div>
            <div className="play-discription">{play.location}</div>
          </div>

🚨 마주친 오류들...

1. color-thief import 실패 (typescript 버전 없음)

ColorThief를 사용하려던 첫 순간부터 오류를 맛보았다. 분명 모듈을 설치하고 import를 한 것임에도 빨간줄이 뜨는 것이었다. 알고 보니 ColorThief는 javascript만을 지원하고 있어서 발생하는 오류였다. typescript 버전이 따로 없는 경우에는 직접 type 지정을 해주면 된다고 한다.

https://github.com/lokesh/color-thief/issues/188
위 깃허브를 참고해서 type 지정을 해주었다.

src > types > colorthief.d.ts

파일을 생성하고 ts 파일에

declare module 'colorthief' {
    type Color = [number, number, number];
    export default class ColorThief {
      getColor: (img: HTMLImageElement | null) => Color;
      getPalette: (img: HTMLImageElement | null) => Color[];
    }
  }

이 코드를 넣었더니 import 문제가 해결되었다.

2. CORS 오류

웹 이미지 url을 사용하려다보니 CORS 문제가 발생해 컬러 팔레트가 추출되지 않는 문제가 발생했다.

시도 1
https://stackoverflow.com/questions/23123237/drawing-images-to-canvas-with-img-crossorigin-anonymous-doesnt-work
위 스택오버플로우를 참고하여 image의 crossOrigin을 Anonymous로 설정해보았다.

img.onload = () => {
 const result = colorThief.getColor(img);
const [r, g, b] = result;
const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;;
setIsDarkImage(brightness < 50);
};
img.src = play.image;
img.crossOrigin = 'Anonymous';

그 결과 여전히 CORS 오류가 발생했다......ㅜㅜ

시도 2
https://stackoverflow.com/questions/30731209/grabbing-color-palette-from-youtube-thumbnail/30741691#30741691
기존 url 앞에 'http://cors-anywhere.herokuapp.com/'를 붙이면 CORS 오류를 우회할 수 있는 서비스가 있길래 시도해 보았다.

img.onload = () => {
 const result = colorThief.getColor(img);
const [r, g, b] = result;
const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;;
setIsDarkImage(brightness < 50);
};
img.src = 'http://cors-anywhere.herokuapp.com/'+play.image;
img.crossOrigin = 'Anonymous';

CORS 오류는 나지 않지만 ColorThief를 사용할 때 403 오류가 발생하여 컬러 팔레트를 가지고 올 수 없는 문제는 여전했다....img url이 변경되어 403 오류가 발생한 것 같다.

시도 3
https://webdoli.tistory.com/181
https://allorigins.win/
CORS 오류를 우회할 수 있는 또 다른 서비스를 발견했다.

  const fetchImage = async () => {
    try {
      const response = await fetch(
        `https://api.allorigins.win/get?url=${encodeURIComponent(play.image)}`
      );
      const data = await response.json();
      const img = new Image();
      img.src = data.contents;
      img.crossOrigin = "Anonymous";
      img.onload = () => {
        const colorThief = new ColorThief();
        const color = colorThief.getColor(img);
        setColor(color);
        const [r, g, b] = color;
        const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        console.log("brightness", brightness);
        setIsDarkImage(brightness < 50);
        console.log("color", color);
      };
    } catch (error) {
      console.error("Error fetching image:", error);
    }
  };

이 코드처럼 url 부분에 접근을 원하는 페이지의 url을 넣으면 response 값으로 해당 페이지에 대한 정보를 넘겨준다. response를 json으로 파싱한 후, contents값을 통해 이미지 url을 접근하면 CORS와 403 오류 없이 컬러 팔레트를 잘 받아올 수 있었다!!!

참고
https://en.wikipedia.org/wiki/Relative_luminance
https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color
https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html

profile
차근차근

0개의 댓글