sgkstudio.
엔지니어링

스레드 자동 발행 — 같은 글이 9번 올라간 재시도 사고

스레드 자동 발행에서 같은 글이 9번 올라간 원인은 문장 필터 하나가 아니라 결함 셋이 겹친 결과였고, 그중 사고를 9배로 키운 쪽은 '실패했다'고 잘못 판정한 게시 확인과 상한 없는 재시도였습니다. 라이브 프로필에는 '스레드 체인 글 초안입니다'라는 한 줄로 시작하는 같은 첫 조각이 9개 쌓여 있었습니다. 처음엔 문장 필터의 빈틈만 보였지만, 필터만 고쳤다면 다른 문장으로 같은 사고가 또 났을 겁니다.

2026-09-27증상 — '스레드 체인 글 초안입니다'로 시작하는 같은 첫 조각 9개가 라이브에 쌓임(14분·34분·54분·1시간 3개·2시간 3개 전), 9건 전부 회수반증된 가설 — 문장 필터의 빈틈 하나가 원인이라는 첫 판단. 필터 구멍은 오염 1건을 설명할 뿐 9번 반복을 설명하지 못했다관측 수단 — 라이브 프로필, 발행 로그의 '검사 통과'와 '첫조각앞=False 끝조각뒤=True' 판정, 사고 당시 화면 문자열 재현진짜 원인 셋 — 머리말을 요구하던 정규식, 발행 전 검사의 축 부재, 개행까지 대조하던 게시 확인과 상한 없는 재시도수리 — 30자 이하 메타 문장 검사 신설, 모든 조각 모든 줄 검사, 공백 제거 비교, 시도 3회 상한검증 — 발행 기록 1,761줄 전수 검사 오탐 0·과거 오염 1건, 서로 다른 소재 2건 연속 체인 7조각 7/7 기록, 테스트 278건 통과(신규 8건)

증상 — 같은 첫 조각이 9개 쌓여 있었다

2026년 9월 9일, 운영자가 스레드 피드를 보다가 이상한 글을 발견했습니다. 체인 글 첫 조각 맨 위에 '스레드 체인 글 초안입니다.'라는 문장이 박혀 있었습니다. 독자에게 하는 말이 아니라, 모델이 자기가 무엇을 만들었는지 설명하는 메타 문장이 본문으로 새어 나온 경우입니다.

스레드 체인 글 초안입니다 이런 문장이 도대체 본문에 왜 들어가 있냐.

라이브 프로필을 열어 보니 문제는 한 건이 아니었습니다. 같은 첫 조각이 9개 쌓여 있었고, 올라간 시각을 거꾸로 세면 14분 전, 34분 전, 54분 전에 한 개씩, 1시간 전에 3개, 2시간 전에 3개였습니다. 오염된 문장 하나가 아니라 오염된 글 하나가 반복해서 나가고 있었다는 뜻입니다. 우선 9건을 전부 회수했습니다.

막대그래프. 라이브 프로필에서 확인한 같은 첫 조각 9개를 올라간 시각별로 나누면 14분 전 1개, 34분 전 1개, 54분 전 1개, 1시간 전 3개, 2시간 전 3개
같은 첫 조각 9개, 올라간 시각별

처음 의심한 것 — 메타 문장 필터의 빈틈

가장 먼저 눈에 들어온 곳은 메타 문장 필터였습니다. 이 봇에는 모델이 붙이는 설명 문장을 걸러 내는 정규식 `META_LEAD`가 이미 있었습니다. 2026년 8월 25일에 '아래는 규칙을 맞춰 다시 쓴 체인입니다.'라는 문장이 새어 나간 일을 잡으면서 만든 검사입니다. 같은 부류의 문장이 또 나갔으니, 그 필터가 이번 문장을 못 알아본 게 원인이라는 판단이 자연스러웠습니다.

실제로 필터에는 빈틈이 있었습니다. `META_LEAD`는 '아래는·다음은·수정한' 같은 머리말을 요구했는데, 이번 출력은 머리말 없이 '스레드 체인 글 초안입니다.'라는 명사구 한 줄이었습니다. 정규식 입장에서는 걸 이유가 없는 문장입니다. 여기서 정규식 한 줄만 고치고 끝냈다면 이 글은 한 문단으로 끝났을 겁니다.

무엇으로 확인했나 — 라이브 화면, 발행 로그, 판정 재현

필터 가설이 전부를 설명하는지 확인하려고, 추측 대신 기록을 봤습니다. 쓴 수단은 네 가지입니다.

  • 라이브 프로필 — 무엇이 몇 번, 어떤 간격으로 올라갔는가. 같은 첫 조각 9개와 시각을 여기서 셌다
  • 발행 로그의 검사 결과 — 오염된 초안이 발행 전 검사에서 어떤 판정을 받았는가. 로그에는 '검사 통과'가 찍혀 있었다
  • 발행 로그의 게시 확인 판정 — 올린 뒤 화면을 다시 읽은 결과. 로그에는 `첫조각앞=False 끝조각뒤=True`가 남아 있었다
  • 사고 당시 화면 문자열 재현 — 그 판정이 왜 나왔는지, 화면에서 읽은 문자열을 그대로 비교 함수에 다시 넣어 확인했다

두 번째와 세 번째 줄이 방향을 바꿨습니다. 오염된 글이 '검사 통과'를 받았다는 건 필터 말고 발행 전 검사에도 구멍이 있다는 뜻이고, 게시 확인이 False를 냈다는 건 봇이 이 글을 '못 올린 글'로 여겼다는 뜻입니다.

왜 필터 가설로는 9번을 설명할 수 없나?

필터의 빈틈은 오염된 문장이 초안에 남은 이유를 설명합니다. 하지만 필터는 글을 몇 번 올릴지 정하지 않습니다. 필터가 놓친 글은 한 번 나가야 정상이고, 9번 나갈 이유는 필터 쪽에 없습니다. 반복을 만든 장치가 따로 있어야 했습니다.

모델이 비슷한 초안을 9번 새로 썼을 가능성도 따져 봤습니다. 그랬다면 문장이 조금씩 달라야 합니다. 그런데 라이브에 쌓인 건 같은 첫 조각이었습니다. 새로 쓴 글이 아니라 한 번 만든 초안이 다시 나간 겁니다. 게시 확인 로그의 False와 합치면 그림이 맞아떨어집니다. 올라간 글을 못 올라갔다고 판정했고, 봇은 그 초안을 다시 올렸습니다.

그래서 첫 가설은 틀렸다기보다 모자랐습니다. 필터는 세 결함 중 하나였고, 그 하나만 고쳤다면 다음번에 모델이 또 다른 모양의 메타 문장을 내는 날 같은 사고가 똑같이 9배로 났을 겁니다.

진짜 원인 — 결함 셋이 한 줄로 이어져 있었다

사고는 세 결함이 차례로 이어진 결과였습니다. 어느 하나라도 제대로 막았으면 9번까지 가지 않았습니다.

사고를 만든 결함 셋
결함무엇이 잘못됐나결과
① 필터의 빈틈`META_LEAD`가 '아래는·다음은·수정한' 같은 머리말을 요구했다머리말 없는 명사구 '스레드 체인 글 초안입니다.'를 못 잡았다
② 막는 곳의 부재걷어내는 함수 `strip_meta_lead`는 첫 조각 맨 앞 한 줄만 봤고, 발행 전 검사 `validate`에는 이 축이 아예 없었다오염된 초안이 로그에 '검사 통과'로 찍혔다
③ 증폭기게시 확인이 초안 원문을 개행까지 그대로 화면과 대조했다올라간 글을 실패로 읽고, 보관한 초안을 10분 뒤 다시 올렸다 — 9번

세 번째가 핵심입니다. 초안에는 문단 사이 빈 줄, 곧 `\n\n`이 들어 있었는데 스레드 화면에서는 이 개행이 한 줄로 접혀 보입니다. 게시 확인은 초안 원문과 화면 문자열을 개행까지 똑같이 비교했으니 첫 조각의 앞부분이 일치하지 않는다고 판정했고, 로그에 `첫조각앞=False`가 남았습니다. 실패로 기록한 글은 발행 기록 파일에 들어가지 않고, 보관한 초안은 10분 뒤 다시 발행 대기열에 오릅니다. 재시도 횟수에 상한이 없었으니 이 순환은 운영자가 발견할 때까지 이어졌습니다.

흐름도. 모델이 메타 문장이 섞인 초안을 쓰면 머리말을 요구하는 필터가 놓치고, 발행 전 검사에는 이 항목이 없어 통과한다. 게시 후 화면을 개행까지 대조한 게시 확인이 실패로 판정하고, 발행 기록에 적지 않은 초안이 10분 뒤 다시 발행돼 9번 반복한다
결함 셋이 이어진 경로

고친 방법 — 세 곳을 각각 막았다

한 곳만 고치면 나머지 두 결함이 다음 사고를 기다립니다. 그래서 셋을 모두 따로 닫았습니다.

  • ① 필터 — `META_LEAD_BARE`를 새로 만들었다. 머리말이 없는 메타 문장을 잡되, 정상 문장을 잘못 막는 오탐은 길이로 막는다. 조건은 30자 이하이면서 메타 낱말이 들어 있고 서술어로 끝나는 줄이다. 진짜 훅은 60자 하한을 받으므로 세 조건을 동시에 만족할 수 없다
  • ② 막는 곳 — 발행 전 검사 `validate`가 첫 줄만이 아니라 모든 조각의 모든 줄을 본다. 걷어내는 함수와 세워 막는 검사를 둘 다 둬서, 걷어내기가 놓쳐도 검사에서 떨어진다
  • ③ 증폭기 — 게시 확인이 양쪽 문자열의 공백과 개행을 지운 뒤 비교한다. 여기에 더해 보관한 초안마다 시도 횟수를 적고, 3회에 이르면 더 올리지 않고 끊는다
코드 비교. 이전에는 초안 원문을 개행까지 그대로 화면과 비교해 첫조각앞=False가 나오고 실패로 기록한 뒤 10분 뒤 재발행했다. 이후에는 양쪽 공백을 지우고 비교해 게시 확인으로 읽고, 시도 횟수 3회에서 끊는다
게시 확인 비교 — 이전과 이후

③의 두 수리는 역할이 다릅니다. 공백 제거는 이번 오판을 고치고, 3회 상한은 앞으로 생길 모르는 오판을 대비합니다. 비교 로직은 언젠가 또 틀릴 수 있지만, 상한이 있으면 틀려도 같은 글이 최대 3번에서 멈춥니다.

고치고 나서 무엇을 더 찾았나?

새 검사기를 만들었으니 과거에도 같은 오염이 있었는지 봤습니다. 발행 기록 1,761줄 전체에 새 검사기를 돌렸더니 오탐은 0건, 과거 오염은 1건이었습니다.

숫자 카드. 발행 기록 1,761줄 전수 검사에서 정상 글을 잘못 잡은 오탐 0건, 과거에 오염된 채 나간 글 1건, 그 글이 아무도 모른 채 남아 있던 기간 4일
발행 기록 1,761줄 전수 검사 결과

그 1건은 2026년 9월 5일에 나간 글이었습니다. 훅 첫 줄이 '90자, 60~220자 범위에 맞습니다. 최종 스레드 체인 글입니다.'였습니다. 모델이 글자 수 규칙을 맞췄다고 스스로 보고한 문장이 그대로 올라간 경우입니다. 조회 180, 좋아요 1로 노출은 이미 끝난 글이었지만, 프로필에 계속 남는 문장이라 체인 7조각을 회수하고 발행 기록에 회수 표시를 적었습니다.

이 글은 4일 동안 아무도 몰랐습니다. 우리는 나간 글을 다시 읽지 않기 때문입니다. 발행 전 검사는 한 번 통과하면 그걸로 끝이고, 나간 뒤에 틀린 것을 찾는 장치는 없었습니다. 이번 전수 검사가 그 공백을 한 번 메웠고, 뒤에 적은 상설 검사가 계속 메웁니다.

실제 발행으로 어떻게 확인했나?

테스트만으로 끝내지 않고 두 방향을 확인했습니다. 먼저 사고 당시 화면 문자열을 그대로 넣었더니, 옛 비교는 `첫조각앞=False 끝조각뒤=True`를 내며 로그에 남은 그 판정을 정확히 재현했고, 새 비교는 같은 입력을 게시 확인으로 읽었습니다. 정상 발행분 3건은 옛 비교와 새 비교 모두 통과해, 고치면서 멀쩡한 경우를 깨뜨리지 않았다는 점도 확인했습니다.

그다음은 실제 발행입니다. 수정 후 첫 사이클에서는 초안이 훅 검사에서 떨어져 발행까지 가지 않았습니다. 18시 12분 사이클에서 사고를 낸 바로 그 소재가 체인 7조각으로 나갔고, 발행 기록에 7조각 중 7조각이 적혔습니다. 라이브 프로필을 다시 열어 보니 훅 첫 줄은 '업종 평균만 믿고 아이템을 골랐으면 큰일 날 뻔했습니다.'였고, 메타 서두 0건, 중복 0건이었습니다.

한 건으로는 우연일 수 있어 다른 소재로 강제로 한 번 더 돌렸습니다. 카카오 챗봇 기술 검토를 다룬 소재도 체인 7조각, 기록 7/7로 끝났습니다. 두 번째 건은 훅 유형과 연결어미 쉼표 비율 55퍼센트 때문에 발행 전 검사에서 두 번 반려된 뒤 3회차에 통과했습니다. 검사는 여전히 제 일을 하고, 게시 확인은 이제 올라간 글을 올라갔다고 읽습니다.

수정 후 실제 발행 2건
건체인 조각발행 기록라이브 재확인
사고를 낸 소재7조각7/7메타 서두 0건 · 중복 0건
다른 소재(강제 재실행)7조각7/7발행 전 검사 2회 반려 후 3회차 통과

③이 남아 있었다면 두 건 모두 확인 실패로 기록에 들어가지 않고 재시도 대기열로 돌아갔을 겁니다. 서로 다른 소재 두 건이 연속으로 기록까지 끝났다는 점이 세 결함이 다 닫혔다는 증거입니다.

같은 실수를 막는 장치는 무엇인가?

테스트 278건이 통과했고, 그중 8건이 이번에 새로 넣은 테스트입니다. 새 테스트는 사고를 다시 부르는 입력을 그대로 고정하는 데 썼습니다.

  • 사고 문장 두 개를 그대로 고정 — '스레드 체인 글 초안입니다.'와 9월 5일 글의 '최종 스레드 체인 글입니다.' 문장이 발행 전 검사에서 반드시 떨어지게 했다
  • 발행 기록 전수 오탐 검사 상설화 — 1,761줄에 한 번 돌리고 끝낸 검사를 테스트로 만들어, 필터를 바꿀 때마다 과거 정상 글을 잘못 막지 않는지 다시 잰다
  • 사고 로그 재현 2건 — 옛 비교가 False를 내던 화면 문자열로 새 비교가 게시 확인을 내는지, 정상 발행분은 둘 다 통과하는지를 양방향으로 못박았다
  • 재시도 상한 3회 — 판정이 또 틀려도 같은 글이 끝없이 나가지 않는다

이 사고의 교훈은 세 번째 결함에 있습니다. 필터 빈틈은 검사의 구멍이고 발행 전 검사의 누락은 이중화의 누락인데, 사고를 9배로 키운 건 '실패했다'는 판정이 틀렸을 때 도는 재시도였습니다. 재시도 상한이 없는 자동 발행기는 검증기가 한 번 오판하는 순간 같은 글을 끝없이 내보냅니다. 재시도를 넣을 때는 '몇 번까지'를 같이 넣어야 합니다.

스레드 자동 발행 봇을 만든다면 무엇부터 점검해야 하나?

SNS 자동 발행은 초안 생성보다 '올라갔는지 확인하고 다시 시도하는' 부분에서 사고가 커집니다. 이번 일에서 뽑은 점검 순서입니다.

  • 재시도에 상한이 있는가 — 없다면 확인 로직이 한 번 틀리는 순간 같은 글이 반복해서 나간다
  • 게시 확인이 화면이 바꾸는 것을 무시하는가 — 개행 접힘, 공백 정리처럼 플랫폼이 표시 과정에서 바꾸는 부분은 비교 전에 양쪽에서 똑같이 지운다
  • 걷어내는 쪽과 막는 쪽이 둘 다 있는가 — 한쪽이 첫 줄만 보면, 다른 쪽은 모든 줄을 본다
  • 새 필터를 과거 발행분 전체에 돌려 봤는가 — 오탐도 재고, 몰랐던 과거 사고도 찾는다
  • 나간 글을 다시 읽는 장치가 있는가 — 발행 전 검사만으로는 4일 동안 아무도 모르는 글이 남는다

LLM이 쓰는 글을 사람 손 없이 내보내는 구조는 모델보다 그 주변의 검사와 재시도 설계가 품질을 가릅니다. 같은 봇 계열에서 답글이 상대 글과 무관한 경험을 끌어온 사고는 LLM 자동 답글 — 재개발 글에 광고 경험이 붙어 나간 이유에 적었습니다. 사내에 비슷한 자동 발행이나 자동 응대를 두려는데 어디서부터 점검할지 막막하다면 상담으로 현재 구조를 알려 주세요.

같은 방식으로 우리 업무를 재보고 싶다면

이 글에 쓴 계측·판정·자동화는 저희가 실제로 매일 돌리는 방식 그대로입니다. 어떤 작업을 자동화할 수 있고 어디를 사람이 잡아야 하는지, 현재 업무 흐름을 놓고 같이 짚어 드립니다.

새 글이 올라오면 알려 드립니다

이런 계측 기록을 새로 쓰면 메일로 한 통 보냅니다. 광고나 판촉은 보내지 않습니다.