자동화 파이프라인7 분 읽기

내 로테이션은 띠의 절반에만 닿을 수 있었다

릴스를 주말에만 올리던 슬롯을 매일로 늘리면서, 날짜 홀짝으로 띠(12)와 일주(60갑자)를 번갈아 내보내기로 했습니다. 그런데 홀짝 필터와 로테이션 나머지 연산을 그대로 겹치면 각 콘텐츠의 절반은 영영 순서가 오지 않는 구조였습니다.

#rotation#modulo#scheduling#reels#hashtags
왼쪽은 짝수 날과 12로 나눈 나머지가 짝수 띠에만 닿는 구조, 오른쪽은 날짜를 2로 나눈 인덱스로 12띠와 60갑자를 모두 도는 구조를 비교한 그림
홀짝 필터와 로테이션 modulus가 2를 공유하면 콘텐츠의 절반은 순서가 오지 않습니다.

주말 릴스를 매일 릴스로 늘렸습니다

운세 콘텐츠를 자동으로 올리는 스크립트에는 릴스 슬롯이 있었고, 원래는 주말에만 돌았습니다. 제가 가진 포맷 중 도달이 가장 큰 것이 릴스라고 판단해서 이 슬롯을 매일 돌리기로 했습니다.

매일 같은 소재를 내보낼 수는 없었습니다. 그래서 날짜 홀짝에 따라 두 종류를 번갈아 쓰기로 했습니다. 한쪽 날에는 12띠 중 하나를, 다른 쪽 날에는 60갑자 일주 중 하나를 나레이션 릴스로 만듭니다. 일주 쪽은 새 템플릿을 만들지 않고 기존 fortune 템플릿을 그대로 썼습니다. 생성 쪽에는 GAPJA 목록(60갑자)과 spec_ilju, 그리고 ilju:<갑자> 형태의 kind만 추가했습니다.

여기까지는 간단해 보였습니다. 문제는 "오늘은 몇 번째 띠인가"를 정하는 인덱스였습니다. 하루에 하나씩 넘어가는 로테이션에 격일 필터를 얹으면, 그 로테이션이 실제로 끝까지 한 바퀴를 도는지 확인해 보셨나요?

홀짝 필터와 나머지 연산이 부딪히는 지점

날짜에서 나온 정수를 o라고 하겠습니다. 가장 먼저 떠오르는 코드는 이런 모양입니다.

# 단순화한 예시
if o % 2 == 0:
    kind = ZODIAC[o % 12]
else:
    kind = GAPJA[o % 60]

이 코드에는 함정이 있습니다. 띠가 나오는 날의 o는 언제나 짝수이고, 12도 짝수입니다. 짝수를 12로 나눈 나머지는 언제나 짝수이므로 o % 12는 0, 2, 4, 6, 8, 10만 나옵니다. 12띠 중 6개는 영원히 순서가 오지 않습니다. 일주도 마찬가지입니다. 일주가 나오는 날의 o는 언제나 홀수이고, 60은 짝수라서 o % 60은 홀수만 나옵니다. 60갑자 중 30개만 계속 돌게 됩니다.

에러는 나지 않습니다. 매일 무언가가 올라가고, 며칠치 결과를 눈으로 보면 다양하게 섞여 보입니다. 홀짝 필터와 로테이션 modulus가 공약수 2를 공유하는 한, 콘텐츠 풀의 절반이 조용히 빠진 채로 계속 돌 뿐입니다.

당신이라면 여기서 modulus를 홀수로 바꾸겠습니까, 아니면 인덱스 자체를 바꾸겠습니까?

인덱스를 o//2로 나눴습니다

12와 60은 콘텐츠 개수라서 바꿀 수 없습니다. 그래서 인덱스를 바꿨습니다. 홀짝은 o로 판정하고, 로테이션 인덱스는 o // 2로 계산합니다.

# 단순화한 예시
idx = o // 2
if o % 2 == 0:
    kind = ZODIAC[idx % 12]
else:
    kind = GAPJA[idx % 60]

짝수 날끼리 비교하면 o // 2가 1씩 늘어나고, 홀수 날끼리도 1씩 늘어납니다. 각 콘텐츠 종류는 자기 차례가 올 때마다 연속된 인덱스를 받으므로 홀짝 필터가 인덱스를 다시 걸러내지 않습니다. 실제로 120일을 돌려서 12띠와 60갑자가 전부 한 번 이상 나오는지 확인했습니다. 120일이면 일주 날이 60번 오기 때문에, 60갑자를 한 바퀴 다 도는 데 필요한 최소 기간입니다.

해시태그도 같이 좁혔습니다

같은 커밋에서 해시태그도 바꿨습니다. #운세, #사주, #타로, #일상스타그램 같은 메가태그는 뺐습니다. 대신 fortune_tags(kind)가 그날 kind에 맞는 띠·별자리·일주 니치 태그를 앞쪽에 붙이고, 전체 개수는 12개에서 자릅니다. 넓은 태그 몇 개를 공통으로 붙이던 방식에서, 그날 콘텐츠를 설명하는 태그를 앞에 세우는 방식으로 바꾼 것입니다.

자가진단 체크리스트

  • 격일·요일·홀짝 같은 필터 뒤에서 날짜 % N으로 로테이션 인덱스를 뽑고 있다면, 필터 주기와 N이 공약수를 공유하지 않는지 확인했습니까?
  • 로테이션을 넣고 나서 "며칠 돌려보니 다양하게 나온다"에서 멈추지 않고, 풀 전체가 한 번 이상 나오는 데 며칠이 걸리는지 실제로 시뮬레이션해 봤습니까?
  • 같은 날짜 정수를 "어떤 종류를 낼지"와 "그 종류 안에서 몇 번째를 낼지" 두 판정에 동시에 쓰고 있지 않습니까?

솔직한 부분

이 버그는 커밋 전에 잡았고, 절반만 도는 로테이션이 실제로 배포된 기간은 없습니다. 제가 검증한 것은 120일 안에 모든 띠와 일주가 나온다는 커버리지뿐입니다. 매일 릴스로 바꾼 것이 도달에 도움이 되는지, 메가태그를 빼고 니치 태그를 앞에 세운 것이 노출을 늘리는지 줄이는지는 이 커밋 시점에는 데이터가 없습니다. 릴스가 도달이 가장 큰 포맷이라는 것도 제 판단이었지 이 변경으로 측정한 결과는 아닙니다.

관련 글