Repository navigation
fix: 비속어 검출 위치를 원문 기준으로 계산 - #83
Merged
Merged
Conversation
원문에서 키워드 글자를 indexOf로 다시 찾던 방식을 정리 시점에 기록한
원문 위치로 바꾼다.
- 앞에 같은 첫 글자가 있으면 그 위치부터 잘라 정상 단어까지 마스킹하던
문제("시간 시발" -> "*****")를 고친다.
- Trie는 대소문자를 무시하는데 indexOf는 구분해서, 대문자 영문이
NORMAL/FILTER에서 누락되고 QUICK에서 StringIndexOutOfBoundsException이
나던 문제를 고친다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJgrQc8EvNg9yM7peD7cCF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
비속어 검출 결과의 원문 위치를 원문에서 키워드 글자를
indexOf로 다시 찾는 대신, 정리(ProfanityText.clean)하는 시점에 기록한 위치로 계산합니다.문제
NormalProfanityFilter는 정리된 문자열에서 매칭한 뒤, 원문 위치를text.indexOf(키워드 글자)로 되찾았습니다. 이 방식에서 두 가지 문제가 생겼습니다.ignoreCase()인데indexOf는 대소문자를 구분합니다. 공개 문서(overview.md)는 "대소문자는 신경 쓰지 않아도 된다"고 약속하지만 실제 동작이 달랐습니다.시간 시발*****시간 **바보 시간 시발** ******* 시간 **FUCK you(사전fuck)FUCK(0, 4)FUCK you**** youFUCK youStringIndexOutOfBoundsExceptionFUCK(0, 4)기호를 끼운 우회 검출(
바-보→***)은 변경 전과 같습니다.변경 내용
ProfanityText.cleanWithOffsets:clean과 같은NOT_MATCHABLE패턴으로 정리하면서, 남은 글자마다 원문 위치를 기록합니다. 정리 규칙은 한 곳에 그대로 둡니다.NormalProfanityFilter:Emit.getStart()/getEnd()(끝은 포함 구간)를 기록한 위치로 원문 구간에 옮깁니다.allMatched와firstMatched두 오버로드의 중복 위치 계산을toFilterWord하나로 모았습니다.ignoreCase일 때 글자 단위(Character.toLowerCase(char))로 비교합니다. 그래서 정리된 문자열과 Emit 좌표가 일치합니다.테스트
신규 24개를 추가했습니다.
NormalProfanityFilterTest.OriginalOffsets: 앞에 같은 첫 글자가 있는 경우, 끼운 기호, 반복 단어, 영문 대소문자 4종(fool/FOOL/Fool/fOoL)을 정확한 위치로 단언합니다.DefaultProfanityHandlerTest: FILTER 모드 마스킹 결과(욕심 많은 욕설→욕심 많은 **)를 확인합니다.AllowedWordsFilterTest: 기존allowsEnglishCaseInsensitively는 허용 여부와 관계없이FOOL이 검출되지 않아 통과하던 상태였습니다. 허용하지 않은 경우의 검출 테스트를 추가해 의미를 되살리고, 허용 단어를 건너뛴 뒤의 영문 위치도 확인합니다.ProfanityTextTest:cleanWithOffsets가clean과 같은 결과를 내는지(이모지와 서로게이트 포함), 위치가 원문의 같은 글자를 가리키는지 확인합니다.로컬 검증 결과입니다.
./gradlew staticCheck: 통과./gradlew unitTest --continueAdminPortalMigrationTest,ApiKeyMigrationTest)는 로컬 환경에 Docker가 없어 Testcontainers가 기동하지 않은 것이 원인입니다. main에서도 같은 3개가 같은 이유로 실패합니다.supportTest,apiE2eTest: Docker가 필요해 로컬에서는 실행하지 못했습니다. CI 결과로 확인이 필요합니다.영향과 주의
filteredWord)와 위치, 그리고 마스킹 범위입니다.request_filterCaffeine 캐시(TTL 24시간)에는 변경 전 결과가 남아 있을 수 있습니다. 로컬 캐시라 롤링 업데이트로 새 pod가 뜨면 비워집니다.Generated by Claude Code