Sprag

Korean writing guidance (한국어)

Claude가 한국어로 쓸 때 나타나는 문체 결함을 교정하는 규칙을 세션 시작 시 한 번 주입합니다.

🇰🇷 한국어 문체 지침

Claude가 한국어로 쓸 때 나타나는 문체 결함을 교정하는 규칙을 세션 시작 시 한 번 주입합니다.

sprag korean on       # 켜기 (모든 프로젝트에 적용)
sprag korean status   # 상태·비용·출처 확인
sprag korean show     # 규칙 원문 출력
sprag korean off      # 끄기

Claude Code의 output style로도 같은 일을 할 수 있지만, output style은 전역 슬롯 하나라서 켜는 순간 다른 스타일을 못 쓰게 되고 머신마다 따로 설정해야 합니다. 이 기능은 지침을 패키지에 담고 이미 설치된 SessionStart 훅으로 전달하므로, CLI가 설치된 모든 프로젝트에 적용되며 output style 슬롯은 비워 둡니다. /clear 이후에도 훅이 다시 실행되어 유지됩니다.

비용은 세션당 약 1,800 토큰이며 매 턴이 아니라 세션 시작에 한 번만 주입됩니다. 두 번째 요청부터는 프롬프트 캐시에 올라가므로 추가 부담이 거의 없습니다. 켜져 있으면 statusline에 ✍️ 칩이 표시됩니다.

적용 전후 비교

슬랙 봇이 claude -p로 저장소를 조사하고 결과를 스레드에 답하는 실제 파이프라인(yaml-sns-agent)에서, 지침만 껐다 켜고 같은 질문을 던진 결과입니다. 질문은 "렌더가 실패했을 때 원인을 어떻게 좁히는지 한국어 3문장으로 설명해라"였습니다.

이전 (지침 꺼짐)

렌더 실패 원인 좁히기 3단계: 먼저 hn:still --check로 스틸이 명세보다 최신인지(FRESH/STALE) 확인합니다. STALE이면 재생성 안 해서 옛 자산으로 렌더 시도한 게 원인. 다음 caption-blocks가 최신 나레이션과 단어열 일치하는지 직접 대조합니다. TTS 재생성 후 blocks 재생성 빠지면 자막·음성 어긋나 렌더 깨짐.

이후 (지침 켜짐)

먼저 hn:still --check 돌려 스틸이 명세보다 최신인지 확인한다. STALE이면 재생성 안 해서 생긴 문제.

다음 caption-blocks가 captions.json 단어열과 일치하는지 본다. 내레이션 재TTS 후 blocks 재생성 빠지면 옛 자막이 새 음성 위에 뜬다.

바뀐 지점은 세 가지입니다. 첫째, 엠대시로 이어 붙이던 절이 마침표로 끊어져 한 문장이 한 가지 사실만 전달합니다. 둘째, "확인", "대조", "렌더 깨짐" 같은 명사형 종결이 "확인한다", "본다", "뜬다"처럼 서술어로 바뀌어 무엇을 하라는 것인지가 분명해집니다. 셋째, "자막·음성 어긋나"처럼 조사가 빠졌던 지점에 조사가 돌아와 어떤 성분이 주어이고 목적어인지 읽는 즉시 잡힙니다.

기술적 내용은 양쪽이 동일합니다. 지침은 판단이나 정확도가 아니라 문장의 완성도에만 관여하므로, 답이 달라지는 것이 아니라 같은 답을 다시 읽지 않아도 되는 형태로 만들어 줍니다. 슬랙처럼 사람이 스크롤하며 읽는 채널에서는 이 차이가 되묻는 횟수를 줄이고, 되묻지 않는 만큼 토큰도 아낍니다.

설계 개념

한국어 문체 관리는 세 층으로 이루어집니다.

  1. 근거가 있는 규칙 (presets/korean-style/rules.md): 세션 시작 때 한 번 주입합니다. 조항마다 실측 수치나 인용한 연구가 있고, 근거가 없는 취향은 싣지 않습니다. 사람이 LLM보다 더 자주 쓰는 표현은 고치지 말라는 과교정 금지 조항도 같은 기준으로 넣었습니다.
  2. 쓰기 시점 검사 (src/korean-lint.cjs): 규칙 가운데 문자열 비교나 정규식으로 판정할 수 있는 조항만 골라 파일을 쓰는 순간 검사합니다. 지침은 한 번 읽히고 끝나지만 검사는 파일을 쓸 때마다 돌기 때문에, 판정할 수 있는 조항은 지침보다 검사가 맡습니다.
  3. 실측 루프: 새 검사 패턴은 실제 코퍼스에 돌려 표본의 정밀도를 확인한 뒤에 넣습니다. 규칙을 바꾼 뒤에는 바꾸기 전후의 산출물을 같은 지표로 다시 잽니다. v3.91.0의 규칙 교체도 이 루프에서 나왔습니다.

규칙이 겨냥하는 대상은 문법 오류보다 독자가 기계의 말투로 알아채는 습관입니다. 루브릭 평가 연구에서 LLM이 쓴 글은 맞춤법 점수가 모두 만점이었습니다. 독자가 거슬려 하는 지점은 지나치게 고른 문장과 정형화된 꼴이므로, 규칙은 문장을 더 다듬기보다 구체적인 사실을 쓰고 정형화된 꼴을 깨라고 요구합니다.

sprag 자체 규칙 (v3.91.0)

v3.91.0부터 주입하는 규칙은 sprag가 직접 쓴 presets/korean-style/rules.md 한 파일입니다. v3.90까지 쓰던 지침을 바꾼 이유는 실측 결과입니다. 기계가 쓴 한국어로 읽히는 습관 가운데 가장 강한 두 가지를 옛 지침이 건드리지 못했습니다.

새 규칙은 9개 절로 되어 있습니다. 쉼표와 가운뎃점, 문장 완결, 정형화된 구성('A가 아니라 B' 대조, 문단 끝 펀치라인, 자문자답), 구체성, 번역투, 문장 연결, 과교정 금지, 낭독 검사입니다. 조항마다 근거가 파일 머리 주석에 있고, 전체 목록은 아래 참고 문헌에 있습니다.

응집성 절의 원칙 대부분은 한국어에만 해당하지 않습니다. 구정보 우선 배열, 대명사의 단일 지시, 문단 안 주어 유지, 비약을 잇는 다리 문장은 영어 산문에도 그대로 적용됩니다. 영어 산출물에는 sprag cohesion on이 같은 원칙을 영어 블록으로 주입합니다(korean 규칙이 켜져 있으면 중복이라 생략됩니다).

마지막에서 두 번째 절은 고치면 안 되는 것을 명시합니다. '~를 통해'와 '~것이다'는 사람이 LLM보다 두 배쯤 더 쓰는 표현이라 일괄로 바꾸지 않습니다. 정착된 전문 용어와 문어체, 원문 인용도 고치지 않습니다. 검사가 내는 지적은 판정이 아니라 확인 요청입니다.

쓰기 시점 검사 (v3.24.0)

지침을 세션 시작에 한 번 넣는 것만으로는 부족했습니다. 모델은 지침을 한 번 읽고 그 뒤로 파일 수십 개를 쓰는데, 그동안 결과물을 다시 읽어 보는 단계가 없었습니다. 그래서 지침이 켜진 세션이 규약에 어긋나는 문장을 문서에 그대로 실어 보냈고, 사람이 완성본을 읽을 때에야 드러났습니다. 2026년 8월에 적용 범위 문장을 고쳐서 같은 문제를 잡으려 했지만, 문장을 고쳐도 검사 단계가 없다는 조건은 그대로였기 때문에 재발했습니다.

v3.24.0부터 korean on이 PostToolUse 훅을 함께 설치합니다. 모델이 방금 쓴 파일을 열어서 기계로 판정할 수 있는 조항을 검사하고, 위반이 있으면 모델에게 되돌려 보냅니다. 파일은 이미 저장된 뒤이므로 잃는 것은 없고, 모델이 즉시 고칩니다.

sprag korean lint block   # 기본값. 위반을 되돌려 보내 고치게 합니다
sprag korean lint warn    # 알리기만 하고 진행을 막지 않습니다
sprag korean lint off     # 검사하지 않습니다

sprag korean lint scope all     # 기본값. 세션이 쓴 모든 텍스트 파일
sprag korean lint scope prose   # 마크다운·텍스트 문서만

sprag korean lint docs/*.md     # 이미 저장된 파일을 직접 검사

검사 항목은 사람이 판정할 필요가 없는 것들입니다. 비유 어휘 15종(~는 자리, ~의 흐름, 닿는다, 걷어내다, 발목을 잡다 등), 번역체 표지(~에 대한, ~를 위한, ~되어지), 구분자(—, ㅡ, |), 한 구에 세 번 이상 이어지는 조사 의, 명사형 종결 뒤의 마침표, 그리고 v3.42.7부터는 이어지는 두 문장이 똑같은 서술어로 끝나는 종결 반복입니다. v3.91.0에서 네 가지가 더해졌습니다. 한 문단에 두 번 이상 나오는 연결어미 뒤 쉼표, 접속부사 뒤 쉼표, 한글 낱말 사이의 가운뎃점(표의 칸과 '한·중' 같은 굳은 표기는 제외), 한 문단에 두 번 이상 나오는 '아니라' 대조입니다. 판단이 필요한 조항(성분 생략, 한자어 선택, 비슷하지만 똑같지는 않은 반복)은 그대로 지침이 담당합니다.

검사 대상에는 Bash로 쓴 파일도 들어갑니다(v3.42.7). heredoc과 tee, sed -i는 Write와 똑같이 한국어를 디스크에 남기고, Write 도구가 없는 하위 에이전트는 cat > 파일부터 집습니다. 그래서 Bash를 빼 두면 위임해서 만든 결과물 전체가 검사를 비켜 갔습니다. 리다이렉션 대상 경로만 인식하고 명령문 자체는 읽지 않으므로, echo에 담긴 한국어가 잘못 걸리는 일은 없습니다.

검사기를 시험하는 파일이나 문체 보고서처럼 금지된 표현을 일부러 담아야 하는 파일에는 korean-lint: off 한 줄을 넣으면 그 파일을 건너뜁니다.

기본 범위 all은 문서뿐 아니라 코드 주석과 화면에 나가는 문자열, 자막·템플릿, 생성 결과물까지 검사합니다. 주석도 사람이 읽고 PDF나 HTML 같은 산출물은 그 문자열들로 조립되기 때문입니다. 주석을 예외로 두면 정확히 문제가 됐던 경로가 다시 열립니다. 검사에서 빠지는 것은 설치된 의존성(node_modules), VCS 내부, 락 파일, 그리고 이진·이미지 파일뿐입니다. dist나 build 같은 산출물 디렉터리는 검사합니다. 문서만 보고 싶으면 korean lint scope prose로 좁힙니다.

주입문의 적용 범위 안내도 이 설정에서 생성합니다. 모델에게 알리는 범위와 검사하는 범위가 갈라지면 8월과 같은 상태로 돌아가기 때문입니다.

답변 언어 검사

쓰기 시점 검사는 모델이 파일에 쓴 글만 봅니다. 파일을 쓰지 않는 실수도 있습니다. 커밋 메시지나 릴리스 노트처럼 영어 작업을 길게 한 직후에, 모델이 한국어 질문에 영어로 답하는 경우입니다. 글자 수만 세면 판정되므로 korean on 은 Stop 훅 sprag korean --reply-hook 도 함께 설치합니다. 이 훅은 턴이 끝날 때 대화 기록의 끝부분을 읽습니다.

마지막 실제 질문이 한국어이고(한글 음절 4자 이상, 글자 중 한글 비율 30% 이상) 최종 답변이 영어이면(라틴 문자 80자 이상, 한글 비율 15% 미만) 멈춤을 막고 같은 내용을 한국어로 다시 답하게 합니다. 코드 블록, 인라인 코드, URL, 경로, 시스템 리마인더는 세지 않습니다. 질문에 영어로, 영문으로, in English 가 들어 있으면 검사하지 않습니다. 14일치 실제 대화 기록의 한국어 질문 2,204턴에서 8건이 걸렸고 전부 실제 오류였습니다. 훅이 이끈 재답변 중에는 Claude Code 가 stop_hook_active 를 켜 주므로 같은 턴에 두 번 막지는 않습니다.

sprag korean reply off   # 훅은 남기되 아무 일도 하지 않게 합니다
sprag korean reply on    # 기본값

sprag korean off 는 이 훅까지 제거하고, sprag korean 은 현재 상태를 보여 줍니다.

함께 주입되는 인코딩 규칙 (v3.23.2)

문체 지침과 별도로, 도구 호출 인자에 담는 비ASCII 문자열은 리터럴 UTF-8로 쓰고 \uXXXX 유니코드 이스케이프로는 쓰지 말라는 한 줄이 같이 주입됩니다.

모델이 Write나 Edit의 인자에 한국어를 이스케이프로 적으면, 그 이스케이프가 코드 포인트로 해석되지 않고 한 같은 문자열 그대로 파일에 기록되는 경우가 있습니다. 결과물에는 깨진 글자가 남고, 모델은 자기가 쓴 값과 파일 내용이 다르다는 사실을 알아차리지 못한 채 다음 편집을 이어 갑니다. 이스케이프를 쓰지 않으면 이 경로 자체가 생기지 않으므로, 사후에 복구하는 대신 입력 단계에서 막습니다.

이 한 줄은 문체 규칙 파일이 아니라 sprag가 쓰는 안내 문단에 들어갑니다. 문체가 아니라 표기 방식을 정하는 규칙이기 때문입니다. 커밋 메시지에 적용하지 않는 문체 예외와 달리 이 규칙에는 예외를 두지 않습니다. 세션당 약 60 토큰이 늘어납니다.

근거 같은 현상이 Claude Code 저장소에 보고되어 있습니다: #12417 유니코드 처리 회귀, #26141 Edit 도구가 유니코드를 조용히 손상시키는 문제.

설치할 때 물어봅니다

설치 과정에서 지침의 내용과 세션당 비용, 출처를 먼저 보여 준 다음 켤지 물어봅니다. 시스템 로캘이 한국어이면(ko_KR 등, macOS는 시스템 설정까지 확인) 질문의 기본값이 "켬"이 되고, 한국어 환경이 아니면 기본값이 "끔"입니다. 로캘은 답이 아니라 기본값일 뿐이므로 영어 로캘에서 한국어로 작업하는 경우에도 설치 중에 바로 켤 수 있습니다.

npm의 postinstall이나 CI처럼 사람이 붙어 있지 않은 설치에서는 질문을 건너뛰고 로캘 기본값을 그대로 적용합니다. 프롬프트가 멈춰 서면 설치 자체가 걸리기 때문입니다. 이때 로캘이 한국어가 아니면 설정을 저장하지 않고 미결정으로 남겨 두므로, 나중에 터미널에서 다시 설치하면 그때 물어봅니다. 질문 없이 기본값으로 넘기려면 --yes나 --no-input을, 기능 자체를 건너뛰려면 SPRAG_NO_KOREAN=1을 쓰십시오. 한 번이라도 직접 켜거나 끈 뒤에는 그 선택을 유지하므로, 업데이트 설치가 사용자의 결정을 되돌리지 않습니다.

참고 문헌

규칙 파일 머리 주석에도 같은 목록이 조항별로 정리되어 있습니다.

LLM 한국어 판별과 문체 실측

번역투와 공공언어

응집성

sprag 자체 실측 (2026-11-01)

다른 문서