Apps Script로 유튜브 채널 콘텐츠 소재 찾기 자동화

1. 소개

✅ 시도하고자 했던 것과 그 이유

Reddit의 게시판에서 인기 있는 "사이다 복수 썰"을 자동으로 수집해서, OpenAI GPT를 통해 요약하고 판별한 후, Notion 데이터베이스에 정리하는 자동화 시스템을 만들고자 함.

이유) 검증된 소재를 주기적으로 노션에 업데이트하여 콘텐츠 기획에 드는 시간을 줄이려고.


2. 진행 방법

✅ 전체 시나리오는 Apps Script에서 구현.

  • Reddit API (OAuth2 인증): 로그인 토큰 발급받아 인기 글 불러오기

  • OpenAI API (GPT-4o): 게시글 요약 및 판별

  • Notion API: 결과를 Notion DB에 저장

✅ 자동화 흐름은 아래와 같습니다:

🔐 ① Reddit API (OAuth2 인증으로 인기글 수집)

파일: OAuth2.gs, fetchPosts.gs

  • Reddit은 일반적인 API 접근이 불가능하기 때문에 OAuth2 인증을 통해 로그인 토큰을 발급받도록 구성.

  • 사용자가 한 번 승인하면 이후부터는 자동으로 Reddit 인기글을 가져올 수 있음.

  • fetchTopAndBest() 함수는 top 게시판과 best 게시판 각각에서 최대 25개씩의 게시글을 수집하고, 최근 3일 이내 게시물만 필터링하여 중복 제거 후 최대 10개를 반환.

  • 이렇게 사전 필터링된 글들이 다음 단계인 GPT 분석 대상으로 넘어갑니다.

/**
 * ① OAuth2 서비스 정의
 */
function getRedditService() {
  const props = PropertiesService.getScriptProperties();
  const clientId     = props.getProperty('REDDIT_CLIENT_ID');
  const clientSecret = props.getProperty('REDDIT_CLIENT_SECRET');
  const basicAuth    = Utilities.base64Encode(clientId + ':' + clientSecret);

  return OAuth2.createService('Reddit')
    .setAuthorizationBaseUrl('https://www.reddit.com/api/v1/authorize')
    .setTokenUrl('https://www.reddit.com/api/v1/access_token')
    .setClientId(clientId)
    .setClientSecret(clientSecret)
    .setCallbackFunction('authCallback')
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope('read')
    // ★ Reddit은 token endpoint에 Basic auth 헤더를 요구합니다.
    .setTokenHeaders({
      Authorization: 'Basic ' + basicAuth
    });
}

/**
 * ② 최초 승인 요청 (한 번만 실행)
 */
function authorize() {
  const service = getRedditService();
  if (!service.hasAccess()) {
    // 이 URL을 로그에서 복사해 브라우저에 붙여넣고, Reddit 로그인·승인 진행
    const url = service.getAuthorizationUrl();
    Logger.log('▶ 이 URL을 복사해서 브라우저에서 열고, 승인하세요:\n' + url);
  } else {
    Logger.log('✔ 이미 승인된 상태입니다.');
  }
}

/**
 * ③ Reddit이 승인 후 돌아오는 콜백 처리
 */
function authCallback(request) {
  const service = getRedditService();
  const success = service.handleCallback(request);
  // 브라우저에 간단 메시지 출력
  return HtmlService
    .createHtmlOutput(success
      ? '✅ Reddit 승인 완료! 스크립트로 돌아가 다음 단계를 진행하세요.'
      : '❌ 승인 실패. 다시 시도해 주세요.');
}


function logRedirectUri() {
  const uri = getRedditService().getRedirectUri();
  Logger.log('▶ 실제 Redirect URI: ' + uri);
}
 

/**
 * 승인 상태 및 실제 Access Token 확인
 */
function testRedditToken() {
  const service = getRedditService();
  Logger.log('Has access? → ' + service.hasAccess());
  Logger.log('Access Token → ' + service.getAccessToken());
}

🧠 ② OpenAI API (GPT-4o를 활용한 요약 및 판별)

파일: analyzeGPT.gs

  • Reddit 게시글 하나하나를 GPT에 보내어 요약과 함께 "사이다 복수" 여부를 판단하도록 함.

  • GPT 응답을 정규표현식으로 파싱해서, 요약문과 별점(재미도)를 각각 추출.

  • 만약 요약이 불가능하거나 GPT 응답이 이상하면, 예외 처리를 통해 다음 글로 넘어가도록 구성.

/**
 * ④ GPT 분석: 사이다 복수판별·요약·별점
 */
function analyzePostWithGPT(post) {
  const OPENAI_KEY = PropertiesService.getScriptProperties().getProperty('OPENAI_KEY');
  const prompt = `
다음 Reddit 게시물이 ‘사이다 복수’ 이야기인지 판단해줘.

■ 조건에 부합하면 아래 형식으로 출력해:
✅ Justice served: [2~3줄 요약, 한국어로 자연스럽게 감정선 살려 작성]
⭐ 재미도: ★☆☆☆☆ ~ ★★★★★

■ 조건에 부합하지 않으면, 반드시 다음 한 줄만 출력해:
❌ Not relevant  (추가 텍스트 없이 정확히 이 단어만)

⚠️ 요약에 반드시 포함:
1. 누가 민폐/무례했는지
2. 피해자가 어떻게 복수했는지
3. 반전 또는 사이다 포인트

예시:
✅ Justice served: 상대가 내 웹페이지로 6천 달러 벌고 나 몰래 잠수했는데, 사이트가 먹통되자 해커인 척하니 바로 송금하더라.
⭐ 재미도: ★★★★☆

제목: "${post.title.replace(/"/g,'\\"')}"
본문: "${post.content.replace(/"/g,'\\"')}"
  `.trim();

  try {
    // 1) GPT 호출
    const res = UrlFetchApp.fetch('https://api.openai.com/v1/chat/completions', {
      method:      'post',
      contentType: 'application/json',
      headers:     { Authorization: 'Bearer ' + OPENAI_KEY },
      payload:     JSON.stringify({
        model:    'gpt-4o',
        messages: [{ role: 'user', content: prompt }]
      })
    });
    const text = JSON.parse(res.getContentText()).choices[0].message.content || '';

    // 2) 원문 로그
    Logger.log('▶ GPT 응답 원문:\n' + text);

    // 3) Not relevant 체크
    if (text.includes('❌ Not relevant')) {
      return { isJustice: false, summary: '', rating: '' };
    }
    if (!text.trim().startsWith('✅ Justice served:')) {
      return { isJustice: false, summary: '', rating: '' };
    }

    // 4) 요약·별점 파싱
    const summaryMatch = text.match(/✅ Justice served:\s*([\s\S]*?)(?=\n⭐|$)/);
    const ratingMatch  = text.match(/⭐ 재미도:\s*(★+☆*)/);

    return {
      isJustice: true,
      summary:   summaryMatch ? summaryMatch[1].trim() : '',
      rating:    ratingMatch  ? ratingMatch[1].trim() : ''
    };
  } catch (e) {
    Logger.log('GPT 호출 오류:', e);
    return { isJustice: false, summary: '', rating: '' };
  }
}

📦 ③ Notion API (결과 자동 저장)

파일: saveToNotion.gs

  • 분석 결과는 바로 Notion 데이터베이스에 자동 저장.

  • 저장되는 항목은 다음과 같음:

    • 제목

    • 요약

    • 사이다여부 (체크박스 형식)

    • 업보트 수

    • 재미점수 (Select 형식: 별점)

    • 원문 링크 (URL)

    • 날짜 (게시일)

🔁 ④ 트리거 연동

파일: workflow.gs

  • Google Apps Script의 시간 기반 트리거를 사용하여 3일에 한 번 자동 실행되도록 설정.

  • 중복 저장 방지를 위해 Notion DB에 이미 저장된 글인지 확인하고, 저장되지 않은 글만 새로 들어가도록 설정.


3. 결과

3일에 한 번씩 "top과 best 게시물에서 중복을 제외하고 upvote 500 이상을 받은 상위 10개" 글 자동으로 노션 페이지에 업데이트

중국어가있는 웹 사이트 스크린 샷


4. 배운 점

1️⃣ 코드는 기능별로 나누는 게 좋다 처음에는 Reddit API 호출, GPT 요약, Notion 저장을 한 화면에 다 넣어서 코딩을 짰습니다. 그런데 코드가 길어지자, 문제가 생기면 어디서 에러가 났는지 찾을 수가 없었습니다. 결국 레딧 인증, GPT 분석, 노션 저장, 중복 검사 등을 모두 함수로 나눠 관리했더니 훨씬 명확하고 유지보수가 쉬워졌습니다.

2️⃣ 스크립트 속성 (ScriptProperties)을 적극 활용하자 API 키나 데이터베이스 ID 같은 값들을 코드에 직접 쓰면, 실수도 많고 관리도 복잡합니다. GAS의 ScriptProperties에 REDDIT_CLIENT_ID, NOTION_TOKEN, OPENAI_KEY 등을 미리 등록해두면, 코드에선 변수명만 불러오면 되고 보안도 훨씬 좋아요. 아주 강력한 팁입니다.

3️⃣ 테스트용 코드는 반드시 지우거나 분리해두자 testSaveToNotion() 같은 테스트 코드를 그대로 두고 쓰다 보니, 실수로 실행하거나 데이터가 중복 저장되거나, 전체 구조가 너무 길어져서 어디가 메인인지 헷갈렸어요. 테스트 코드는 임시로 쓸 땐 쓰되, 끝나면 삭제하거나 별도 파일에 모아두는 게 베스트입니다.

4️⃣ 코딩은 ‘이해 후 시작’이 아니라, ‘시작 후 이해’다. GPT와 대화하면서 어떤 자동화를 하고 싶은지를 명확하게 설정하고 나면, 일단 지피티의 도움을 받아 코딩을 시작하는 게 좋습니다. 일단 해보면서 시행착오를 계속 겪으면 코딩에 대해서 조금이나마 이해하게 되고 전반적 구조가 보이기 시작합니다.

5️⃣ GPT도 모르는 게 있다 GPT는 설계는 도와줄 수 있어도, 실제 구현에서 발생하는 모든 예외상황까지 예측하지는 못합니다. 그래서 더욱 일단 부딪쳐 봐야 하는데, GPT와 오류를 겪어가는 과정은 내 의도(or GPT의 의도)를 검증하는 과정입니다. 그걸 피드백 삼아 고쳐나갈수록 더 정교한 결과물이 나옵니다.

6️⃣ 에러가 쌓이면 ‘새 대화창’이 낫다. 오류가 발생하고 해결하기 위해 이것 저것 시도하다 보면, 특정 시점에 이르러, 여기저기 덧붙인 코드가 얽히고 설키게 됩니다. 만약 대화창도 거의 포화상태가 되었고 코드도 회복 불능 상태가 됐다는 판단이 든다면, 다음과 같이 말하고 새 대화창에서 새롭게 코드를 짜는 것도 방법입니다.

(기존 대화창에 입력->) 내가 원하는 자동화 흐름과 지금까지 시도한 방식, 그리고 거기에서 나타난 에러를 핵심만 간결하게 정리해줘. (나온 답변을 새 대화창에 복붙 한 뒤 다음을 입력->) 위의 자동화를 구현하고 싶은데 이런 에러들이 있었어. 새로운 방식으로 코드를 처음부터 다시 짜줘.

그럼 GPT가 과거의 복잡한 코드 맥락에서 벗어나 더 깔끔하게 정리된 해결책을 줄 수도 있습니다.


5. 앞으로의 계획

데이터베이스에 자동으로 쌓인 글들 중에서 좋은 글 직접 고르고 난 후, 그 글의 댓글들을 분석하는 자동화 흐름을 만들어보려 합니다.(make로 구현해볼 예정):

  1. 가장 웃긴 댓글을 크롤링: 영상 초반의 후킹 자막 작성에 활용

  2. 사람들이 가장 반응한 지점 찾기: 클라이맥스 타이밍을 파악하여 썸네일or후킹용으로 사용

사람들이 실제로 반응한 포인트를 바탕으로 더 몰입감 있는 쇼츠 영상을 만들 수 있을 것이라 기대합니다.

3
2개의 답글

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요