n8n Threads 포스팅 자동화 : 본문 + 댓글 형식으로 자동화 포스팅 만들기 💬

소개

Threads에 본문과 댓글을 이어서 자동으로 포스팅하는 워크플로우를 만들었습니다.
기존에는 본문과 댓글을 각각 복사-붙여넣기 하다 보니 실수도 많고 반복도 많았는데, 만들고 보니 작업이 무척 편해졌습니다.

Threads 글 소스는 이전에 만들었던 자동화 시스템(n8n 롱폼 블로그 작성 자동화 #3)에서 생성된 콘텐츠입니다.

https://www.gpters.org/nocode/post/n8n-long-foam-blog-ydRoK7ooB8ZPx7F

진행 방법

전체 흐름은 아래와 같습니다:

앱의 프로세스를 보여주는 흐름도

1. Schedule Trigger

  • 정해진 시간마다 워크플로우를 시작합니다.

2. Get row(s) in sheet1

  • 구글 시트에서 아직 처리되지 않은 블로그 글을 불러옵니다.

  • threadYes = 'Yes' 인 row를 호출

    한국어 문서의 스크린 샷

3. Loop Over Items

  • 여러 개의 블로그 글이 있을 경우, 한 줄씩 반복 처리합니다.

4. Replace Me (전처리용 노드)

  • 프롬프트에서 사용할 수 있게 텍스트를 정제합니다.

5. Basic LLM Chain2 (Gemini)

  • 입력된 블로그 글을 바탕으로 Threads 포스팅 형식(JSON: topic_tag, body, reply)을 생성합니다. (위 2번 노드의 H열이 입력된 블로그 글)

  • 이전에 설계한 시스템/user 프롬프트와 출력 스키마를 사용했습니다.

    • User Prompt

      너는 쓰레드 전문가다.
      너는 긴 유튜브 대본을 하나의 Threads용 인기 포스트로 
      축약 정리할 JSON만 출력한다. JSON 외의 텍스트,
      백틱, 설명, 주석, 아무것도 절대 출력하지 마.
      
      작성 규칙:
      - body: 사람들의 관심을 끌 수 있는 내용 중심
        * 충격적이거나 놀라운 사실
        * 호기심을 자극하는 질문이나 상황
        * 트렌드나 화제성 있는 내용
        * 개인적 경험이나 감정적 공감대
      
      - reply: body글에 이어서 핵심 내용 작성
        * body에서 제기한 내용의 구체적 설명
        * 실용적이고 유용한 정보
        * 기술적 디테일이나 방법론
        * actionable한 팁이나 가이드
      
      **주의사항**:
      * 본문과 댓글의 내용이 자연스럽게 이어져야 해.
      * 이모지는 과하지 않게, 내용의 흐름을 방해하지 않는 선에서 사용해.
      * 핵심 해시태그를 포함하되, 남발하지 않도록 유의해.
      * 내용의 전문성은 유지하되, 독자가 쉽게 이해할 수 있도록 쉽게 풀어 써줘.
      - 캐주얼하고 친근한 톤 (반말 사용)
      - 이모지를 문장 시작이나 강조 포인트에 활용
      - 해시태그는 트렌딩 키워드 중심으로 2-3개
      - 줄바꿈으로 가독성 극대화
      
      출력 스키마(수정 금지):
      
      {
        "topic_tag": "주제명_키워드",
        "body": "🔥 충격적이거나 호기심 유발하는 첫 문장\n\n💭 사람들이 궁금해할 만한 내용\n감정적 공감대나 개인적 경험\n\n🤔 호기심을 자극하는 마무리\n\n#해시태그1 #해시태그2 #해시태그3",
        "reply": "👆 위에서 말한 그 내용은...\n\n💡 구체적인 핵심 설명\n기술적 디테일이나 방법\n\n⚡ 실용적인 팁이나 가이드\n즉시 적용 가능한 내용\n\n#해시태그1 #해시태그2"
      }
      
      ---
      다음 YouTube 블로그 글을 읽고, 위 가이드라인에 맞춰 **body**와 **reply**를 생성해 주세요.
      ---
      {{ $json.blogText }}
    • System Prompt

      너는 Threads 인기글 작성 전문가다.
      유튜브 블로그 글을 받아서 Threads용 포스트로 변환한다.
      반드시 JSON 형식으로만 출력하고, 다른 텍스트는 절대 포함하지 마.

6. Code

  • LLM이 반환한 raw JSON 문자열에서 body, reply, topic_tag 필드를 분리합니다.

    • 위 Prompt에서 분리하고자 했으나, 분리에 실패해서 부득불 Code 노드로 해결

  • " ☞ 댓글에서 확인" 문구를 본문(body) 마지막에 삽입.

    • 댓글로 유도하기 위해서 본문(body) -> 댓글(reply) 두 개 글(포스팅) 구조화

      // AI 응답에서 JSON 추출 및 파싱
      const inputData = $input.first().json;
      let aiResponse = inputData.text || inputData.content || inputData.output || '';
      
      // JSON 문자열 정리 (백틱, 마크다운, 불필요한 문자 제거)
      let cleanJson = aiResponse
        .replace(/```json\n?/g, '')
        .replace(/```\n?/g, '')
        .replace(/^[^{]*/, '') // JSON 시작 전 문자 제거
        .replace(/[^}]*$/, '') // JSON 끝 후 문자 제거
        .trim();
      
      // Body 텍스트에 "☞ 댓글에서 확인" 추가하는 함수
      function addCommentPrompt(bodyText) {
        if (!bodyText) return bodyText;
        
        // \n\n으로 분리
        const parts = bodyText.split('\n\n');
        
        if (parts.length >= 2) {
          // 마지막 부분이 해시태그인지 확인
          const lastPart = parts[parts.length - 1];
          if (lastPart.startsWith('#')) {
            // 마지막에서 두 번째 부분(문장)에 " ☞ 댓글에서 확인" 추가
            parts[parts.length - 2] = parts[parts.length - 2] + ' ☞ 댓글에서 확인';
            return parts.join('\n\n');
          }
        }
        
        return bodyText + ' ☞ 댓글에서 확인';
      }
      
      try {
        // JSON 파싱
        const parsed = JSON.parse(cleanJson);
        
        // Body에 댓글 안내 문구 추가
        const modifiedBody = addCommentPrompt(parsed.body || "body 내용을 찾을 수 없습니다");
        
        // 개별 필드로 분리하여 출력
        return [{
          json: {
            topic_tag: parsed.topic_tag || "파싱_실패",
            body: modifiedBody,
            reply: parsed.reply || parsed.reply1 || "reply 내용을 찾을 수 없습니다",
            original_response: aiResponse // 디버깅용
          }
        }];
        
      } catch (error) {
        // 파싱 실패시 원본 텍스트에서 수동 추출 시도
        console.log("JSON 파싱 실패, 수동 추출 시도:", error.message);
        
        const topicMatch = aiResponse.match(/"topic_tag":\s*"([^"]+)"/);
        const bodyMatch = aiResponse.match(/"body":\s*"([^"]+(?:\\.[^"]*)*?)"/);
        const replyMatch = aiResponse.match(/"reply":\s*"([^"]+(?:\\.[^"]*)*?)"/);
        
        const extractedBody = bodyMatch ? bodyMatch[1].replace(/\\n/g, '\n') : "body 추출 실패";
        const modifiedBody = addCommentPrompt(extractedBody);
        
        return [{
          json: {
            topic_tag: topicMatch ? topicMatch[1] : "추출_실패",
            body: modifiedBody,
            reply: replyMatch ? replyMatch[1].replace(/\\n/g, '\n') : "reply 추출 실패",
            error: error.message,
            original_response: aiResponse
          }
        }];
      }

7. Update row in sheet

  • 생성한 body글과 reply글을 구글 시트에 기록(2번 노드 구글 시트의 K열, L열)

8. 토큰 행 가지고 오기

9. 쓰레드 포스팅 셋팅

  • API 호출에 필요한 값을 Set 노드에서 준비합니다.

10. Body

  • body 텍스트를 Threads에 게시합니다. 이때 새 스레드로 올라갑니다.

    녹색 화면이있는 컴퓨터 화면의 스크린 샷

11. Reply

  • 중요: 이 노드는 Body가 만든 스레드 아래에 댓글을 달도록 구성돼야 합니다.

  • 초반엔 이 Reply도 새로운 스레드로 올라가는 문제가 있었고, 그걸 해결하기 위해 reply_to_id를 추가했습니다.

  • 이 ID는 body >id 필드를 가져오면 됩니다.

    여러 가지 다른 설정을 보여주는 컴퓨터 화면의 스크린 샷

결과와 배운 점

배운 점

  • Threads API는 reply 요청도 /threads 엔드포인트를 쓰지만, reply_to_id 파라미터를 줘야 댓글로 인식됩니다.

  • auto_publish_text 옵션을 true로 설정하면 게시글이 임시 저장 없이 바로 공개됩니다. Threads API에서는 이 옵션을 기본값 false로 두는 경우, 포스트가 비공개 상태로 올라갈 수 있기 때문에 자동화 시 반드시 true로 설정해야 안정적인 게시가 가능합니다.

😵 시행착오

  • 댓글이 본문으로 따로 올라오는 문제: reply가 아닌 새글로 등록됨 → reply_to_id 해결

    • 댓글을 Threads에 달려면 본문(post) ID가 정확해야 합니다. 그렇지 않으면 reply로 생성된 글을 새로운 본문으로 올려버림. 그 해결책은 본문을 올린 뒤에 응답으로 받은 id 값을 새로운 parameter로서  reply_to_id를 생성하고 id값을 정확히 전달하는 것. 이걸 놓치면 댓글이 아예 등록되지 않거나 별도 포스트로 올라갑니다.

  • LLM에서 바로 JSON 형태로 topic_tag, body, reply를 출력하도록 프롬프트를 구성했으나, 실제로는 백틱(```json)이 포함되거나 문자열 파싱 오류가 발생했습니다. 결국 안정적인 파싱을 위해 Code` 노드를 하나 추가하여, raw 텍스트에서 백틱 제거 → JSON.parse() 처리 → 필드 분리 작업을 수행하도록 했습니다. 

    • LLM 출력값을 안정적으로 분리하려면 Function 노드에서 JSON 파싱 처리를 직접 해주는 게 좋습니다.

🎯 자동화 후 효과

  • Threads 포스팅에 소요되는 시간이 엄청 줄어들 것으로 예상

  • Gemini의 글쏨씨가 대박.

도움 받은 글

3
1개의 답글

뉴스레터 무료 구독