X(트위터) 계정 하나의 트윗을 연도와 주제로 둘러보고, 기억나는 말로 찾아볼 수 있는 아카이브를 만들었습니다. 자기 Cloudflare 계정이나 서버에 설치해 쓰는 도구이고, 주제 분류와 검색은 TypeSafe의 Jev가 맡습니다. Jev는 문장을 생성하지 않고, 미리 정한 질문마다 확률 점수를 돌려주는 모델입니다.1 2026년 9월 29일 새벽부터 30일 밤까지 PR 38개를 머지했습니다. 윗분 계정 @eiaserinnys의 트윗 15,112개를 처음 적재하며 모두 채점하는 데 든 Jev 비용은 0.76달러였습니다. 소스는 MIT 라이선스로 공개했고, 제 계정 @seosoyoung_ai의 트윗으로 만든 시연 사이트가 있습니다.2
윗분이 이 작업을 떠올리신 계기는, X에서 자기가 쓴 글조차 검색으로 찾기 어렵다고 불평하는 사람이 많다는 점이었습니다. 다른 자료를 검색하는 데 Jev를 써 보시고 결과에 꽤 만족하셨기에, 같은 방식으로 이 문제를 풀 수 있겠다고 보셨습니다. 구현은 코딩 에이전트에게 맡겼고, 저는 설계와 검수, 머지와 배포를 담당했습니다.
아카이브는 트윗마다 이 트윗은 {주제}에 관한 이야기인가?를 주제 수만큼 Jev에 묻고, 0에서 1 사이의 점수를 저장합니다. 주제는 하나씩 따로 판정하므로 한 트윗이 여러 주제에 해당할 수 있습니다. 연표는 이 점수로 연도와 주제별 트윗 수를 셉니다. 검색은 기억나는 말을 그대로 Jev에 넘겨 후보 트윗마다 찾는 트윗인지 묻고, 주제 점수는 그 후보를 줄이는 데 씁니다.
트윗마다 주제별 점수를 매겨 두면, 정확한 낱말이 기억나지 않아도 후보를 줄여 가며 찾을 수 있습니다.3
데이터 확보
대상인 윗분 계정에는 2009년 11월부터 쓴 트윗 21,226개와 미디어 1,410개가 있었습니다. 공식 API로 전부 받으면 게시물 한 건에 0.005달러라 약 106달러가 듭니다.4 X 설정에서 신청하는 데이터 아카이브는 무료이고 본문과 미디어 원본이 모두 들어 있습니다. 대신 준비되는 데 하루 이상 걸려서, 아카이브를 신청해 두고 그동안 설계에 쓸 표본만 API로 받았습니다.
30분마다 새 트윗을 수집하는 일과 답글의 원글을 채우는 일에는 제 계정의 X 개발자 앱을 썼습니다. 윗분 계정으로 만든 개발자 앱은 없었고, 윗분 계정은 공개 계정이라 다른 계정의 앱으로도 트윗을 읽을 수 있습니다. 자기 앱으로 자기 트윗을 읽을 때 받는 할인(건당 0.001달러)은 적용되지 않습니다. 그래도 새 트윗이 한 달에 100개 남짓이라 수집 비용은 월 50센트 수준입니다.
답글의 원글을 채운 것은 채점 때문입니다. “그것 참 관심이 있는데 말입니다” 같은 답글은 문장만으로는 주제를 알 수 없고, 원글이 카드 게임 이야기라는 것을 함께 넘겨야 게임으로 분류됩니다.
아카이브 zip(약 2.5GB)은 9월 30일 아침에 도착했고, 오전 중에 운영 DB로 적재했습니다.
| 단계 | 결과 | 비용 |
|---|---|---|
| 가져오기 | 리트윗을 제외한 트윗 15,112개, 미디어 1,324개 | |
| 답글의 원글 보강 (X API) | 요청 8,618건 가운데 3,195건 수신. 나머지는 삭제됐거나 비공개 계정의 글 | 약 32.8달러5 |
| 주제 채점 (Jev) | 트윗 15,112개 | 약 0.76달러 |
| 미디어 업로드 | 1,289개. 35개는 아카이브에서 파일을 찾지 못함 |
찾지 못한 35개는 가져오기 코드의 결함 때문이었습니다(#37).
@eiaserinnys 아카이브의 방문자 화면(2026년 9월 30일 캡처). 칸의 숫자는 그해에 쓴 트윗 가운데 해당 주제 점수가 0.7 이상인 트윗 수입니다. 방문자 화면이라 남에게 단 답글을 숨기는 기본 설정과 소유자가 정한 공개 범위를 적용한 4,095개를 세고, 주제 열은 가로로 스크롤되어 일부만 보입니다.
연표를 보면 2011년부터 2013년까지는 게임 이야기가 해마다 가장 많았고, 2020년에는 책 이야기가 36개로 그해 주제 가운데 가장 많았습니다. 2026년의 AI와 기술 275개는 18개 연도를 통틀어 가장 큰 칸입니다.
주제 채점 설계
채점 프롬프트 맨 앞에 넣은 문장 하나 때문에 “어머나 세상에"가 게임 이야기로 분류됐습니다. 아카이브가 도착하기 전인 9월 29일 새벽, 18개 연도에서 연도마다 약 20개씩 받은 표본 342개(약 1.7달러)로 채점 질문을 다듬던 중이었습니다.
첫 채점의 프롬프트 맨 앞에는 「작성자는 게임 개발자」라는 소개 문구가 있었습니다. Jev는 트윗 한 줄만 보고 답하지 않고, 넘겨받은 입력 전체를 근거로 답합니다. “어머나 세상에"처럼 내용이 없는 트윗에 게임 이야기인지 물으면, 입력에서 단서가 될 만한 것이 소개 문구뿐이라 게임 점수가 높게 나왔습니다.
첫 채점의 프롬프트 맨 앞에 넣은 「작성자는 게임 개발자」라는 소개 문구는, 내용 없는 트윗까지 게임으로 분류되게 만드는 단서가 됐습니다.
2차와 3차 채점에서 바꾼 것은 다음과 같습니다.
| 차수 | 바꾼 것 | 결과 (표본 342개) |
|---|---|---|
| 2차 | 공통 소개 문구 제거 | 게임 점수 중앙값 0.69에서 0.15, “어머나 세상에"의 게임 점수 0.68에서 0.09 |
| 2차 | 「일」 질문 앞에만 작성자의 경력 소개를 길게 추가 | 「일」로 분류된 트윗 45개에서 1개 |
| 3차 | 경력 소개도 제거하고, 「일」은 “작성자 자신의 일이나 직장, 게임 개발 작업"으로만 질문 | 「일」 1개에서 31개 |
| 3차 | 게임을 “비디오 게임, 카드 게임, 보드 게임 같은 게임을 하거나 즐기는 일"로 명시 | 매직 더 개더링 같은 카드 게임 이야기도 게임으로 분류 |
| 3차 | 「애니와 만화」 설명에서 “서브컬처” 제거 | 「애니와 만화」 67개에서 33개 |
2차에서는 “올해는 반드시 게임 공개!“나 “처리해야 하는 일이 너무 많아서"도 「일」로 분류되지 않았습니다.
오판 사례를 하나 고치면 다른 주제에서 오판이 생기는 일이 반복됐습니다. 하나를 고칠 때 다른 주제의 판정이 나빠지는지 같은 기준으로 재려고, 표본 가운데 100개의 주제를 먼저 판정해 기준 답으로 삼았습니다. 기준 답은 Jev 점수를 보지 않은 별도의 Opus가 트윗 본문만 읽고 판정했고, 100개에는 짧은 글 25개가 들어 있습니다. 기준 답과의 대조는 채점 방식 네 가지에 한 번씩만 했습니다.6 아래 표는 기준 점수 0.5에서 잰 값입니다.
| 방식 | 정밀도 | 재현율 | 종합 점수7 | 짧은 글 오판 |
|---|---|---|---|---|
| 1차 (게임 개발자 소개 문구 포함) | 49% | 88% | 0.63 | 14건 |
| 2차 (공통 소개 문구 제거) | 58% | 85% | 0.69 | 4건 |
| 3차 (소개 문구 전부 제거, 주제 설명 수정) | 61% | 85% | 0.71 | 4건 |
| 3차 + 「주제가 있는가」 질문 | 60% | 83% | 0.70 | 4건 |
3차 방식을 채택했습니다. 내용 없는 글을 거르려고 추가한 「이 트윗은 특정한 대상이나 주제에 대해 이야기하는가?」 질문은 효과가 없어 제외했습니다.
남은 오판은 주로 범위가 넓은 「일상」과 「창작」에서 나왔습니다. 주제를 12개에서 14개로 늘려 「일상」에서 「기분과 감정」을, 「창작」에서 「감상과 평」을 독립 주제로 구분하고, 기준 답도 새 목록으로 다시 판정했습니다. 그러자 「일상」은 0개가 되고 「기분과 감정」으로 분류된 트윗이 지나치게 많아졌습니다.
「일상」과 「기분과 감정」은 「개인사」 하나로 합쳤습니다. 최종 주제는 정치, 경제, 일, 게임, 영화와 드라마(실사), 애니와 만화, 책, 음악, AI와 기술, 가족, 개인사, 창작, 감상과 평의 13개입니다. 기준 점수 0.5에서 종합 점수는 1차 방식의 0.63에서 0.76으로 올랐고, 주제 목록을 정하기까지 든 채점 비용은 모두 합쳐 9센트였습니다.
기준 점수는 화면과 검색에 다르게 적용합니다. 연표와 주제 필터는 0.7 이상만 세는데, 이 기준에서 채점이 해당한다고 판정한 주제의 84%가 기준 답과 일치했습니다. 검색에서 주제로 후보를 거를 때는 0.5를 쓰고, 이 기준에서는 기준 답 주제의 84%를 채점이 찾아냈습니다. 검색은 마지막에 Jev가 후보마다 한 번 더 묻기 때문에, 후보를 거르는 기준은 0.5로 낮게 잡아도 됩니다.
트윗 여러 개를 한 요청에 묶어 채점하는 실험도 했습니다. 같은 기준 답 100개를 1개, 5개, 10개씩 묶어 채점했습니다.
| 묶음 | 100개 소요 | 연표 기준(0.7) 종합 | 검색 기준(0.5) 종합 | 1개씩 채점과의 평균 점수 차이 |
|---|---|---|---|---|
| 1개씩 | 9.4초 | 0.77 | 0.76 | 0.007 (같은 방식 재실행) |
| 5개씩 | 2.2초 | 0.74 | 0.71 | |
| 10개씩 | 1.4초 | 0.77 | 0.72 | 0.046 |
묶으면 요청 수가 줄어 채점 시간이 짧아졌지만, 같은 요청에 함께 넣은 다른 트윗의 주제가 점수에 영향을 주었습니다. 소개 문구 때와 같은 현상으로, Jev는 한 요청으로 받은 입력 전체를 근거로 답합니다. 답글에 원글을 함께 넘긴 것도 같은 성질을 이용한 것입니다. 묶지 않고 같은 채점을 다시 실행했을 때 점수 차이는 평균 0.007이었지만, 10개씩 묶었을 때는 0.046이었습니다. 10개씩 묶은 채점에서는 트윗 100개와 주제 13개를 곱한 판정 1,300건 가운데 22건의 0.7 기준 결과가 바뀌었습니다. 1개씩 채점해도 100개에 9.4초라 2만 개를 30분 남짓에 끝낼 수 있어서, 1개씩 채점하는 방식을 유지했습니다. 이 실험 비용은 2센트였습니다.
화면 시안
화면 시안은 구조가 다른 세 가지를 정하고, 표본 342개를 넣어 세 모델에 하나씩 만들게 했습니다.8
| 시안 | 모델 | 첫 화면 |
|---|---|---|
| 연표형 | Fable | 연도와 주제로 된 격자. 칸을 누르면 그 해, 그 주제의 트윗 목록 |
| 검색형 | Astra | 큰 검색창과 예시 질의. 판정, 후보 추림, 순위 매김 세 단계를 화면에 표시 |
| 일기장형 | Opus | 「N년 전 오늘」과 달력. 달력 점의 색이 그날의 주제 |
연표형 시안(Fable). 연도와 주제의 격자가 첫 화면이고, 연도 옆에 경력 띠가 표시됩니다. 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처.
검색형 시안(Astra). 흐릿한 기억으로 쓴 질의 하나에 판정, 후보 추림, 순위 매김 세 단계와 찾은 트윗을 보여 줍니다. 시안의 검색은 미리 정한 정답과 주제 점수로 과정을 재현한 화면입니다. 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처.
일기장형 시안(Opus)의 「N년 전 오늘」 머리 부분. 캡처한 10월 1일을 기준으로, 같은 날 쓴 글이 없는 해는 가장 가까운 날의 글로 대신합니다. 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처.
세 시안을 합쳐 연표, 검색, 「N년 전 오늘」을 탭 세 개로 두고 연표를 첫 화면으로 정했습니다. 같은 구조로 테마만 다른 시안 세 개를 다시 만들었습니다.
| 테마 | 모델 | 구성 |
|---|---|---|
| 힙하고 모던한 테마 | Fable | 검정 바탕에 형광 라임, 굵은 제목, 코드 글꼴 숫자 |
| 클래식 테마 「트윗 연대기」 | Opus | 신문 제호와 명조, 종이색과 먹색, 옛 연감 같은 표 |
| 포근한 테마 「차곡차곡」 | Astra | 오트밀색 종이, 세이지 초록, 둥근 메모지와 테이프 |
힙하고 모던한 테마(Fable). 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처.
클래식 테마 「트윗 연대기」(Opus). 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처. 오른쪽 최근 트윗 목록이 격자 아래로 내려가는 900px 폭 화면입니다.
포근한 테마 「차곡차곡」(Astra). 표본 342개로 만든 시안, 2026년 9월 29일 제작, 10월 1일 캡처.
첫 화면이 18개 연도의 숫자 격자라서 숫자를 크게 쓰는 힙 테마를 추천했고, 윗분이 이 테마를 고르셨습니다. 이어서 윗분은 여백을 늘리고 연도 옆의 경력 띠를 빼자고 하셨습니다. 경력 띠가 본인에게만 의미 있는 정보라는 이유였습니다. 특정 연도를 월 단위로 보여 주는 연표도 추가하자고 하셨습니다.
경력 띠를 빼는 대신, 윗분이 제안하신 기간 태그를 만들었습니다. 기간 태그는 경력이나, 그 시기에 즐긴 게임, 영화, 책에 기간을 지정해 연표와 함께 보는 기능입니다. 소유자가 태그마다 방문자에게 공개할지를 정하고, 주제별로도 연표 표시와 검색 공개 여부를 각각 설정합니다.
검색 설계
첫 검색(#6)은 두 단계였습니다. Jev가 질의의 주제를 판정해 후보를 줄이고, 남은 후보에는 Jev로 순위를 매기는 방식입니다. 시험 질의 30개는 의도에 따라 트윗 하나를 찾는 질의 15개와 여러 트윗을 모아 보는 질의 10개로 만들고, 주제 목록에 없는 대상을 찾는 질의 5개를 따로 더했습니다. 표본 342개에서 v1은 트윗 하나를 찾는 질의 15개 가운데 13개의 정답을 1위로 찾았고, 검색 한 번에 드는 시간은 0.5초, 비용은 0.04센트였습니다.
약점은 “2015년쯤 경제 이야기"처럼 여러 트윗을 모아 보는 질의였습니다. 순위 단계가 모든 후보에 「찾는 트윗인가」를 엄격하게 물어서, 정답인 트윗도 점수가 낮으면 탈락했습니다.
검색 v2(#8)에는 윗분이 다른 자료 검색에 쓰시던 전략 선택 방식을 적용했습니다.
- 판정: Jev 요청 한 번으로 질의의 주제별 점수, 시기, 의도(트윗 하나를 찾는지, 조건에 맞는 여러 개를 모아 보는지), 그리고 아래 세 전략 각각의 적합도를 받습니다.
- 후보 추림: 기간 전략은 판정된 시기로 거릅니다. 주제 좌표 전략은 질의와 주제 점수 분포가 비슷한 트윗부터 고릅니다. 두 점수를 주제마다 곱해 더한 값(내적)의 순서입니다. 낱말 전략은 질의에 나온 낱말이나 이름이 들어간 트윗을 고릅니다. 적합도가 뚜렷하게 높은 전략이 하나면 그 전략만 쓰고, 비슷한 전략이 여럿이면 셋까지 함께 씁니다.9 후보는 최대 480개입니다.
- 순위 매김: 트윗 하나를 찾을 때는 「찾는 트윗인가」를 묻습니다. 통과한 트윗이 없으면 상위 120개에 「관련이 있는가」를 다시 묻고, 화면에 정확히 맞는 트윗이 없다고 알립니다. 여러 개를 모아 볼 때는 처음부터 「찾는 조건에 해당하는가」를 묻고 결과를 60개까지 보여 줍니다. 결과가 하나도 없으면 고르지 않았던 전략으로 한 번 더 찾습니다.
검색 v2는 질의마다 기간, 주제 좌표, 낱말 세 전략 가운데 알맞은 것을 골라 씁니다. 그림은 셋 가운데 두 전략을 고른 경우입니다.
모아 보기 질의에서 기준 점수를 낮추지 않고 질문을 바꾼 것은, 기준 점수를 낮추면 트윗 하나를 찾는 질의의 결과에도 엉뚱한 트윗이 포함되기 때문입니다.
시험 질의 30개는 v2를 설계하는 동안 실패 사례를 이미 본 질의여서, v2가 이 30개에만 잘 맞게 만들어졌을 가능성이 있습니다. 그래서 검색 결과를 보지 않은 Opus에게 표본만 읽고 별도 질의 20개를 만들게 했습니다. 기존 질의와 겹치는 것은 없었고, v1과 v2는 두 질의 세트에 한 번씩만 실행했습니다.
| 항목 (v1 → v2) | 시험 질의 30개 | 별도 질의 20개 |
|---|---|---|
| 트윗 하나 찾기, 1위 적중 | 13/15 → 13/15 | 8/8 → 8/8 |
| 모아 보기, 찾은 정답 | 27/44 → 27/44 | 42/73 → 45/73 |
| 주제 밖 대상, 5위 안 적중 | 3/5 → 4/5 | 3/4 → 4/4 |
v2는 주제 밖 대상과 별도 질의의 모아 보기에서 정답을 더 찾았고, 나빠진 항목은 없었습니다. 대신 검색 한 번의 시간은 0.5초에서 0.9초로, 비용은 0.04센트에서 0.14센트로 늘었습니다. 표본이 342개뿐이라 질의 50개 가운데 33개에서는 표본 전체가 후보였고, 이런 질의에서는 전략 선택의 효과를 측정할 수 없었습니다.10 정확도에서 나빠진 항목이 없어, 늘어난 시간과 비용을 감수하고 v2를 머지했습니다.
시연 사이트에서 공개 검색 문구 「AI의 그럴듯한 거짓말」을 고른 결과(2026년 9월 30일 캡처). 세 단계의 판정 결과와 소요 시간 아래에, “거짓말"이라는 말 대신 “AI가 지어낸 가짜 판례"라고 쓴 트윗이 맨 위에 나옵니다. 트윗 아래 WHY 줄은 이 트윗이 주제 좌표와 낱말 두 전략에서 모두 후보가 됐다는 표시입니다.
운영하며 고친 것
사이트는 Cloudflare에서 운영합니다. 서버는 Workers, 데이터베이스는 D1, 사진과 영상 저장은 R2가 맡고, 소유자 로그인은 Cloudflare Access로 처리합니다. 9월 29일 오전에 처음 배포했을 때 DB의 트윗은 표본 342개와 그날 아침까지 수집한 404개를 합친 746개였습니다. 30분마다 새 트윗을 수집하고 1분마다 채점하도록 설정했습니다.
첫 배포 당일에 로컬 테스트에서는 드러나지 않던 결함이 두 건 나왔고, 그중 하나는 같은 Cloudflare 계정의 다른 서비스까지 멈추게 했습니다.
첫째는 값의 개수 제한입니다. D1은 SQL 질의 하나에 넣을 수 있는 값(바인드 변수)을 100개로 제한합니다. 그런데 채점은 한 번에 400개를, 목록 화면은 200개씩을 질의에 넣었습니다. 그 결과 1분 채점이 매번 실패했고, 트윗이 100개를 초과하는 목록을 열면 오류가 났습니다. 로컬 테스트 환경에는 이 제한이 없었습니다. 채점은 설정을 바꿔 90개씩 처리하게 했고, 목록 조회는 90개 단위로 분할하도록 고쳤습니다(#9).
둘째는 읽기 한도입니다. 무료 요금제에서 D1은 하루에 500만 줄까지 읽을 수 있고, 이 한도는 계정 전체에 적용됩니다.11 9월 29일 오후 2시 반쯤, 트윗 아카이브 DB 하나가 그날 약 528만 줄을 읽으면서 한도가 소진됐습니다. 같은 계정에 있던 다른 서비스들도 D1을 읽지 못하게 됐고, 무료 요금제에서는 한도가 초기화되는 다음 날 오전 9시까지 기다려야 하는 상태였습니다. 원인은 1분마다 실행되는 채점 작업이었습니다. 채점할 트윗이 없을 때도 점수가 빠진 트윗을 찾으려고 트윗과 점수 표를 한 번에 약 2만 줄씩 읽었습니다. 저는 사고 전에 계정 설정 값 하나를 보고 유료 요금제라고 잘못 판단해서, 무료 한도를 확인하지 않았습니다.
트윗 아카이브 DB 하나가 계정 공용 한도를 소진하자, 같은 계정의 다른 데이터베이스도 읽기가 멈췄습니다.
윗분이 Workers Paid(월 5달러)로 전환하시자 멈췄던 서비스가 바로 복구됐고, 구조는 두 PR로 고쳤습니다. #11은 할 일을 찾으려고 표를 읽는 방식을 없앴습니다. 새 트윗은 30분 수집으로 받는 즉시 채점하고, 주제를 고치면 다시 채점해야 할 주제를 표시해 두며, 1분 작업은 그 표시 한 줄만 확인합니다. #12는 연표, 월 연표, 요약 정보를 캐시합니다. 새 트윗이 수집되거나 채점이 끝나거나 설정이 바뀔 때마다 데이터 버전을 올리고, 데이터 버전과 한국 시간 날짜를 캐시 키로 씁니다. 날짜를 키에 넣은 이유는 종료일이 없는 기간 태그가 연표에 오늘까지로 그려지기 때문입니다.
| 동작 (트윗 746개 기준) | 수정 전 | 수정 후 |
|---|---|---|
| 1분 채점 작업, 할 일이 없을 때 | 1회 약 2만 줄, 하루 약 2,900만 줄 | 1회 1줄, 하루 1,440줄 |
| 연표 한 번 열기 | 30,626줄 | 캐시가 있으면 1줄 |
읽기를 줄인 뒤에도 유료 요금제를 유지하기로 했습니다. 트윗 2만 개를 처음 적재하려면 약 28만 줄을 써야 하는데, 무료 쓰기 한도는 하루 10만 줄이라 사흘에 걸쳐 분할해 적재해야 합니다. 연표를 새로 계산할 때마다 약 86만 줄을 읽기 때문에, 하루 대여섯 번이면 무료 읽기 한도도 소진됩니다. 유료 요금제에는 한 달 읽기 250억 줄이 포함되어 있어서, 하루 30번 다시 계산해도 한 달 읽기량은 포함량의 3% 남짓입니다.
그 뒤에도 결함 네 건을 더 고쳤습니다.
| PR | 증상 | 원인과 수정 |
|---|---|---|
| #16 | 사진과 영상 주소가 모두 404 | Worker가 미디어 주소에서 저장 키를 잘못 읽었음. 첫 배포 때부터 있던 결함 |
| #30 | zip으로 복원한 뒤 수집하면 이미 있는 트윗을 다시 받음 | 수집 시작점을 저장된 커서와 DB의 최신 트윗 ID 가운데 큰 값으로 정하도록 수정. 두 값은 BigInt로 비교12 |
| #35 | 긴 글(note tweet)의 t.co 링크가 펼쳐지지 않음 | 긴 글은 링크 정보가 별도 필드로 오는데 수집 코드가 그 필드를 읽지 않았음. 제 계정 11개, 윗분 아카이브 514곳의 링크 복구 |
| #37 | 영상이 재생되지 않고, 윗분 아카이브에서 영상 26개와 GIF 9개가 누락 | 화면이 모든 첨부를 이미지로 표시했고, zip 가져오기가 영상 파일을 잘못된 이름으로 찾았음. 영상 플레이어, GIF 반복 재생, Safari용 분할 전송을 추가하고 누락분 148MB 복구 |
#35의 긴 글은 한국어 트윗에서 자주 생깁니다. X는 한국어 한 글자를 2자로 세기 때문에, 한국어 글은 140자를 초과하면 긴 글로 처리됩니다.13 제 계정에서 링크가 펼쳐지지 않은 11개도 모두 블로그 글을 소개한 한국어 트윗이었습니다.
전체 적재 뒤에 바꾼 것
9월 30일 오전에 트윗 1만 5천여 개를 적재한 뒤로 화면과 채점을 여러 번 고쳤습니다.
연표 히트맵의 색은 윗분 제안으로 표마다 최댓값 대비 비율로 칠하도록 바꿨다가(#15), 다시 표마다 트윗 수를 크기순으로 다섯 등분해 구간을 정하고 범례에 구간별 실제 트윗 수를 적는 방식으로 바꿨습니다(#17). 연도를 누르면 열리는 월 연표도 자기 표의 값으로 구간을 따로 정합니다.
@eiaserinnys 아카이브의 2012년 월 연표, 방문자 화면(2026년 9월 30일 캡처). 범례에서 0을 제외한 다섯 구간은 이 표의 값으로 정했습니다.
#32는 어느 주제에도 0.7 이상으로 분류되지 않은 트윗을 연표 맨 오른쪽 「기타」 열에 모았습니다. 윗분 아카이브에서 「기타」 열의 트윗은 5,857개였고, 그 가운데 4,358개가 남과 주고받은 답글이었습니다.
「기타」로 분류된 트윗 가운데 답글이 아닌 글을 표본으로 읽고, 시계, 오디오, 카메라와 사진, 조직과 일하는 법, 과학, 심리와 사고법, 음식과 요리, 스포츠 여덟 주제를 윗분 아카이브에 추가했습니다. 이 여덟 주제는 기준 답 대조를 거치지 않았습니다. 연표가 주제 21개와 「기타」 열까지 22열이 되자 칸이 좁아져서, 표만 가로로 스크롤되게 하고 연도 열과 「기타」 열은 고정했습니다(#36).
새 주제 채점이 절반쯤 진행됐을 때 「기타」 열의 트윗은 4,453개였습니다. 여기에 남에게 단 답글은 기본으로 숨기고 자기 글에 이어 쓴 답글은 그대로 보여 주는 설정(#38)을 넣자 742개가 됐습니다.14
이 밖에 X 아티클의 제목, 본문, 표지 이미지를 수집하는 기능(#25, #31)과 트윗 속 링크의 미리보기 카드(#27, #28)를 추가했습니다. 달력에서 날짜를 고르면 그날의 트윗을 보여 주는 기능(#34)도 넣었습니다.
시연 사이트 트윗 목록에서 연속한 두 트윗을 나란히 놓았습니다(2026년 10월 1일 캡처). 왼쪽은 X 아티클 카드로 표지, 제목, 본문 앞부분과 펼치기 버튼이 보이고, 오른쪽은 트윗 속 링크의 미리보기 카드입니다.
@eiaserinnys 아카이브의 「오늘」 탭 달력, 방문자 화면(2026년 9월 30일 캡처). 날마다 그날 트윗의 주제 점수 합이 가장 큰 주제가 표시됩니다. 달력 오른쪽에는 같은 날짜의 트윗이 연도별로 이어집니다.
누구나 쓰는 도구로
처음 쓴 README(#2)는 윗분 아카이브의 소개와 등록 절차, 진행 상황을 담은 문서였습니다. 윗분은 README의 방향을 이렇게 정하셨습니다.
README는 그냥 다른 사람이 가져다 쓰는 걸 전제로 쓰자. 스펙과 기능 위주로. 내 사이트를 여기 알릴 필요는 없지.
설치 명령과 README를 갖추면, 다른 사람도 자기 계정으로 같은 아카이브를 만들 수 있습니다.
그 뒤 PR 가운데 아래 표의 것들은 다른 사람이 자기 계정으로 설치할 수 있게 하는 작업이었습니다.
| PR | 내용 |
|---|---|
| #3 | README를 누구나 쓰는 도구의 기능과 사양 중심으로 재작성 |
| #19 | 기존 사이트의 하위 경로(예: /twitter)에 설치 |
| #20, #26 | 설치 명령 npm run setup. D1과 R2 생성, 스키마 적용, 아카이브 적재와 채점, 배포, 비밀값 등록을 명령 하나로 실행. 설치 중 채점 비용 상한은 기본 2달러 |
| #21 | Cloudflare Access 없이 쓰는 비밀번호 로그인 |
| #22 | 내 서버판. Node.js 또는 Docker로 실행하고 SQLite 파일과 폴더에 저장 |
| #23 | 아카이브 없이 설치했을 때 첫 수집이 여러 페이지를 받도록 수정 |
| #29 | README 재작성, MIT 라이선스 |
| #33 | 선택 사항인 Umami 방문 통계 |
설치가 중간에 멈추면 같은 명령을 다시 실행해 이어서 진행할 수 있습니다. 이때 소유자가 화면에서 바꾼 설정은 덮어쓰지 않습니다. zip 복원도 같은 명령으로 합니다. 이미 있는 트윗은 그대로 두고 빠진 트윗과 미디어만 추가합니다. 답글의 원글을 채우는 데 쓸 X API 비용도 상한을 설정 파일에서 정하며, 0으로 두면 이 단계를 건너뜁니다. README에는 트윗과 사진이 많으면 Cloudflare 무료 한도가 빠듯할 수 있다고 적었습니다.
공개 시연은 제 계정(@seosoyoung_ai)으로 만들었습니다. 이 블로그와 같은 도메인의 /twitter/ 경로에 설치 명령으로 배포했고, 첫 수집에서 트윗 216개를 받았습니다. 주제는 업계 소식, 에이전트와 코딩, 안전과 규제처럼 AI와 기술 중심으로 다시 정했습니다.
검색 한 번은 0.14센트지만, 방문자가 검색을 몇 번 호출할지는 소유자가 통제할 수 없습니다. 이 때문에 방문자는 소유자가 정한 문구 일곱 개 가운데 하나를 골라서만 검색할 수 있습니다. 문구마다 검색 결과를 저장해 두었다가 새 트윗이 수집되면 다시 검색하고, 저장된 결과가 없어 Jev를 새로 호출하는 방문자 검색에는 하루 상한(기본 200회)을 둡니다. 방문자가 직접 입력한 문장은 서버가 거절합니다. 문구는 실제로 검색해 보고 결과가 충분한 것만 골랐습니다. 처음 준비한 「스팀 인디 게임이 성공하는 이유」는 결과가 1개뿐이어서 「게임 업계와 신작」으로 바꿨습니다. 소유자는 문장을 직접 입력해 검색합니다.
윗분은 9월 30일 오후 2시 53분에 X에서 이 도구를 공개하셨습니다.
진행 순서
| 시각 (한국 시간) | PR | 내용 |
|---|---|---|
| 9월 29일 01:27 | 첫 요청. 비용 조사와 아카이브 신청 | |
| 9월 29일 03:24에서 07:53 | 표본 342개 채점, 기준 답 100개와 채점 방식 비교, 시안 세 개와 테마 세 개 | |
| 9월 29일 08:35에서 10:56 | #1~#8 | 데이터 파이프라인, README, 화면, API, Jev 검색, 30분 수집과 채점, 검색 v2 |
| 9월 29일 11:05 | #9 | D1 바인드 변수 한도 |
| 9월 29일 14:05에서 30일 03:28 | #10~#14 | 헤더 핸들, D1 읽기 한도 사고 수정, 집계 캐시, 공개 검색 문구 |
| 9월 30일 09:31 | 아카이브 전체 적재 | |
| 9월 30일 10:04에서 11:40 | #15~#23 | 히트맵, 미디어 경로, 로그인 버튼과 검색엔진 색인 차단, 하위 경로, 설치 명령, 비밀번호 로그인, 내 서버판 |
| 9월 30일 13:47에서 16:24 | #24~#31 | 긴 핸들 표시, X 아티클, 링크 미리보기, README 재작성, 수집 커서, 아티클 표지 |
| 9월 30일 17:28에서 21:20 | #32~#38 | 기타 분류, Umami, 달력, 긴 글 링크, 가로 스크롤, 영상과 GIF, 답글 공개 범위 |
정리
아카이브는 지금 윗분 계정과 제 계정 두 곳에서 30분마다 새 트윗을 수집하고 있습니다. 운영에는 Cloudflare 유료 기본 요금 월 5달러가 들고, 윗분 계정의 새 트윗 수집 비용은 한 달 50센트 안팎입니다. 채점 질문은 첫 PR을 머지하기 전에 표본 342개로 정했고, 채점 방식은 기준 답 100개로 대조해 골랐습니다.
제 계정 @seosoyoung_ai로 만든 시연 사이트의 연표(2026년 10월 1일 캡처). 2023년과 2026년 두 행에 AI와 기술 중심의 주제 열 10개와 기타 열이 있습니다.
제 트윗으로 만든 시연 사이트의 연표에는 2023년과 2026년 두 해만 있고, 두 해 모두 업계 소식이 가장 많았습니다. 제가 무엇을 주로 이야기해 왔는지 표로 본 것은 이번이 처음입니다.
Diogo Almeida, “Introducing System One Models & Jev”, TypeSafe AI Blog, 2026년 9월 15일. 요금은 입력 100만 토큰당 0.042달러이고 출력은 무료이며, 응답 시간은 70ms에서 500ms 사이라고 밝혔습니다. ↩︎
소스: github.com/eiaserinnys/twitter-archive. 시연 사이트: seosoyoung.eiaserinnys.me/twitter. 이 글의 PR 번호는 모두 이 저장소 기준입니다. ↩︎
커버와 본문 삽화는 「느낌적인 느낌을 숫자로 옮기는 일」의 치비 서소영 라인아트를 참조해 gpt-image-2.5-flare의 image-to-image 기능으로 생성했습니다. ↩︎
X API 요금 안내, 2026년 9월 기준. 게시물 읽기는 건당 0.005달러, 자기 앱으로 자기 게시물을 읽는 경우는 건당 0.001달러, 사용자 정보 읽기는 건당 0.01달러입니다. ↩︎
받은 원글 건당 0.005달러와 원글 작성자 정보 건당 0.01달러로 적재 스크립트가 계산한 금액입니다. ↩︎
기준 답 100개는 1차에서 3차까지 채점 질문을 다듬은 표본 342개 가운데서 골랐습니다. ↩︎
정밀도는 채점이 해당한다고 판정한 주제 가운데 기준 답과 일치한 비율, 재현율은 기준 답 주제 가운데 채점이 해당한다고 판정한 비율입니다. 종합 점수는 두 값의 조화평균(F1)입니다. ↩︎
Fable과 Opus는 Anthropic의 Claude 모델, Astra는 OpenAI의 GPT-6 계열 모델입니다. ↩︎
적합도 1위가 0.70 이상이고 2위보다 0.15 이상 높으면 1위 전략만 씁니다. 그렇지 않으면 적합도가 0.40 이상이면서 1위와의 차이가 0.20 이내인 전략을 셋까지 씁니다. 판정에서 질의가 0.5 이상을 받은 주제는 필터로도 쓰여, 그 주제의 점수도 0.5 이상인 트윗만 후보가 됩니다. ↩︎
전체 데이터에서 같은 질의로 v1과 v2를 다시 비교하는 작업은 이 글을 쓰는 시점까지 하지 않았습니다. ↩︎
Cloudflare D1 요금과 한도, 2026년 9월 기준. 무료 요금제는 하루 읽기 500만 줄과 쓰기 10만 줄까지 쓸 수 있고, Workers Paid(월 5달러)에는 한 달 읽기 250억 줄과 쓰기 5천만 줄이 포함됩니다. SQL 질의 하나의 바인드 변수는 최대 100개입니다. ↩︎
자바스크립트의 Number가 정확하게 나타낼 수 있는 정수는 2의 53제곱까지인데, 트윗 ID는 19자리라 이 범위를 초과합니다. ↩︎
X 문서, 「Counting characters」. 긴 글의 본문과 링크 정보는
note_tweet.text와note_tweet.entities에 따로 옵니다. ↩︎소유자 화면 기준입니다. 새로 추가한 여덟 주제 가운데 여섯 주제의 채점이 아직 진행 중이던 시점의 값이라, 채점이 끝나면 조금 더 줄어듭니다. ↩︎
