바닐라 Node.js에서의 OG 이미지 (프레임워크 없음)
프레임워크 없이 FastOG의 구독 기반 Dynamic API를 사용하여 OG 이미지를 생성하세요. Dynamic API 구독(대시보드에서 API 키 생성)이 필요합니다. 보일러플레이트를 건너뛰고, Free OG Image Tester에서 미리보기하고, 순수 Node.js와 HMAC 서명 URL로 동적 Open Graph 이미지를 구축하세요.
서명자 모듈
javascript
// og-signer.js
const crypto = require("crypto");
const { URL } = require("url");
function generateSignedOgUrl(params, options = {}) {
const {
baseUrl = "https://fastog.com/api/v1/og",
apiKey = process.env.FASTOG_API_KEY,
hmacSecret = process.env.FASTOG_HMAC_SECRET,
} = options;
// RFC-3986 percent-encoding: keep A-Za-z0-9-._~
const enc = (s) =>
encodeURIComponent(s).replace(
/[!'()*]/g,
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
);
// Every query param except the signature, INCLUDING key
const all = { ...params, key: apiKey };
// Canonical query: sorted enc(key)=enc(value) pairs joined with &
const pairs = [];
for (const [k, v] of Object.entries(all)) {
for (const vv of Array.isArray(v) ? v : [v]) {
pairs.push(`${enc(k)}=${enc(vv)}`);
}
}
pairs.sort();
const canonical = pairs.join("&");
// Sign the exact bytes the server will verify
const signature = crypto
.createHmac("sha256", hmacSecret)
.update(`GET/api/v1/og\n${canonical}`)
.digest("hex");
// Build the URL from the same pairs so bytes match the canonical form
const url = new URL(baseUrl);
for (const [k, v] of Object.entries(all)) {
for (const vv of Array.isArray(v) ? v : [v]) {
url.searchParams.append(Array.isArray(v) ? `${k}[]` : k, vv);
}
}
url.searchParams.set("s", signature);
return url.toString();
}
module.exports = { generateSignedOgUrl };
사용 방법
javascript
// index.js
const { generateSignedOgUrl } = require("./og-signer");
const ogUrl = generateSignedOgUrl({
template: "blog",
title: "My Blog Post",
});
console.log("OG Image URL:", ogUrl);
렌더링:
ogUrl을 HTML<head>의<meta property="og:image" content="...">태그에 넣거나, 아래 설명된/api/og엔드포인트에서 제공하세요.
HTTP 서버 사용하기
javascript
// server.js
const http = require("http");
const { generateSignedOgUrl } = require("./og-signer");
const server = http.createServer((req, res) => {
if (req.url === "/api/og") {
const url = new URL(req.url, `http://${req.headers.host}`);
const title = url.searchParams.get("title");
if (!title) {
res.writeHead(400, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ error: "Missing title" }));
}
const ogUrl = generateSignedOgUrl({
template: "default",
title,
});
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ogImageUrl: ogUrl }));
return;
}
res.writeHead(404);
res.end("Not Found");
});
server.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
ES 모듈 버전
javascript
// og-signer.mjs
import crypto from "crypto";
import { URL } from "url";
export function generateSignedOgUrl(params, options = {}) {
const {
baseUrl = "https://fastog.com/api/v1/og",
apiKey = process.env.FASTOG_API_KEY,
hmacSecret = process.env.FASTOG_HMAC_SECRET,
} = options;
// RFC-3986 percent-encoding: keep A-Za-z0-9-._~
const enc = (s) =>
encodeURIComponent(s).replace(
/[!'()*]/g,
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
);
// Every query param except the signature, INCLUDING key
const all = { ...params, key: apiKey };
// Canonical query: sorted enc(key)=enc(value) pairs joined with &
const pairs = [];
for (const [k, v] of Object.entries(all)) {
for (const vv of Array.isArray(v) ? v : [v]) {
pairs.push(`${enc(k)}=${enc(vv)}`);
}
}
pairs.sort();
const canonical = pairs.join("&");
// Sign the exact bytes the server will verify
const signature = crypto
.createHmac("sha256", hmacSecret)
.update(`GET/api/v1/og\n${canonical}`)
.digest("hex");
// Build the URL from the same pairs so bytes match the canonical form
const url = new URL(baseUrl);
for (const [k, v] of Object.entries(all)) {
for (const vv of Array.isArray(v) ? v : [v]) {
url.searchParams.append(Array.isArray(v) ? `${k}[]` : k, vv);
}
}
url.searchParams.set("s", signature);
return url.toString();
}
javascript
// index.mjs
import { generateSignedOgUrl } from "./og-signer.mjs";
const ogUrl = generateSignedOgUrl({
template: "blog",
title: "Hello World",
});
환경 변수
bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here