답변 한국어 강제 — 매 요청 규칙 주입 + 영어 답변 자동 감지·재작성 (MAMORU)

Project Identity: High-End Scissor Consulting Platform Tech Stack: Imweb(Frontend) + GAS doPost(e)(수신/기록) + Google Sheets(DB) + AppSheet(후속 관리/자동화)
CLAUDE.md 및 .claude/ADDENDUM_* 입니다..claude/ADDENDUM_IMWEB.md 기준탐색 → 정렬 → 안심 → 행동 준비 → 망설임 제거.claude/BRAND_COLOR_SYSTEM.md)#F2F2EA#181725#D4613E (CTA, 배지, 강조).mm- 접두사를 사용합니다.이동 시간 + 상담 시간 기준으로 전후 일정 차단(후속 관리는 AppSheet 우선).projects/as/_gas/| 방식 | 설명 |
|---|---|
| 방문수거 | 롯데택배 기사님이 고객 주소지로 방문하여 수거 |
| 직접발송 | 고객이 직접 택배 발송 (수거비 무료) |
| 항목 | 단가 |
|---|---|
| 마모루 가위 A/S | 10,000원/자루 |
| 타사 가위 A/S | 20,000원/자루 (마모루 가위 접수 시에만 추가 가능) |
| 총 수량 | 수거비 |
|---|---|
| 1자루 | 5,000원 |
| 2자루 | 3,000원 |
| 3자루 이상 | 무료 |
| 직접발송 | 무료 |
총 수량 = 마모루 가위 + 타사 가위 합산
{
"as_id": "AS-20260203-001",
"name": "고객명",
"phone": "01012345678",
"proceed_type": "방문수거|직접발송",
"pickup_date": "2026-02-05",
"qty_mamoru": "2",
"qty_other": "1",
"service_cost": 40000,
"shipping_fee": 0,
"total_amount": 40000
}
projects/
├── common_code/ # 아임웹 공통 코드 (환경설정)
│ ├── header_code_top.txt ← Header Code 상단 (preconnect)
│ ├── header_code.txt ← Header Code (iframe CSS)
│ ├── header_product_style.txt ← Header Code 추가 (상품 진열 공통 스타일)
│ └── footer_code.txt ← Footer Code (iframe JS)
├── product_page/ # 상품 진열 / 카테고리 페이지
│ └── productpage_categorybar.txt ← 카테고리 TOP 코드위젯 (배너+탭)
├── as/ # A/S 접수
│ ├── page_form.html ← GitHub Pages 배포 (실제 콘텐츠)
│ ├── page_guide.html ← GitHub Pages 배포
│ ├── iframe_form.html ← 아임웹 코드위젯에 붙여넣기 (/53)
│ ├── iframe_guide.html ← 아임웹 코드위젯에 붙여넣기 (/60)
│ ├── icons/ # SVG 아이콘
│ ├── holidays_2026_2027.tsv # 공휴일 원본 데이터
│ └── _gas/ # GAS 백엔드 (Code.gs 등)
├── consulting/ # 상담접수
│ ├── page_form.html ← GitHub Pages 배포
│ ├── page_diag.html ← GitHub Pages 배포
│ ├── page_guide.html ← GitHub Pages 배포
│ ├── page_recommend.html ← GitHub Pages 배포
│ ├── iframe_form.html ← 아임웹 코드위젯
│ ├── iframe_diag.html ← 아임웹 코드위젯
│ ├── iframe_guide.html ← 아임웹 코드위젯 (/61)
│ ├── iframe_recommend.html ← 아임웹 코드위젯
│ ├── products.js # 제품 데이터 (추천 로직)
│ └── Code.gs # GAS 백엔드
└── brand/ # 브랜드 소개
├── page_intro.html ← GitHub Pages 배포
├── iframe_intro.html ← 아임웹 코드위젯
└── Code.gs # GAS 백엔드
| 계층 | 파일 | 배포 방식 | 변경 시 반영 |
|---|---|---|---|
| 공통 코드 | common_code/*.txt | 아임웹 환경설정에 수동 붙여넣기 | 아임웹 환경설정에서 직접 교체 |
| 콘텐츠 | page_*.html | GitHub Pages 자동 배포 | git push → 1~2분 후 자동 |
| 코드위젯 | iframe_*.html, productpage_*.txt | 아임웹 코드위젯에 수동 붙여넣기 | 아임웹에서 직접 교체 필요 |
| 백엔드 | Code.gs | GAS 에디터에서 배포 | GAS 새 배포 필요 |
중요:
page_*.html수정은 push만 하면 되지만,iframe_*.html수정 시에는 아임웹 해당 페이지의 코드위젯도 동일 내용으로 교체해야 반영됩니다.
| 아임웹 환경설정 영역 | 로컬 파일 | 역할 |
|---|---|---|
| Header Code 상단 | common_code/header_code_top.txt | preconnect/dns-prefetch |
| Header Code | common_code/header_code.txt + header_product_style.txt | iframe CSS + 상품 진열 공통 스타일 |
| Footer Code | common_code/footer_code.txt | iframe 높이/모달 뷰포트 JS |
상품 진열 스타일은 Header Code에 한 번만 넣으면 전 카테고리 페이지에 적용됩니다. 새 상품 위젯 추가 시
header_product_style.txt의IDS배열에 위젯 ID만 추가하면 됩니다.
| 아임웹 경로 | 코드위젯 파일 | 로드하는 콘텐츠 |
|---|---|---|
/53 | projects/as/iframe_form.html | AS 접수 폼 |
/60 | projects/as/iframe_guide.html | AS 안내 |
/61 | projects/consulting/iframe_guide.html | 상담 안내 |
| (상담접수) | projects/consulting/iframe_form.html | 상담접수 폼 |
| (간편진단) | projects/consulting/iframe_diag.html | 간편진단 |
| (맞춤추천) | projects/consulting/iframe_recommend.html | 맞춤 추천 |
| (브랜드) | projects/brand/iframe_intro.html | 브랜드 소개 |
각 카테고리 페이지(블런트/틴닝/장가위/슬라이싱)는 코드위젯 2개로 구성:
| 순서 | 코드위젯 | 파일 | 역할 |
|---|---|---|---|
| 1 | 카테고리 TOP | productpage_categorybar.txt | 배너 이미지(PC/모바일 반응형) + 텍스트 오버레이 + 블랙 칩 탭 |
| 2 | 상품 진열 | 아임웹 기본 상품 위젯 | 스타일은 공통 코드(header_product_style.txt)에서 일괄 적용 |
카테고리 TOP 수정 포인트 (productpage_categorybar.txt):
<source srcset="..."> / 
— 배너 이미지 URL (PC/모바일).cat-banner-en — 영문 카테고리명 (예: BLUNT).cat-banner-kr — 한글 설명 (예: 커트 시작의 출발점).cat-mini a.active — 현재 페이지에 해당하는 탭에 active 클래스카테고리 페이지 매핑:
| 아임웹 경로 | 카테고리 | 상품 위젯 ID |
|---|---|---|
/29 | 블런트 | w2025082780aa858e21dc6 |
/30 | 틴닝 | (위젯 ID 확인 후 IDS 배열에 추가) |
/39 | 장가위 | (위젯 ID 확인 후 IDS 배열에 추가) |
/40 | 슬라이싱 | (위젯 ID 확인 후 IDS 배열에 추가) |
모든 page_*.html ↔ iframe_*.html 통신에 사용되는 표준 메시지 타입:
| 메시지 type | 방향 | 용도 |
|---|---|---|
MAMORU_IFRAME_SIZE | page → parent | iframe 높이 자동 조절 ({ height }) |
MAMORU_SCROLL_TOP | page → parent | 부모 페이지 맨 위로 스크롤 |
MAMORU_NAVIGATE | page → parent | 부모 페이지 이동 ({ url }) |
MAMORU_REQUEST_VIEWPORT | page → parent | 모달 중앙 배치용 뷰포트 정보 요청 |
MAMORU_VIEWPORT_INFO | parent → page | 뷰포트 정보 응답 ({ visibleTop, visibleHeight }) |
MAMORU_REQUEST_VIEWPORT/MAMORU_VIEWPORT_INFO는 iframe 내부에서 모달/달력을 현재 보이는 화면 중앙에 배치하기 위해 사용합니다. 모달이 있는 page에서 요청하면, iframe_*.html(부모)이 응답합니다.
7aa062f)<picture> PC/모바일 반응형 (6a8cb1e)73fb61d)IDS)로 일괄 관리, 페이지별 스타일 코드위젯 불필요 (22eb8c2)projects/common_code/ 폴더 신설 (83c4a2d)3324f94)openCalendar() 중복 호출 가드 (06488fa)removeAttribute('style') 타이밍 개선 (52b084a)iframe_form.html에 MAMORU_REQUEST_VIEWPORT 핸들러 추가 — iframe 내 모달 중앙 배치 지원 (52b084a) > 아임웹 /53 코드위젯도 동일 내용으로 교체 필요CLAUDE.md 및 .claude/ADDENDUM_*를 따릅니다.docs/reference/BRAND_GUIDE_FULL.mdhooks/register.ts 39 lines1import type { Register } from 'claude-code'
2
3import { englishHeavy } from './lang'
4
5const RULE = `[답변 언어 — 절대 규칙 (사장님 지시, korean-only mod)]
6사장님(사용자)에게 보이는 모든 글은 처음부터 끝까지 한국어로 쓴다: 최종 답변, 도구 사용 사이의 진행 메시지, 표, 목록, 제목, 체크리스트, 기술 설명 전부.
7도구 결과·문서·로그가 영어여도 설명은 한국어로 옮겨 쓴다. 영어는 화면에 실제로 보이는 메뉴명·명령어·코드·파일 경로를 괄호나 코드 표기로 덧붙일 때만 허용.
8보내기 전에 영어 문장이 섞였는지 스스로 확인한다.`
9
10const REWRITE = '방금 답변이 영어 위주로 쓰였습니다. 같은 내용을 빠짐없이 한국어로 다시 써 주세요. (korean-only mod 자동 요청)'
11
12let isRewriting = false // 재작성 요청이 무한 반복되지 않게 1회만
13
14export const register: Register = on => {
15 // 매 요청마다 시스템 프롬프트 끝에 한국어 규칙을 붙인다
16 on('prompt.compose', async ($, e, next) => {
17 const composed = await next(e)
18 return { ...composed, sections: [...composed.sections, { id: 'korean-only:rule', text: RULE, scope: 'session' as const }] }
19 })
20
21 // 사장님 메시지마다 바로 옆에 한국어 규칙을 한 번 더 붙인다 (시스템 프롬프트보다 가까워 가장 잘 지켜짐)
22 on('prompt.submit', async ($, e, next) => next({ ...e, context: [...(e.context ?? []), RULE] }))
23
24 // 답변이 끝나면 영어 위주인지 검사 → 경고 + 한국어 재작성 자동 요청
25 on('turn.complete', async ($, e, next) => {
26 const done = await next(e)
27 if (isRewriting) {
28 isRewriting = false
29 return done
30 }
31 if (e.reason === 'answer' && englishHeavy(done.text)) {
32 isRewriting = true
33 $.ui.toast('⚠ 영어 답변 감지 — 한국어로 다시 쓰게 했습니다')
34 void $.prompt.submit({ text: REWRITE })
35 }
36 return done
37 })
38}
39hooks/lang.ts 14 lines1// 답변이 영어 위주인지 판정 — 코드·경로·주소·표 구분선은 빼고 순수 문장만 센다
2export function englishHeavy(text: string): boolean {
3 const prose = text
4 .replace(/```[\s\S]*?```/g, ' ') // 코드 블록
5 .replace(/`[^`]*`/g, ' ') // 인라인 코드·경로
6 .replace(/https?:\/\/\S+/g, ' ') // 주소
7 .replace(/\[[^\]]*\]\([^)]*\)/g, ' ') // 마크다운 링크
8 .replace(/[A-Za-z]:[\/][^\s)]*/g, ' ') // 윈도우 경로
9 const hangul = (prose.match(/[가-힣]/g) ?? []).length
10 const latin = (prose.match(/[A-Za-z]/g) ?? []).length
11 if (latin < 60) return false // 짧은 영어(메뉴명·약어)는 허용
12 return hangul / (hangul + latin) < 0.5
13}
14