(ChatGPT) OpenAI로 블로그 자동화하기 - 2

반응형

지난번 포스팅 ChatGPT 자동 글쓰기에 이어서 이번에는 블로그에 자동 업로드하는 것까지 만들어본다. 블로그는 여러 플랫폼을 찾아봤지만 단순한 API를 제공하는 블로그를 찾기 쉽지 않아서 그냥 스팀잇에 하기로 했다.


데이터베이스 데이터 구조

데이터베이스(DB)는 Firestore에 생성했고, DB 관리 툴은 Jetadmin를 사용하고 있다.
자동으로 생성되는 데이터는 심플하게 제목을 제외한 본문, 메인 이미지, 해시태그이다. 그리고 실제 documents 구조는 다음과 같다.

완전 자동으로 하지 않고 제목(Title)은 내가 입력하도록 시스템을 구성했다. DB에 제목을 입력하면 나머지 필드는 ChatGPT가 자동으로 생성하여 입력한다. 그다음 ChatGPT가 작성한 글이 블로그에 포스팅될 것이다.

ChatGPT로 블로그 글 작성하기

DB에 제목만 있고 body가 아직 입력되지 않은 데이터를 가져온다. 이것은 아직 블로그에 포스팅되지 않은 글이므로 ChatGPT에게 글을 작성하게 할 것이다.

body가 입력되지 않은 데이터를 가져오는 함수를 ChatGPT에게 작성하도록 요청했다.

그리고 ChatGPT가 작성한 getDocumentsWithNullBody 함수의 코드를 조금 수정했다. 왜냐하면 Jetadmin에서 내가 제목을 등록해보니, 입력하지 않은 필드의 값은 Null 이 아닌 빈값("")으로 입력되어 있었기 때문이다.

async function getDocumentsWithNullBody(collectionName: string) {
  const db = firebase.firestore();
  const collectionRef = db.collection(collectionName);
  const query = collectionRef.where("body", "in", [null, ""]);

  const snapshot = await query.get();
  return snapshot.docs.map((doc) => ({ id: doc.id, data: doc.data() }));
}

이제 Jetadmin에서 New Record 버튼을 눌러, 블로그를 작성할 제목을 입력해 보자.

DB에 새로운 Record가 하나 생성되었다.

그다음 getDocumentsWithNullBody 함수를 테스트해 보면, 제목만 작성되어 있는 레코드 한 건을 가져오는 것을 확인할 수 있다.

const response = await getDocumentsWithNullBody("documents");
console.log(response);

지난번에 작성한 writeBlogPost 함수를 사용하여, 나머지 필드의 값을 생성하도록 ChatGPT에게 요청한다. 그다음 생성된 데이터를 Firestore에 업데이트하자.

// #1. 작성 예정인 블로그 제목 가져오기
const response = await getDocumentsWithNullBody("documents");
const { id, data: { title } } = response[0];

// #2. ChatGPT에게 body, tags, mainImage 생성 요청하기
const body = await writeBlogPost(
  `나는 개발자 일상에 대한 에세이를 블로그에 작성하고 있어. 제목은 "${title}"이야. 500단어 정도의 글을 작성해줘. 그리고 주요 제목은 포함하지마.`
);
const tags = await writeBlogPost(
  `글 제목 "${title}"에 적당한 해시태그를 5개 정도 생성해줘. 태그 앞에 #은 사용하지마, 그리고 해시태그는 쉼표로 연결해서 만들어줘`
);
const mainImage = await writeBlogPost(
  `[INFO: Use the Unsplash API (https://source.unsplash.com/1600×900/?). the query is just some tags that describes the image. Write the final Image URL.] ## DO NOT RESPOND TO INFO BLOCK ##\n\nGive me a blog cover image url fit to this subject: ${title}`
);

// #3. Firestore에 업데이트 하기
await updateDocument(id, title, body, tags, mainImage);

ChatGPT에서 생성한 데이터가 DB에 잘 저장된 것을 확인한다.

블로그에 업로드 하기

이제 블로그에 포스팅하는 uploadPost 함수를 작성한다.

uploadPost 함수도 ChatGPT가 작성한 코드를 그대로 사용할 수가 없어서 약간 수정했다.

import { Client, PrivateKey } from "@upvu/dsteem";

async function uploadPost(post: {
  title: string;
  body: string;
  tags: string[];
  image: string[];
  author: string;
  postingKey: string;
}) {
  const { title, body, tags, author, image, postingKey } = post;
  const client = new Client(["https://api.steemit.com"]);

  try {
    const result = await client.broadcast.comment(
      {
        author,
        body,
        parent_author: "",
        parent_permlink: tags[0],
        permlink: new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, "").toLowerCase(),
        title,
        json_metadata: JSON.stringify({
          tags,
          image,
          app: "dsteem-js",
          format: "markdown",
        }),
      },
      PrivateKey.fromString(postingKey)
    );
    return result;
  } catch (error) {
    console.error(error);
    throw error;
  }
}

이제 uploadPost 함수로 블로그에 포스팅해 보자.

const author = process.env.STEEMIT_USERNAME!;
const postingKey = process.env.STEEMIT_POSTING_KEY!;

await uploadPost({
 title,
 body: `![](${mainImage})\n\n${body}`,
 tags: ['kr', ..tags.split(',')],
 image: [mainImage],
 author,
 postingKey,
});

테스트 결과, 성공적으로 블로그에 포스팅되었다!

마지막 단계: 스케쥴러 만들기

마지막으로 10분마다 DB에 새로 등록된 데이터가 있는지 체크하면서, 새로 등록된 레코드가 있으면 자동 블로그를 수행하도록 한다.

참고로 Googld Cloud Firestore에서 문서 읽기는 하루에 5천회까지 무료이다. 그러므로 1분에 약 34번 데이터를 조회할 수 있다. 만약 1초마다 DB를 체크하도록 하면 하루치를 빠르게 소진해버리게 된다. 그래서 나는 적당히 10분마다 DB를 체크하도록 했다.

다음은 10분마다 DB를 체크하면서, 새로 등록된 레코드가 있다면 자동 블로그를 수행하는 코드이다.

while (true) {
    console.log(`${new Date().toISOString()} | CHECK!`);

    // #1. 작성 예정인 블로그 제목 가져오기
    const response = await getDocumentsWithNullBody("documents");
    if (!response.length) return;

    const { id, data: { title } } = response[0];
    console.log(`${new Date().toISOString()} | 제목: "${title}"`);

    // #2. ChatGPT에게 body, tags, mainImage 생성 요청하기
    // ⋯ (생략) ⋯
    
    // #3. Firestore에 업데이트 하기
    // ⋯ (생략) ⋯
    
    // #4. 블로그에 포스팅하기
    // ⋯ (생략) ⋯

    console.log(`${new Date().toISOString()} | DONE!`);

    // 10 minutes waiting
    await new Promise((resolve) => setTimeout(resolve, 10 * 60 * 1000));
  }

만약 cron와 비슷한 스케쥴러 동작 방식을 원한다면, node-schedule 패키지를 사용하면 된다.

소감

  • Jetadmin도 zapier와 비슷한 automations 기능을 제공하고 있다. 하지만 사용방법이 어려웠다. 나는 그냥 직접 개발하는 것이 더 쉬운 방법이다.
  • Openai API를 사용하면서 API 요청을 12번 했다. 그리고 사용료은 $0.23가 발생했다. Openai API로 하나의 글을 작성하는데 $0.08 정도의 비용이 발생하는 것 같다. 매우 저렴하다고 생각한다. 프롬프트를 잘 작성한다면 비용을 더 아낄 수 있을 것 같다.
  • 이번에 ChatGPT와 코딩 및 블로그 글 작성을 함께 해보니 든든한 동료가 생긴 것 같다.

반응형