Free

Claude로 글자 수 제한 안 넘는 다국어 트윗 스레드 쓰기

다국어 스레드는 번역 문제가 아니다. 'translate' 대신 'write'를 쓰고, 로컬 .length로 사전 게이트를 친다.


오늘 기사 5개를 공개했다. 각 기사마다 6개 언어(en / zh / ja / ko / ar / id), 언어당 4트윗씩 스레드가 붙는다. 총 120트윗.

그중 4개 기사에서 중국어 스레드를 잘라내야 했다. 스크립트가 찍은 원시 수치(형식 chars/limit):

zh[1]=160/140 OVER
zh[2]=164/140 OVER
zh[3]=152/140 OVER
...
id[3]=286/280 OVER
zh[2]=163/140 OVER
zh[3]=148/140 OVER
zh[2]=152/140 OVER

매번 같은 패턴이다. 영어 4트윗을 먼저 쓴다(각 230~260자, 280 한도에 바짝 붙게) → Claude에게 "이 4개 기반으로 중국어 버전 만들어 줘"라고 한다 → 중국어는 거의 매번 140을 넘긴다.

이건 버그가 아니다. Claude에게 잘못된 단어를 쓴 거다. "translate"라고 말하는 순간 Claude는 문장을 한 줄씩 옮기기 시작한다. 하지만 다국어 스레드는 번역 문제가 아니다. 각 언어는 그 언어의 글자 예산에 맞게 처음부터 다시 써야 한다. 이 글은 Claude의 첫 드래프트를 한도 근처로 뽑아내서 3~5라운드 손절을 피하는 방법이다.


근본 원인: X는 CJK에 2배 가중 × Claude 기본값은 직역

X의 글자 수 계산 규칙

X의 길이 제한은 "글자 수"가 아니라 weighted characters로 계산된다:
- 라틴 문자, 숫자, 구두점: 글자당 1 weight
- CJK 문자(중/일/한), 일부 이모지: 글자당 2 weight
- 트윗 총 상한 280 weight

실제 효과:
- 영어 트윗 상한 280자
- 순수 중국어 트윗 상한 140자(140 × 2 = 280)
- 혼합: 글자별 1 또는 2로 계산

그래서 "영어 260자를 중국어로 번역"은 중국어 260자가 아니라 — 중국어 버전은 실제로 약 130자 공간뿐이다. 영문에 남은 구두점·따옴표·backtick은 그대로 자리를 차지하고, 중국어 글자는 전부 두 배로 센다. 직역하면 초과는 거의 확실하다.

"translate"라는 단어가 Claude의 직역 모드를 켠다

Claude가 "translate these 4 EN tweets to ZH"를 보면, 첫 반응은 영어 문장 구조를 유지한 채 단어만 중국어로 바꾸는 것이다:

  • EN: "Production 400/500 isn't like local. User's already staring at a blank screen after hitting Pay."
  • Claude 직역: "生产环境的 400/500 和本地并不相同。用户已经点击了『付款』并看着一个空白屏幕。"(40자)

의미는 맞다. 하지만:
- "并不相同"을 유지("不同"보다 한 글자 길다)
- "已经点击了"를 유지("刚点完"이 더 짧다)
- "并看着一个空白屏幕"를 유지("看着空白屏"이 더 짧다)

유지된 잉여 표현 하나하나가 글자를 먹는다. 트윗 4개에 누적하면 20~40자 초과는 쉽게 발생하고, 한도를 넘긴다.

효과적인 프롬프트: "번역" 대신 "다시 쓰기"

두 표현 비교:

안 되는 프롬프트:

"Translate these 4 English tweets to Chinese, Japanese, Korean, Arabic, Indonesian."

되는 프롬프트:

"For each of these 4 points, write a thread in ZH / JA / KO / AR / ID. Each language gets a fresh pass — same argument, adjusted for that language's tweet budget.

  • ZH: ≤ 140 chars per tweet, hook-first, cut detail if needed
  • other languages: ≤ 280 chars
  • use each language's natural phrasing, don't preserve EN sentence structure
  • each tweet must be self-contained but add 🧵 only on tweet 1"

차이점:

  1. "write"가 "translate"를 대체 — Claude의 창작 모드를 켜고, 번역 모드가 아니다.
  2. "fresh pass" / "same argument" — 문장 재구성을 명시적으로 허용한다.
  3. "don't preserve EN sentence structure" — 기본 동작을 정면으로 막는다.
  4. "cut detail if needed" — 길이에 맞추려면 내용을 버려도 된다고 알린다(안 주면 Claude는 모든 사실을 보존하고 초과한다).
  5. 언어별 명시적 한도 — "≤ 140 chars"는 "short"보다 100배 효과적이다.

로컬 sanity check: Ruby + .length

눈으로 세지 말 것. 매번 돌리는 작은 스크립트를 쓴다:

tweets.each do |loc, list|
  list.each_with_index do |t, i|
    len = t.length
    limit = loc == "zh" ? 140 : 280
    status = len > limit ? "OVER" : "ok"
    puts "#{loc}[#{i}]=#{len}/#{limit} #{status}"
  end
end

Ruby의 String#length는 Unicode 코드포인트 수를 반환한다 — CJK는 각 1로 센다. X는 CJK를 weighted 2로 세지만, 중국어 트윗의 한도는 140 코드포인트(140 × 2 = 280 weighted)이므로 .length는 X의 한도와 직접 맞물린다. 오차는 다음에서 나온다:
- 이모지 코드포인트 수(🧵는 1 CP지만 여러 CP로 구성된 이모지도 있다)
- 제로폭 문자(ZWJ 등)

대부분의 CJK 트윗에서 Ruby .length는 X의 카운트와 ±1 범위로 맞다. 이걸 로컬 게이트로 쓰면 X에 시험 게시하는 것보다 100배 안정적이다.

이모지와 backtick 함정

중국어 트윗을 줄일 때 가장 먼저 손대야 할 두 범주:

backtick(`)

`kamal exec` 같은 코드 인용은 중국어 맥락에서 backtick 하나당 1글자를 먹는다 — 한 쌍이면 2글자, 의미에는 기여하지 않는다. 대처:
- 콜론으로 대체 가능하면 콜론: "명령 kamal exec"이 "`kamal exec`"보다 짧다
- 꼭 backtick을 남겨야 하면 핵심 식별자만(전체 shell 라인은 안 된다)

이모지

🧵는 스레드 첫 트윗에만 붙인다. 다른 이모지는 중국어 트윗에서 신중하게 — 이모지 하나는 사실상 2글자(1 코드포인트 + 때때로 ZWJ 수식자).

전각 vs 반각 구두점

  • 중국어 구두점(「」、,。——): 글자당 weighted 2
  • 영어 구두점("",-): 글자당 weighted 1
  • 중국어 트윗에서 따옴표를 섞을 때: 반각 따옴표('X')를 쓰면 전각(「X」)보다 쌍당 2글자 절약

Claude의 4가지 기본 이탈 방향

다국어 스레드를 생성할 때 막아야 할 4가지:

1. 영문 구조를 유지한 직역

증상: 중국어 문장에 "并不相同", "已经点击了", "一个空白屏幕"처럼 영어에서 직역된 게 뻔한 표현이 등장한다.

차단: 프롬프트에 "don't preserve EN sentence structure" / "use natural ZH phrasing"를 명시한다.

2. 자발적 길이 체크 없음

증상: Claude가 생성 후 "done"이라 말하지만 실제로는 한도 초과인데 본인은 모른다.

차단: 프롬프트에서 생성 후 스스로 세도록 지시("count each tweet's char length and flag any over the limit")하거나 로컬 스크립트로 게이트한다. 나는 후자 — Ruby가 더 정확하다.

3. 따옴표와 이모지의 weight 누락

증상: Claude가 셀 때 이모지와 전각 따옴표의 추가 weight를 무시한다.

차단: Claude에게 "한도 넘었어?"라고 묻지 말 것 — 로컬에서 .length를 계산하고 그 수치를 기준으로 Claude에게 줄이라고 시킨다.

4. 🧵 위치 일관성 없음(전부 / 첫 트윗만 / 마지막에)

증상: 어떤 때는 모든 트윗에 🧵, 어떤 때는 첫 트윗에만, 어떤 때는 4/4 끝에 붙는다.

차단: 프롬프트에 "🧵 only on tweet 1"로 고정. Claude는 스레드의 시각적 관례를 기본적으로 모른다.

체크리스트

Claude의 첫 드래프트를 한도 근처 다국어 스레드로 뽑는 6가지 규칙:

  1. "translate" 대신 "write". Translate는 직역을 트리거 = 영문 구조 유지 = 초과.
  2. 언어별 명시적 한도. "≤ 140 chars"는 "short"보다 100배 효과적.
  3. 내용 삭제 권한: "cut detail if needed"로 길이 때문에 사실을 버려도 된다고 알린다.
  4. 로컬 Ruby .length 게이트. Claude 자체 카운트보다 정확하고, X 시험 게시보다 빠르다.
  5. 중국어에서 backtick과 이모지를 먼저 자른다. backtick 1개 = 1글자, 이모지 1개 = 2글자.
  6. 🧵 위치를 프롬프트에 고정. Claude는 스레드 시각적 관례를 기본적으로 모른다.

진짜 통찰: Claude는 다국어 스레드를 맞게 쓸 수 있다. 그런데 기본 반응은 "번역"이다. 프롬프트로 "언어별 독립 재작성" 모드로 돌리는 건 당신 몫이다. .length 게이트를 더했더니, 오늘 마지막 기사의 중국어 스레드는 첫 드래프트에 140/140 딱 — 잘라낼 필요 없었다.