Table of contents
Open Table of contents
Claude에게 설계 문서를 맡기면 결과물이 꽤 그럴듯하게 나온다. 구조도 잡혀 있고 빠진 항목도 별로 없다. 그런데 읽다 보면 어느 순간 글이 머릿속에 들어오지 않는다. 분량은 세 페이지인데 실제로 판단에 필요한 내용은 한 문단이다.
주석도 비슷하다. 메서드 하나에 JavaDoc이 열두 줄씩 붙는데, 그중 여덟 줄은 시그니처를 옮겨 적은 것이다. 불필요하게 느껴지는 내용이 과하게 많이 붙는다.
“좀 더 간결하게 써줘”라고 하면 그 턴에서는 줄어든다. 다음 세션에서는 원래대로 돌아온다. 이건 프롬프트로 매번 고칠 문제가 아니라 규칙으로 박아둘 문제였다.
어떻게 “사람이 읽기 좋은” 문장으로 구성하고, 간결하게 문서를 쓰도록 했는지, CLAUDE.md에 넣은 내용을 정리해본다.
왜 길어지고 왜 어색한가
원인은 두 가지로 나뉜다. 섞어서 다루면 규칙이 애매해지니 분리해서 봤다.
-
장황함은 친절함의 부작용이다: 모델은 빠뜨리는 쪽보다 더 쓰는 쪽을 안전하게 여긴다. 그래서 섹션을 시작하기 전에 이 섹션이 무엇을 다룰지 예고하고, 본문을 쓰고, 맺음말에서 다시 요약한다. 같은 사실이 세 번 나온다. 사람이 쓴 문서라면 성의로 읽히겠지만, 읽는 사람 입장에서는 필요한 한 문장을 찾기 위해 세 배나 많은 문장을 읽어야 한다.
-
번역투는 어순의 문제다: 영어 문장 구조가 한국어 표면에 그대로 남으면 “~하는 것이 가능하다”, “~를 수행하도록 한다” 같은 문장이 나온다. 뜻은 통하는데 주어가 흐려지고 길이가 늘어난다. 여기에 피동과
~적(的)이 붙으면 한국어의 문장 구조가 아니게 된다. “Config 룰에 의해 자동적으로 탐지되어진다” 같은 문장은 정보량이 “Config 룰이 탐지한다”와 같다.
”간결하게”는 지시가 되지 않는다
처음에는 이런 식으로 썼다.
문서는 간결하게 쓴다. 번역투를 피하고 자연스러운 한국어로 작성한다.
효과는 미미했다. “간결”과 “자연스러움”은 모델이 자기 기준으로 해석한다. 이미 자기 딴에는 간결하게 쓰고 있었으니 바뀔 이유가 없다.
그래서 지시를 지켰는지 아닌지 기계적으로 판정할 수 있는 형태로 바꿨다. 네 가지 방식을 썼다.
- 금지 목록: 쓰지 말 것을 이름으로 지목한다.
- 치환표: 금지한 표현을 무엇으로 바꿀지 짝으로 준다.
- 수치 상한: 줄 수, 글자 수처럼 셀 수 있는 한계를 준다.
- 나쁨/좋음 쌍: 같은 내용을 두 버전으로 보여준다.
이렇게 모델이 판정 가능한 기준을 제시했더니 의도한 대로 동작했다. 각 항목에 대한 상세한 내용은 다음과 같다.
표기와 문장 규칙
모든 문장 작성 시 반영되어야 하는 공통 규칙을 따로 정리했다. 엠 대시 등의 기호 금지와 번역투 치환은 예외 상황이 없다. 이런 공통 규칙은 CLAUDE.md 상단에 두고, 문서/주석/커밋 메시지 등의 상세 규칙에서 전부 이걸 참조하게 했다.
엠 대시 금지
**엠 대시(`—`)와 엔 대시(`–`)를 쓰지 않는다.** 한국어에서 흔히 쓰는 기호가 아니라
읽는 흐름을 끊는다. 문서, 코드 주석, 로그 메시지, 커밋 메시지, PR 본문 모두 해당한다.
대신 쉼표, 콜론(`:`), 괄호, 하이픈(`-`), 또는 문장 분리로 표현한다.
- `ClickUp-Task는 필수 — 누락 시 hook이 거부한다`
-> `ClickUp-Task는 필수다. 누락 시 hook이 거부한다`
사소해 보이지만 AI가 쓴 글의 표식처럼 되어 있는 기호다. 한국어에서는 거의 사용하지 않는 기호라 문서에서는 특히 눈에 들어온다.
적용 범위를 문서에 한정하지 않고, 모든 문장을 작성할 때 반영되도록 했다. 문서에만 제한하면 커밋 메시지나 주석에서 그대로 나온다.
번역투 치환표
번역투는 목록으로 나열하는 대신 다음과 같이 표로 짝을 지어 치환할 수 있게 했다. 흔히 사용되는 어색한 번역투 대신 사람이 자연스럽게 읽을 수 있는 문장을 작성하는 핵심이다.
| 번역투 | 우리말 |
|---|---|
| ~를 가지고 있다 | ~가 있다 |
| ~하는 것이 가능하다 | ~할 수 있다 |
| ~할 필요가 있다 | ~해야 한다 |
| ~할 수 있도록 하기 위하여 | ~하려고, ~하기 위해 |
| ~하는 것으로 판단된다 | ~한다, ~로 보인다 |
| ~를 수행하도록 한다 | ~한다 |
| ~를 통하여 | ~로, ~으로 |
| ~에 대하여 검토한다 | ~를 검토한다 |
| ~에 있어서 | ~에서, ~은 |
| ~중의 하나이다 | ~다 |
| 항목들, 룰들 | 항목, 룰 |
| 기본적으로, 일반적으로, 사실상 | (뜻이 안 바뀌면 삭제) |
마지막 줄이 의외로 많이 걸린다. “기본적으로”, “일반적으로”, “사실상”은 문장에서 빼도 뜻이 그대로인 경우가 대부분인데, 습관처럼 붙는다. 뺐을 때 뜻이 바뀌는지를 판정 기준으로 준 덕분에 모델이 스스로 거를 수 있게 됐다.
추가로 표에 담기 어려운 세 가지를 따로 적었다.
- **피동을 남발하지 않는다.** "Config 룰에 의해 탐지된다" -> "Config 룰이 탐지한다".
행위자를 모를 때만 피동을 쓴다
- **이중피동은 쓰지 않는다.** "보여진다", "되어진다" -> "보인다", "된다"
- **`~적(的)`을 붙여 뜻을 흐리지 않는다.** "자동적으로 처리된다" -> "자동으로 처리한다"
이렇게 문장 규칙을 적용하니 AI 특유의 어색한 문장은 대체로 고칠 수 있었고, 개별 문장은 훨씬 자연스럽고 편하게 읽혔다.
문서 작성 규칙
그런데 문장 규칙만으로는 분량이 안 줄었다. 문장 하나하나는 짧아졌는데 문단 수가 그대로였다. 이 문제를 고치려면 구조 차원의 규칙이 따로 필요했다.
중복을 줄인다
장황함의 대부분은 같은 말을 여러 형태로 반복하는 것이었다. 그래서 반복이 나타나는 자리를 하나씩 지목했다.
- **같은 사실을 두 곳에 쓰지 않는다.** 한 곳에 쓰고 나머지는 참조한다.
양쪽에 쓰면 한쪽만 고쳐져서 문서가 스스로와 어긋난다
- 섹션 도입부에 그 섹션이 무엇을 다루는지 예고하지 않는다. 제목이 한다
- 맺음말에서 본문을 다시 요약하지 않는다
- "즉", "다시 말해"로 앞 문단을 바꿔 말하지 않는다. 앞 문단이 안 통했으면 앞 문단을 고친다
- 첫머리 요약은 본문과 층위가 다를 때만 둔다. 본문을 줄여 놓은 것이면 뺀다
섹션 도입부에 예고하지 말 것과 “즉”, “다시 말해”로 앞 문단을 바꿔 말하지 말라는 두 문장이 체감상 가장 효과가 컸다. 제목 다음에 “이 섹션에서는 ~~에 대해 설명한다” 처럼 불필요한 문장이 붙는 패턴이 사라졌고, 앞 문장을 다시 풀어서 설명하는 경우도 줄어들었다. 두 경우 모두 짧은 시간에 똑같은 내용을 여러 번 읽게 만드는 주범이었다.
히스토리를 쓰지 않는다
두 번째로 큰 분량 증가 요인은 경위 서술이었다. 대화 중에 처음엔 “A로 하자”고 했다가 “B로 바꾸자”고 하면, 문서에 그 과정이 그대로 들어간다. 꼭 히스토리를 남겨야 하는 경우가 아니라면 결정이 번복되었다는 내용은 불필요한 사실이다.
**지금 상태를 쓴다.** 어떤 경위로 그렇게 됐는지는 회의록이나 결정 기록이 답한다.
"초기에는 A였으나 B로 바꿨다"는 대개 지울 문장이다. 독자는 B만 알면 된다.
예외는 둘뿐이다.
- 경위를 모르면 유지보수자가 되돌릴 위험이 있는 결정
- 사용자가 이력을 명시적으로 요청한 경우 (회고, 인수인계 문서)
여기서 예외를 명시한 것이 중요하다. 예외 없이 “히스토리 금지”라고만 쓰면, 남겨야 할 결정 배경까지 지워버린다. 금지 규칙을 줄 때는 예외 조건을 같이 주는 편이 안전했다.
문장 중간에서 줄을 바꾸지 않는다
이건 문체와는 별개인데, diff 품질 때문에 넣은 규칙이다.
**줄바꿈은 문장이 끝난 자리에서만 한다.** 화면 너비를 맞추려고 문장을 접지 않는다.
단어 하나를 고쳐도 뒤따르는 줄이 전부 다시 접힌다. diff가 실제 변경보다 커지고,
리뷰에서 무엇이 바뀌었는지 묻힌다.
특정 모델은 80자쯤에서 문장을 접는 습관이 있다. 마크다운에서는 단어 하나만 고쳐도 문단 전체가 다시 접히면서 diff가 순식간에 늘어난다. 그러면 정작 중요한 변경사항은 확인하기 어려워진다.
대신 커밋 메시지는 사내 포맷을 맞추기 위해 72자에서 접어야 해서, 거기서는 예외를 명시했다.
나쁨/좋음 쌍을 나란히 둔다
문서 작성 가이드 끝에는 예시를 붙였다. 앞의 규칙 전부가 한 번에 드러나는 문단을 골랐다.
**나쁨: 예고 + 배경 나열 + 이력 + 동어 반복**
```markdown
## 4.3 리전 위반 탐지
이 섹션에서는 리전 위반 탐지 규칙에 대해 설명한다. 리전 위반 탐지란 허용되지 않은
리전에서 리소스가 생성되는 것을 탐지하기 위한 기능으로, 기본적으로 AWS Config 룰을
활용하여 구현하도록 하였다. 초기에는 허용할 타입을 나열하는 포함 목록 방식으로
설계하였으나, 검토 결과 제외 방식으로 전환하는 것이 타당한 것으로 판단되어
변경하였다. 즉, 허용 타입을 나열하는 대신 제외할 타입을 나열하는 방식이다.
```
**좋음: 결론 먼저, 지금 상태만**
```markdown
## 4.3 리전 위반 탐지
Config 룰이 비허용 17개 리전에서 리소스 생성을 탐지한다. 제외 목록 방식이라 새 리소스 타입이 생겨도 탐지 범위에 자동으로 들어온다.
```
다섯 줄이 한 줄이 됐는데 빠진 정보는 없다. 오히려 읽어야 하는 분량이 줄어들고 비슷한 문장의 반복이 없어 머릿속에 더 잘 들어온다.
주석 작성 규칙
주석은 문서보다 다루기 쉬웠다. 줄 수 상한이라는 명확한 기준을 줄 수 있어서다.
| 대상 | 최대 길이 |
|---|---|
| 변수·필드·상수 | 2줄 |
| 메서드·함수 | 5줄 |
| 히스토리(예외 허용 시) | 5줄 |
여기에 두 가지를 덧붙였다.
- 코드만 봐도 자명한 내용은 주석을 아예 쓰지 않는다.
(`// 캠페인 ID`를 `campaignId` 위에 붙이지 않는다.)
- 파라미터·반환값을 그대로 나열하는 형식적 JavaDoc은 쓰지 않는다.
설명할 제약(단위, 범위, null 허용 여부, 예외)이 있을 때만 남긴다.
“형식적 JavaDoc을 쓰지 않는다”만으로는 부족했다. 그러면 주석을 아예 안 달아버리는 경우가 생긴다. 무엇을 남겨야 하는지(단위, 범위, null 허용 여부, 예외)까지 지목하고 나서야 남길 것과 지울 것이 갈렸다.
주석에서도 이력 서술은 금지하되, 지우면 장애로 이어지는 경우는 예외로 뒀다.
// 나쁨: 이력 + 자명한 설명
/**
* 캠페인 예산 상한.
* 2024-03 기획 변경으로 1000만원 -> 5000만원 상향 (김OO 요청).
* 이전에는 CampaignPolicy에 있었으나 이쪽으로 옮김.
*/
private static final long MAX_BUDGET = 50_000_000L;
// 좋음: 코드에 없는 정보(단위)만
/** 캠페인 일일 예산 상한 (KRW). */
private static final long MAX_BUDGET = 50_000_000L;
// 허용되는 히스토리 주석
// SSP 응답의 price 필드가 문자열로 오는 경우가 있어 방어 파싱한다.
// 파트너 스펙 수정 전까지 제거 금지.
어떤 규칙을 어디에 둘까
이걸 전부 CLAUDE.md 한 파일에 넣으면 몇백 줄이 된다. 그래서 층을 나눴다.
~/Projects/
├── CLAUDE.md # 전 작업 공통: 표기와 문장 규칙
└── guides/
├── document-writing.md # 문서 작성 규칙
├── code-comment.md # 주석 작성 규칙
└── commit-message.md # 커밋 메시지 작성 규칙
CLAUDE.md 하단에서 @ 경로로 불러온다.
## 세부 지침
아래 문서를 따른다. (`@` 경로는 세션 시작 시 자동으로 함께 로드된다.)
- [문서 작성](guides/document-writing.md): @guides/document-writing.md
- [커밋 메시지 작성](guides/commit-message.md): @guides/commit-message.md
- [코드 주석 작성](guides/code-comment.md): @guides/code-comment.md
나눈 기준은 적용 범위다. 엠 대시 금지와 번역투 규칙은 모든 산출물에 걸리니 CLAUDE.md에 두고, 줄 수 상한이나 커밋 footer 규칙처럼 특정 산출물에만 걸리는 건 가이드로 뺐다. 그리고 가이드 안에서는 문장 규칙을 다시 쓰지 않고 참조만 한다.
문장 단위 표현은 CLAUDE.md `표기와 문장 규칙`을 따른다.
문서 작성 가이드에 적은 “같은 사실을 두 곳에 쓰지 않는다”를 가이드 자신에게도 적용한 셈이다. 양쪽에 써두면 한쪽만 고쳤을 때 규칙끼리 어긋난다.
왜 시간이 지나면 흐려지는가
규칙을 적용한 이후에 한동안 잘 쓰다가도, 세션이 길어지면 어느 순간 다시 문장이 어색해지고, 문단이 늘어난다. “아까 그 지침 어디 갔지” 싶은 순간이 온다.
먼저 확인한 건 파일이 빠진 게 아니라는 점이다. CLAUDE.md는 대화 본문과 따로 관리된다. 매 요청마다 대화 앞에 다시 붙으므로 compact로 대화가 요약돼도 지워지지 않고, @로 가져온 가이드도 같이 따라온다. 문제는 내용은 그대로 있는데 모델이 거기에 주는 비중이 줄어드는 거였다.
이유는 네 가지로 보였다.
-
위치가 멀어진다:
CLAUDE.md와 가이드는 대화 맨 앞에 한 번 들어간다. 그 뒤로 수만 토큰이 쌓이면, 모델은 가까운 내용, 특히 직전 몇 턴에 더 강하게 반응한다. 지침은 들어 있어도 덜 지켜진다. -
모델이 자기 출력을 따라 한다: 이게 생각보다 크다. 앞선 답변에 엠 대시나 번역투가 한 번 섞이면, 이후 답변은
CLAUDE.md보다 그 문체를 더 닮아 간다. 대화 안에 실제로 등장한 예시가 규칙 문장보다 강한 신호이기 때문이다. 한 번 어긋나면 갈수록 더 어긋나게 된다. -
경쟁하는 지시가 많다:
/context로 세어보면 지침 파일은 7.5k 토큰인데, 시스템 프롬프트와 도구 설명이 그 몇 배다. 여기에 도구 실행 결과가 계속 쌓인다. 영어 로그, 남의 코드 주석, 영어 웹 문서 같은 것들이다. 이런 텍스트의 문체가 들어올수록 지침의 상대적 비중은 줄어든다. 영어 자료를 한참 읽은 뒤에 한국어 문서를 쓰게 하면 번역투가 유독 많이 올라오는 이유다. -
compact 요약은 원문이 아니다:
CLAUDE.md원문은 남지만 대화 본문은 요약으로 바뀐다. 그래서 “이 세션에서 사용자가 엠 대시를 지적했다” 같은 교정 이력이 요약에서 빠지면, 그 교정의 효과도 같이 사라진다. 방금 고쳐놨는데 compact 후에 되돌아간 것처럼 느껴진다면 대개 이 경우다.
그리고 덧붙이자면, 하위 디렉토리의 CLAUDE.md는 세션 시작 때가 아니라 그 디렉토리 파일을 처음 열 때 불러온다. compact 후에 빠질 수 있고, 그 디렉토리 파일을 다시 열면 다시 들어온다. 하위 디렉토리의 규칙이 적용이 안 되는 것처럼 느껴지는 이유다.
지침을 다시 가까이 두기
| 방법 | 효과 |
|---|---|
| 기계로 판별되는 규칙은 hook으로 검사 | 모델의 주의력에 의존하지 않는다. 가장 확실하다 |
작업이 바뀌면 새 세션이나 /clear | 지침이 다시 가까운 위치로 오고, 쌓인 나쁜 예시가 사라진다 |
| 문서나 커밋 작성은 스킬이나 서브에이전트로 | 작성 직전에 가이드를 새로 읽으므로 지침이 가까워진다 |
CLAUDE.md는 짧게, 핵심 규칙은 맨 위에 | 분량이 적을수록 규칙 하나의 비중이 커진다 |
| 어긋나면 그 자리에서 지적 | 대화 안에 교정 예시가 남아 이후 출력에 반영된다 |
앞의 다섯 가지 중 제일 확실한 건 hook이다. 엠 대시 금지처럼 grep 한 줄로 판별되는 규칙은 모델에게 기억시킬 이유가 없다. Stop이나 PostToolUse에서 —, –를 찾아 걸리면 되돌려 보내면, 세션 길이와 무관하게 똑같이 작동한다.
나머지 네 가지는 결국 지침을 작업 가까이로 다시 가져오는 일이다. 파일을 나누고 @로 불러오는 구조를 택한 것도 여기에 맞물린다. 문서를 쓰기 직전에 해당 가이드만 다시 읽히면, 전부를 맨 앞에 쌓아두는 것보다 효과가 크다.
마치며
위 규칙들을 적용하고 많이 나아진 것은 사실이지만, 그렇다고 완전히 해결되지는 않았다. 그래도 “간결하게 써줘”를 매번 입력하던 시절보다는 낫다. 작업을 여러 번 하는 시간보다 규칙을 정리하는 데 든 시간이 훨씬 짧았다.
정리하면 이렇다. AI에게 문체를 요구할 때는 형용사가 아닌 명확한 판정 기준을 준다. “간결하게”가 아니라 “5줄 이내로”, “자연스럽게”가 아니라 “이 표현은 저 표현으로”. 이 방법이 문체 지시를 실제로 작동하게 만든 전부였다.
다만 지시인 이상 세션이 길어지면 어쩔 수 없이 묻힌다. 기계로 판별되는 규칙까지 모델의 주의력에 맡겨둘 이유는 없으니, 거기서부터는 hook으로 검사하는 게 맞다고 본다.