채팅 placeholder 노이즈를 서버에서 차단해 검색 품질 개선
목차
채팅 노이즈, 클라이언트만 막아선 안 됨
상담 채팅 로그를 훑다가 "사진을 보냈습니다", "동영상을 보냈습니다", "파일을 보냈습니다" 같은 placeholder 문자열이 그대로 검색 인덱스에 박혀 있는 걸 발견함. 클라이언트에서 미리 필터링하니 괜찮을 줄 알았는데, 외부 채팅 위젯이나 구버전 앱에서 들어오는 메시지는 서버까지 그대로 뚫고 들어왔음. 결국 검색하면 "사진을 보냈습니다 12,847건" 같은 무의미한 결과가 상위에 올라와서 운영팀이 짜증냄.
어디서 막을지 한참 고민함
처음엔 DB 트리거에서 막을까 했는데, 이미 쌓인 데이터까지 손대긴 부담스러웠고 트리거 디버깅이 지옥이라 패스. 결국 메시지 저장 경로의 유틸 레이어에서 단일 진입점을 잡고 거기서 거르기로 함.
판단 기준은 이 정도였음.
- 재사용성: REST/웹소켓/배치 다 같은 함수 타게 만들고 싶음
- 확장성: "이모티콘만 있는 메시지", "공백만 있는 메시지" 같은 케이스 추가될 거 뻔함
- 롤백: 잘못 거르면 고객 메시지가 통째로 사라지므로 토글 가능해야 함
필터 자체는 별 거 없음
private val NOISE_PATTERNS = listOf(
"사진을 보냈습니다",
"동영상을 보냈습니다",
"파일을 보냈습니다",
"이미지를 전송했습니다"
)
fun isNoise(text: String?): Boolean {
val trimmed = text?.trim().orEmpty()
if (trimmed.isEmpty()) return true
return NOISE_PATTERNS.any { trimmed == it }
}
equals 가 아니라 contains 로 짜고 싶은 충동을 꾹 참음. 고객이 "사진을 보냈습니다만 안 열려요" 라고 쓰면 그건 진짜 메시지라 거르면 안 됨. 정확 일치 + trim 만 함.
막상 붙이고 나니 데이터가 더 많이 보였음
| 구분 | 차단 전(일평균) | 차단 후 |
|---|---|---|
| 저장 메시지 수 | 약 4.2만 | 약 3.1만 |
| 검색 노이즈 비율 | 26% | 3% |
| 운영팀 클레임 | 주 2~3건 | 0 |
전체 메시지의 1/4 이 placeholder 였다는 게 충격. 클라이언트 필터링을 믿고 있었던 게 부끄러움.
회고
- 클라 필터는 UX 용, 서버 필터는 데이터 무결성 용 — 둘 다 있어야 함. 한쪽만 있으면 결국 뚫림
- 정규식 욕심내지 말 것. 완전 일치로 시작해서 케이스 쌓이면 그때 확장
- 필터 로그는 카운트만 남기고 본문은 안 남김. 안 그러면 노이즈를 또 다른 노이즈로 적재하는 꼴
다음
댓글 0
첫 댓글 달아줘.