Dev.to처럼 포스팅 제목을 이미지로 만들어서 링크 미리보기(Link Preview) 이미지에 넣어보자

최근에 페이스북에 어떤분이 dev.to에 포스팅한 한글로 된 글을 보면서 저런 건 어떻게 만드는 걸까? 관심이 생겨 알아보게 되었습니다.

최종 완성된 프로젝트 코드는 깃헙 리포지토리에 올려 두었습니다.

어떻게 포스팅 제목을 커버 이미지로 만들까?

보통은 직접 커버 이미지를 제작해서 사용하는 방법도 있지만 조금 더 생각해보니 SVG(Scalable Vector Graphics)의 방식을 잘 활용하면 어떻게 되지 않을까? 싶었습니다.

MDN 문서에는 SVG가 그래픽을 마크업하기 위한 W3C XML의 특수언어라고 되어있습니다.

그래서 SVG 파일을 브라우저 개발자 도구나 편집기로 열어보면 아래와 같이 코드로 되어있는 것을 확인 할 수 있습니다.

SVG 파일을 편집기로 열었을 때

그렇다면 SVG 템플릿을 만들어 두고 타이틀 같은 것만 입력받을 수 있게 한다면 원하는 결과물을 보여줄 수 있지 않을까?

그리고 만들어진 커버 이미지를 API에 응답으로 보내주고 OpenGraph나 Twitter Cards(?) 태그에 이미지 주소를 API 주소로 해주면 우리가 원하는 결과를 얻을 수 있겠지…? 하고(????) 아래와 같은 구조로 정리를 했습니다.

동작 구조

-시작하기 전엔 몰랐습니다… svg가……..

개발 환경 구축

Node.js + babel (with typescript presets) 환경에 express를 사용하여 개발 환경을 구축하였습니다.

$ npm init -y
$ npm i -D @babel/cli @babel/core @babel/node @babel/preset-env @babel/preset-typescript babel-plugin-module-resolver nodemon prettier
$ npm i @types/express @types/node axios dotenv express

Mock 데이터 추가

이번 글에서는 실제 DB에 접속하지 않고, 목데이터를 가져오는 식으로 적용했습니다.

커버 이미지 API 라우팅 추가

위에 정의한 목데이터를 활용하여 유저가 포스팅 ID로 요청하면 해당 포스팅 데이터를 가져오도록 했습니다.

SVG Template를 사용해 응답 데이터에 SVG 넣기

커버 이미지 라우터에 포스팅 데이터를 전달해 SVG를 만들어주는 템플릿 함수를 추가해 주었으며, 응답 헤더에 Content-Typeimage/svg+xml로 설정했습니다.

또한 SVG에서 image를 불러올 경우에 crossorigin 관련 이슈가 있어서 이를 서버에서 직접 이미지를 호출한 뒤 DataURI 형태로 변환해준 후 적용해 주었습니다.

SVG 이미지가 잘 나오는지 확인

실제로 접속해보면 정상적으로 SVG가 나오는 걸 확인 할 수 있습니다~! 🎉🎉🎉🎉🎉🎉🎉

잘된다!!!

그러나…

If you include svg image, The Open Graph Object Debugger will say it is not an image.

뒤늦게 알게 된 사실이지만 Open Graph나 Twitter Cards의 경우 SVG 포맷의 이미지를 지원하지 않는다고 합니다… (아… ㅠㅠㅠㅠ)

sharp 라이브러리 사용

울며 겨자 먹기로 어떻게든 미리보기 이미지는 보여주고 싶어서 알아보던 도중 예전에 gatsbyjs로 만들었던 프로젝트에서 sharp라는 이미지 프로세싱 라이브러리를 플러그인으로 사용했던 기억이 떠올랐습니다.

그렇게 기존에 만든 SVG 이미지를 sharp를 통해 png로 변환하여 보여주는 기능을 추가해 주어 아래와 같이 구조가 되었습니다.

sharp를 추가한 구조
$ npm i @types/sharp sharp
// Sharp를 사용하여 png로 만든 후 응답const svgImage = await generateSvgWithPost(postData);
const convertedImage = await convertSvgToPng(svgImage);
res.setHeader("Content-Type", "image/png");
res.send(convertedImage);

Sharp를 통해 png로 잘 변환되었는지 확인

OG 태그 및 Twitter Cards 태그 추가하기

png 이미지까지 변환이 잘 되었으니 이제 만든 커버 이미지 API의 주소를 og:imagetwitter:image 태그에 넣어줍니다.

저는 테스트용으로 public 폴더에 post-1.htmlpost-2.html을 만들어 줬습니다.

ngrok를 사용하여 로컬 서버를 외부 환경에서 테스트할 수 있게 설정

마지막으로 지금까지 만든 기능이 실제로 Facebook이나 Twitter 등에 잘 노출이 되는지 확인을 하기 위해서 ngrok을 사용해줍니다.

$ ngrok http 8080

ngrok이 열렸다면 할당받은 ngrok.io 주소를 복사하여 아까 위에 만든 post-1.html 파일 안 코드에 {{YOUR_DOMAIN_URL_HERE}} 를 ngrok.io 주소로 변경해 줍니다.

post-1.html

그리고서 ngrok.io로 변경된 URL(https://~~~.ngrok.io/post-1.html)을 공유했을 때 잘 동작하는지 확인합니다.

Discord 채팅에 공유

마치며

다행히도 나름 원하는 결과물이 나왔지만, 진행을 하면서 느낀 것은 SVG 템플릿 방식을 통한 커버 이미지 제작은 많은 제약이 있다는 것을 알게 되었습니다.

  • SVG 파일에서 이미지 사용 시 DataURI 형태로 변환해서 사용해야 함 (CORS)
  • SVG text 태그 사용 시 자동 줄 바뀜이 안됨
  • png로 한 번 더 변환해줘야 하는 추가 과정

그래서 커버 이미지의 경우에는 SVG 대신 퍼펫티어 같은 걸 사용해서 만드는 방식이 좀 더 나을 것 같다는 생각이 들었습니다.

추가로 dev.to나 github.com에서는 HTML/CSS를 이미지로 바꿔주는 API인 HCTI(https://htmlcsstoimage.com/)를 사용하더군요.

자세히 확인해보진 않았지만 헤드리스 브라우저로 스크린샷을 찍는 방식이지 않을까… 싶습니다..(아마?) 혹시나 알고 계신다면 댓글로 알려주세요! :)

많이 부족하게 쓴 글이지만 읽어주셔서 감사합니다. 🙏

Web Frontend Developer. 디자인과 개발의 영역을 조화롭게 표현할 수 있는 프론트엔드 개발의 매력에 빠진 사람, 황주성입니다 :)

Web Frontend Developer. 디자인과 개발의 영역을 조화롭게 표현할 수 있는 프론트엔드 개발의 매력에 빠진 사람, 황주성입니다 :)