🔒 내부용 분석 노트 · 고객 비노출. URL 외부 공유 금지.

이메일 스레드 매칭 실패 — kimgihan9895@naver.com

대상 회신: 2026-05-26 19:38 KST "네~ !" · 분석일: 2026-05-27 · 분석자: chlee

TL;DR

고객이 "답장(Reply)" 버튼이 아닌, 메일 클라이언트에서 새 메일을 직접 작성·발송했습니다. 결과적으로 inbound 메일에 In-Reply-To, References 헤더가 빠진 채로 수신되어, 서버의 1순위 매칭 키(emails.message_idin_reply_to)가 비어 작동하지 못했고, email_replies 매핑 행이 생성되지 않아 inbox 에서 이전 발송 스레드와 연결되지 않았습니다. 이는 PR #7922 (multi-reply 덮어쓰기 버그) 와는 별개의 케이스이며, 코드 버그가 아닌 헤더 부재로 인한 알고리즘 한계입니다.
📌 우리 파싱 버그가 아님 — DB 로 검증 완료 (60일 inbound 5,500+ 건) 네이버 inbound 의 95.1%(173/182)는 In-Reply-To 가 정상 파싱·저장되고 있으며, 본 9건의 raw_email 원문을 정규식으로 grep 한 결과 9건 모두 헤더 자체가 부재합니다 (raw_email ~* '^(in-reply-to|references):' → 0 hit). "네이버만 유독" 도 아니며 도메인별 패턴이 명확합니다.

1. 데이터 (beta.emails)

해당 lead 의 모든 메일 (created_at DESC):

시간 (KST) 방향 제목 / 본문 message_id in_reply_to thread_id
05-26 19:38 inbound "제목 : 해외세일즈 자동화건" <3fa93de7…@cmweb01.nm> NULL <3fa93de7…@cmweb01.nm> (자기 자신)
05-26 19:35 inbound (제목 없음, 본문 "네~ !") <f46a427a…@cmweb03.nm> NULL <f46a427a…@cmweb03.nm> (자기 자신)
05-12 12:42 outbound (광고) 돛단배수산 대표님, K-Food 해외 수요 관련하여 공유드립니다 <010c019e1a46…@amazonses.com> <010c019dd6a8…@amazonses.com>
04-30 25:17 outbound (광고) 돛단배수산 해외 영업 관련하여 한 번 더 연락드립니다 <010c019ddf2e…@amazonses.com> <010c019dd6a8…@amazonses.com>
04-29 09:33 outbound (광고) 돛단배수산 해외 영업 관련하여 제안드립니다 <010c019dd6a8…@amazonses.com> <010c019dd6a8…@amazonses.com> (시퀀스 첫발송)

email_replies 매핑 조회

SELECT * FROM email_replies
WHERE reply_email_id IN (<5/26 inbound 2건>)
   OR original_email_id IN (<4/29–5/12 outbound 3건>);

-- 결과: 0 rows

즉 우리 시스템은 두 inbound 를 "어떤 outbound 의 회신" 으로도 인식하지 못했고, 매핑 행 자체가 만들어지지 않았습니다.

2. 헤더 패턴 분석

우리 outbound (SES)

  • Message-ID 도메인: @ap-northeast-2.amazonses.com
  • thread_id = 시퀀스 첫 발송의 Message-ID (010c019dd6a8…)
  • 3건 모두 동일 thread_id 로 묶임 ✓

고객 inbound (네이버)

  • Message-ID 도메인: @cmweb01.nm, @cmweb03.nm (네이버 발신 서버)
  • in_reply_to: NULL
  • references: 캡처되지 않음 (별도 컬럼 없음 + body 에도 인용 없음 추정)
  • thread_id = 자기 자신 Message-ID (= 새 스레드)
네이버 모바일 앱 동작 가설 본문 푸터 "네이버 메일 앱에서 보냈습니다." + In-Reply-To NULL 패턴 → 고객이 받은편지함에서 우리 메일을 보고 별도로 새 메일을 작성해 rinda@send.grinda.ai 로 직접 보냈을 가능성이 큼. (네이버 앱이 정상 "답장" 으로 보낼 때는 In-Reply-To 를 채워서 보내는 것이 기준임 — 다른 회신 메일 케이스에서 확인됨.)

3. 도메인별 In-Reply-To 보유율 (beta inbound 60d)

"우리가 파싱을 못하는 건지 / 네이버만 유독 그런 건지" 검증을 위한 모집단 분포:

도메인 inbound NULL NULL % 해석
naver.com18294.9%정상 — 본 9건이 예외 케이스
gmail.com3725.4%정상
qq.com, dooray.com, helenacoffee.vn, acb.com.vn, umma.io, diligencecertification.com, help.boots.com5~8 each00%완전 정상 (정상 클라이언트 답장)
cosmoprofna.com, qogita.com, cxrd.co6~81~412~67%혼재 — 일부 직접 회신
ap-northeast-2.amazonses.com (SES 시스템 메일)5,1114,80093.9%bounce/complaint/auto-reply — 답장 아님 (제외 모집단)
hanmail.net (다음)2020100%⚠ 다음 웹메일이 헤더를 안 보내는 듯 — 별도 조사 필요
daum.net1212100%⚠ 동일 (다음 메일 인프라)
자사 도메인 (fancl.co.jp, unilever.com, atlifestylestore.com, watsons.co.th, tsum.ru, mail.terravita.fr 등)5~11 each전수100%회사 메일 시스템이 답장 시 새 스레드로 보냄 (또는 CRM/Zoho 등이 인입)
핵심 인사이트

본 9건 (네이버 NULL) 의 raw_email 헤더 검증

SELECT COUNT(*) FILTER (WHERE raw_email ~* '^(in-reply-to|references)[[:space:]]*:') AS has_header,
       COUNT(*) FILTER (WHERE raw_email !~* '^(in-reply-to|references)[[:space:]]*:') AS no_header
FROM emails WHERE id IN (<9개 ID>);

-- has_header: 0 | no_header: 9

즉 우리가 헤더를 받고도 무시한 게 아니라, 메일 원문에 헤더가 도착하지 않은 것이 확정.

본 9건의 subject 패턴

Re:/RE:/회신: prefix 가 단 1건도 없음. 모두 "받은 광고에 대해 새 메일로 자기 소개를 시작하는" 한국 비즈니스 메일 패턴. 옵션 A(from_email + 시간 윈도우) 가 가장 효과적인 모집단.

4. 매칭 알고리즘 vs 이번 케이스

우선순위 매칭 키 코드 위치 이번 케이스 결과
1 In-Reply-To → emails.message_id webhook.service.ts:277, unipile-reply.service.ts ❌ in_reply_to=NULL → 미실행
2 from_email → lead.findLeadByEmail() webhook.service.ts:326 ⚠ lead 식별은 가능하나 thread/sequence 연결은 아님
3 leadId → sequence_enrollments webhook.service.ts:348-366 ⚠ enrollment 추정은 되나 특정 outbound 와의 1:1 매핑은 못 만듦 → email_replies INSERT 조건 불충족
Subject prefix (Re:/회신:) 매칭 미구현 설령 구현돼 있었어도 본 케이스는 prefix 없음 → fail

5. PR #7922 (multi-reply 덮어쓰기) 와의 차이

PR #7922 케이스 본 케이스 (kimgihan9895)
In-Reply-To 헤더 ✓ 있음 (정상 reply) ✗ NULL (새 메일)
email_replies INSERT ✓ 수행됨 (이후 UPDATE 가 덮어씀) ✗ 아예 수행 안 됨
손실 메커니즘 같은 outbound 에 2번째 회신이 들어와 첫 회신 매핑이 UPDATE 로 덮어써짐 매칭 키 부재 → 매핑 미생성
버그 분류 코드 버그 (UPSERT 키 설계 오류) 알고리즘 한계 (헤더 부재 fallback 부족)
수정 완료 (734783db5, 5/27 머지) 미해결 — 아래 옵션 참고

6. 해결 옵션

옵션 A — 발신자 이메일 + 시간 윈도우 기반 best-effort 매핑 (권장)

In-Reply-To 부재 시, 동일 from_email 의 lead 로 보낸 direction=outbound가장 최근(예: 60일 이내)의 outboundoriginal_email_id 로 추정해 매핑.

옵션 B — Subject prefix(Re:, 회신:, 제목 :) 기반 매칭 보조

본 케이스는 inbound subject 가 "제목 : 해외세일즈 자동화건" 으로 Re: prefix 가 없고 outbound subject 와도 직접 일치하지 않음 → 단독으로는 부족. 옵션 A의 tie-break 보조로만 유효.

옵션 C — 본문 quoted-reply 패턴 파싱

본문 안에 인용된 outbound subject/snippet 을 grep 으로 찾아 매칭. 본 케이스의 본문은 "네~ !" 한 줄이라 적용 불가. 일반 케이스 보강용으로는 가치 있음.

옵션 D — 고객 회신 가이드(UX)

발송 메일 푸터에 "이 메일에 직접 답장(Reply) 해주세요" 또는 회신 전용 mailto 링크 노출. 신규 회신만 개선, 본 케이스 같은 모바일 앱 새 메일 작성은 여전히 누락 가능. 옵션 A 와 병행 권장.

7. 해결 후 재분석 TODO

해결 패치 머지 후 이 메모를 업데이트해 다음을 측정해야 함: