주제

바닐라 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

전체 API 참조

무료로 체험해 보세요 — 가입 불필요

몇 초 만에 동적 OG 이미지를 미리 보고 테스트하세요.

무료 OG 이미지 테스터 열기

A BeCaSimulations site