Javascript/Next

[NextJS] canvas 이용해서 썸네일 만들기

eulBlue 2023. 8. 29. 18:59

📱테스트 환경

"react": "18.2.0"
"react-dom": "18.2.0"
"typescript": "^5.1.6"

😢 내가 겪은 문제

SEO 에 사용할 이미지를 하나만 사용하면 사용할 일 없었겠지만, 같은 페이지라도 query 값에 따라 보여지는 SEO 이미지가 달라야했다..

예를 들어서 localhost:8000/result 에는 A 이미지가 보여야 했다면

localhost:8000/result?id=123 에는 B 이미지가 보여야했다.

그리고 예시로 든 URL 에 result 가 들어있는것만봐도 결과값을 이용해서 썸네일을 만들고 싶었다... ^^

대충 이런식의 썸네일을 만드려고 했다. 규격이 800x400 인 이유는 카톡 썸네일 기준이기 때문이다.

내가 보기엔 이거 할 줄 알면 canvas 로 못만들거 없어진다. ( canvas 이용한 것 중에 이게 제일 어려웠다. )

일단 canvas 에 대해 알아보자면

캔버스(Canvas)는 HTML5에서 제공되는 그래픽 그리기를 위한 요소로

JavaScript를 사용하여 그림을 그리고 다양한 그래픽 요소를 조작할 수 있는 기능을 제공해준다.

캔버스를 사용하면 웹 페이지에서 동적인 그래픽 및 애니메이션을 생성할 수 있습니다. ( 사실상 다 만들 수 있다는 소리다. )

나도 이때문에 canvas 이용해서 하라고 해서 개고생하면서 열심히 만들었따 ..

일단 전체코드이다.

getThumbnail.tsx

export const getThumbnail = async () => {
  const image: any = getImage();

  const canvas = document.createElement("canvas");
  canvas.width = 800;
  canvas.height = 400;

  canvas.style.display = "none";

  document.body.appendChild(canvas);

  const context = canvas.getContext("2d");

  if (context) {
    context.fillStyle = "#7491ff";
    context.fillRect(0, 0, canvas.width, canvas.height);

    context.font = "700 40px Arial"; // font-weight font-size font

    const paddingX = 40;
    const paddingY = 40;

    const img = new Image();
    img.src = location.origin + image.src;

    await new Promise((resolve) => {
      img.onload = resolve;
      img.onerror = resolve;
    });

    const imgWidth = 80; // 이미지의 너비
    const imgHeight = 80; // 이미지의 높이
    const imgX = paddingX;
    const imgY = paddingY;
    context.drawImage(img, imgX, imgY, imgWidth, imgHeight);

    const additionalText = "첫번째 줄 텍스트";
    context.fillStyle = "white";
    context.font = "700 50px Arial";
    const additionalTextY = paddingY + 150;
    context.fillText(additionalText, paddingX, additionalTextY);

    context.fillStyle = "white";
    context.font = "700 135px Arial";
    const nameText = "두번쨰 줄 텍스트";
    const nameTextY = additionalTextY + 170;
    context.fillText(nameText, paddingX + 10, nameTextY);

    const img2 = new Image();
    img2.src = fileURL;

    await new Promise((resolve) => {
      img2.onload = resolve;
      img2.onerror = resolve; // Handle error, since we don't want to reject the main promise
    });

    const img2Width = 400; // 이미지의 너비
    const img2Height = 400; // 이미지의 높이
    const img2X = canvas.width - img2Width;
    const img2Y = canvas.height - img2Height;
    context.drawImage(img2, img2X, img2Y, img2Width, img2Height);

    const dataURL = canvas.toDataURL();
    return dataURL;
  }
};

중요한 코드를 쪼개서 이해해보도록자면

const canvas = document.createElement("canvas");
  canvas.width = 800;
  canvas.height = 400;

  canvas.style.display = "none";

  document.body.appendChild(canvas);

  const context = canvas.getContext("2d");
  1. 가로 800, 높이 400 짜리를 canvas 를 생성하고, display 는 none 로 설정한다.
    display none 처리를 안하면 body 에 추가할껀데 화면에 계속 보이기 때문이다.
  2. body 에 canvas 엘리먼트를 추가한다.
  3. 캔버스의 2D 컨텍스트를 가져온다.
    2D 컨텍스트는 캔버스 위에 그래픽을 그리고 조작하는 데 사용되는 도구 모음이라고 생각하면 된다.
const img = new Image();
    img.src = location.origin + image.src;

    await new Promise((resolve) => {
      img.onload = resolve;
      img.onerror = resolve;
    });
  1. 약간 선택사항(?) 같은데 .. img.src 에 location.origin + image.src 해주는 이유는
    image 는 로컬에 저장 ( ../public/png/image.png) 되어있어서 이렇게 호출 해 주었다. 다른 방법이다 더 좋은 방법이 있다면 맞게 수정하면 될 것이다. ( 알려주면 더 좋다. )
  2. 여기가 중요한데, img 를 로드하기전에 썸네일을 다 만들어버리면 의미가없다. 그렇기 때문에 비동기 처리가 필요하다.
    그렇기 때문에 이미지가 로드되거나 로드 중에 오류가 발생할 때까지 대기하는 비동기 작업을 수행할 수 있도록 한다.

중요한 코드는 이정도 인 것 같다.

나머지는 주석도 달려있고, 읽어보기만 해도 쉽게 이해할 수 있을 것이다.

나처럼 SEO 대응해서 썸네일 만들일이 많을지는 경험상 잘 모르겠따. 나도 처음해보는거라서.

Canvas 를 이용해서 이런것도 할 수 있구나 ~ 안되는게 없구나 ~ 이런식으로 하면 되겠구나 ~

정도로 이해하고  하고 넘어갈 수 있다면 좋을 것 같다.