# 내 AI가 재발 방지 규칙을 또 틀렸다 — A2A 회고, 24시간 후

> 출처: https://retn.kr/blog/nae-aiga-jaebal-bangji-gyucigeul-ddo-teulryeossda-a2a-hoego-24sigan-hu/ · 수정 2026-04-23 · 리텐션 주식회사

## TLDR

- 어제 쓴 "A2A 시대의 Human In The Lead" 포스트 발행 직후 두 가지 새 실수가 터졌다. **Ghost internal tag(**`**#blog-col**`**) 를 일반 태그로 오해**, 그리고 **Ghost + Mailgun 이중 클릭 트래킹**으로 뉴스레터 링크가 블로그로 리다이렉되지 않음.
- 더 아픈 지점: 내가 AI에게 "재발 방지 룰"을 같이 짜게 했더니 AI가 **반대 방향 가드(**`**#**`** 자동 strip)**를 제안할 뻔했다. 운영 직감이 없으면 잘못된 자동화를 굳힐 뻔한 순간.
- 결과적으로 스킬 4개 + 메모리 2개 + 스케줄링 스크립트 1개를 **교정**으로 다시 갱신. 고정 규칙을 만들 때는 AI 한 번, 사람 한 번의 왕복 검증이 필요하다.
- 솔로 파운더 체크리스트: 뉴스레터 발송 전에 (a) 테스트 preview, (b) Mailgun 클릭 트래킹 상태, (c) internal tag prefix 형태 세 가지를 직접 본다.

## 1. 첫 발행의 두 가지 사각지대

어제 "AI가 내 AI를 팩트체크한 날" 포스트를 `retn.kr/ghost` 에 예약 발행했다. 발행까지는 깔끔했는데, 두 가지가 어긋났다.

**블로그 목록 페이지(**`**retn.kr/blog**`**)에 글이 안 떴다.** Ghost 관리자에서 확인해 보니 태그는 전부 있는데 나열되지 않았다. 원인: `retn.kr/blog` 라우트는 `#blog-col` 이라는 **internal tag** 로 필터링 중인데, 내가 넣은 태그는 `blog-col` (해시 없음) 이었다. Ghost에서 `#`-prefix는 UI 장식이 아니라 **"이 태그는 내부용"** 이라는 load-bearing 마커다.

**뉴스레터에서 본문 링크가 리다이렉 되지 않았다.** 구독자들이 이메일에서 블로그 링크를 누르면 404 비슷한 화면으로 튕겼다. 원인: Ghost의 네이티브 클릭 트래킹 + Mailgun의 클릭 트래킹이 같은 링크를 두 번 wrap 하면서 최종 URL이 꼬였다.

## 2. 실수 1 — `#` 이 메타가 아니라 본질이었다

Ghost의 tag 시스템에는 두 종류가 있다.

| 형태 | Ghost 저장 | 용도 |
| --- | --- | --- |
| {"name": "blog-col"} | visibility public, slug blog-col | 공개 태그 페이지에 노출 |
| {"name": "#blog-col"} | visibility internal, slug hash-blog-col | 템플릿/라우트 필터링 전용, 공개 페이지엔 미노출 |

`retn.kr/blog` 의 routes.yaml 은 internal `#blog-col` 로 컬렉션을 만든다. 내가 API 호출로 `{"name": "blog-col"}` 를 보내면 Ghost는 이걸 **다른 태그(public)** 로 저장해 버린다. Slug가 같아 보이지만(`blog-col`), visibility가 다르고 라우트가 못 잡는다.

더 흥미로운 건 **slug 규칙**. `#blog-col` 의 slug 는 Ghost가 `hash-blog-col` 로 치환한다. `#` 이 단순 제거되는 게 아니라 `hash-` 로 바뀌는 것. 이 규칙을 모르면 검증 코드도 틀린다.

## 3. 실수 2 — 클릭 트래킹은 한 군데서만

이메일 링크 체인이 꼬인 원리는 이렇게 생겼다.

![Ghost와 Mailgun이 같은 링크를 이중으로 wrap해 최종 URL이 파괴되는 흐름](__DIAGRAM_URL__)

해결: **Mailgun 도메인 설정에서 Click Tracking을 OFF**. Ghost의 네이티브 트래킹은 남겨 둬서 뉴스레터 내부 분석은 유지. Open tracking은 Mailgun 쪽을 켜도 충돌 없음.

## 4. 진짜 아픈 지점 — AI 가 제안한 재발 방지가 반대 방향이었다

위 두 문제를 발견한 직후, 나는 내 AI에게 "재발 방지 패턴을 같이 짜자" 고 했다. AI의 첫 제안 중 하나:

> "`ghost_schedule.py` 에 태그 sanitize 로직 추가: 입력된 태그 이름 앞에 `#` 이 있으면 `lstrip('#')` 으로 제거한 뒤 저장. 사용자가 실수로 `#` 을 붙여도 방어됨."

이 가드를 심었다면 **앞으로 모든 포스트에서 **`**#blog-col**`** 이 **`**blog-col**`** 로 바뀌어 더 이상 **`**/blog**`** 에 안 뜨는 회귀**가 됐을 것이다. AI는 "`#` 은 사용자가 잘못 붙인 것" 이라는 잘못된 전제 위에서 답을 만들었다.

운영에서 실제 화면을 본 사람만이 **"아니, **`**#**`** 은 Ghost가 이걸 internal 로 취급하라는 신호"** 라고 되돌려 줄 수 있다. 이게 **Human In The Lead의 두 번째 층** — 규칙을 만들 때의 검증이다.

## 5. 실제로 무엇을 고쳤나

어제 세션의 결과를 덮어써서 다음 파일들이 최종 형태가 됐다.

- `~/.claude/skills/retention-style/SKILL.md` — 이미지 전략 섹션을 "최대 3장" 상한으로 명문화. 매 포스트 3장을 강제하지 않음.
- `~/.claude/skills/blog-publisher/SKILL.md` — publish checklist에 `#blog-col` (해시 포함, internal tag 마커), `email_segment=all`, `?newsletter=<slug>` 세 항목 추가.
- `~/.claude/skills/ghost-api-utils/SKILL.md` — internal tag 저장 규칙 (`#` 유지, slug는 `hash-...` 로 변환) 문서화. "lstrip('#') 하지 말 것" 명시.
- `memory/reference_ghost_blog_publishing.md` — Ghost retn.kr 퍼블리싱 체크리스트 전용 메모리.
- `/tmp/ghost_schedule.py` — `EXTRA_TAGS` 에 `#blog-col` 포함 + 응답의 `visibility=internal` 검증 주석.

## 6. 한 번 더 — 테스트 메일은 Post settings 에 없다

이 포스트 자체를 예약 전에 테스트 메일로 확인하려다 또 시간을 잡아먹혔다. Ghost 에서 **"Send test email" 버튼은 Post settings (톱니바퀴) 안에 없다.** 에디터 상단의 별도 **"Preview"** 액션에서만 나온다.

올바른 경로:

1. 스케줄된 포스트라면 먼저 **Unpublish** 해서 draft 로 되돌린다 (scheduled 상태에서는 test email 버튼이 비활성화).
1. 에디터 상단 오른쪽 **"Preview"** 를 누르면 모달이 뜨고 **Web / Email / Desktop / Mobile / Member tier** 탭이 나타난다.
1. **Email 탭** 에서 상단 오른쪽 **"Test"** 버튼을 눌러 본인 주소로 발송.

![Ghost Preview 모달의 Email 탭 상단 우측 ](/content/images/2026/04/ghost-preview-email.png)

메일이 실제로 도착하고 링크가 정상 리다이렉 되는지까지 확인한 뒤 스케줄을 복원한다.

![Gmail 받은편지함에 도착한 Ghost 테스트 뉴스레터 제목 앞에 [TEST] 접두어](/content/images/2026/04/gmail-test-received.png)

이 순서 하나만 알고 있어도 다음 포스트부터 발행 전 체크에 10 초면 끝난다. 아는 사람한테는 당연하지만 **"아는 사람" 이 되는 데 나는 또 한 번 삽질 했다.**

## 7. 메타 교훈 — A2A 피드백 루프에는 두 번째 사람이 필요하다

A2A 시대의 작업 흐름이 이렇게 된다.

1. 사람이 의도를 말한다.
1. AI가 실행 + 규칙을 같이 짠다.
1. 사람이 규칙을 검증한다. ← **이 단계가 빠지면 틀린 자동화가 굳는다.**

어제 내가 놓친 건 1번이 아니라 3번이었다. "`#` 은 실수가 아니라 load-bearing marker" 라는 한 문장을, 내가 직접 화면을 보지 않았으면 AI는 영영 모르고 반대로 굳혔을 것이다.

솔로 파운더가 지켜야 할 최소 루프:

- 뉴스레터 발송 **전**에 preview email을 내 주소로 받아 실제 링크를 클릭해 본다.
- 스케줄링 스크립트가 발행하는 첫 한 건은 **무조건 육안 확인**.
- AI가 "자동 방어" 류 코드를 제안할 때, 그 제안이 **뭘 안 하게 막는지** 까지 읽는다. "무엇을 하는가" 보다 "무엇을 회수하는가" 가 더 중요하다.

**📞 AX 도입 상담**

이 글처럼 AI를 실제 운영 파이프라인에 태우다가 벌어지는 일들을 정리·설계해 드립니다.

- 📧 [Contact Form](/contact/)
- 📅 [30분 예약](https://tidycal.com/simgyusup/30m?ref=retn.kr)
