Profanity Filter Library
GITHUB2026.01
1인 개발 (기여도 100%)
Aho-Corasick 알고리즘 기반의 O(N) 비속어 탐색 엔진 및 라이브러리
Technical Insights & Record
1. Why this project? (Motivation)
정규식은 금칙어가 늘수록 O(N×K)로 느려지고, '시1발' 같은 변칙 표현을 커버하려면 패턴이 기하급수적으로 복잡해지는 구조적 문제가 있었습니다.
단순 구현에 그치지 않고 JMH로 기존 방식과 직접 비교 측정하는 것까지 스스로 범위를 확장했습니다. 결과적으로 사이드 프로젝트는 무산됐지만 라이브러리는 끝까지 완성해 Jitpack에 배포했습니다.
2. Performance Results
* JMH(Java Microbenchmark Harness)를 사용하여 직접 측정한 성능 비교 수치입니다.
라이브러리
3.15 ms
단순 정규식
70.64 ms
퍼포먼스 향상
22.4x
시간복잡도
O(N)

3. Architecture & How It Works
Core Architecture
Trie 자료구조를 기반으로 Failure Function을 결합한 아호코라식 알고리즘을 핵심 엔진으로 채택했습니다. 매칭 실패 시 루트로 돌아가지 않고 매칭된 접두사의 가장 긴 접미사 노드로 즉시 이동하여 입력 문자열을 단 한 번만 순회(O(N))하도록 설계했습니다.
Deployment (Jitpack)
오픈소스 라이브러리로 배포하기 위해 Jitpack을 선택했습니다. GitHub 리포지토리의 태그 릴리즈로 버전을 관리하며, Gradle 및 Maven 환경에서 의존성 한 줄만 추가하면 바로 사용할 수 있도록 구성했습니다.

4. Engineering Challenges
변칙 우회 방어 및 원본 인덱스 복원 알고리즘
detectedBans.forEach { ban ->
val isAllowed = detectedAllows.any { allow ->
overlaps(ban.start, ban.end, allow.start, allow.end)
}
if (!isAllowed) {
val originalStart = mapping.indexMap[ban.start]
val originalEnd = mapping.indexMap[ban.end]
for (i in originalStart..originalEnd) {
resultChars[i] = maskChar // 비속어를 **로 마스킹
}
}
}정상 단어 보호를 위한 Interval Overlap 로직
// Interval Overlap: 금칙어가 허용 단어 구간에 포함되면 무시
val remains = detectedBans.filterNot { ban ->
detectedAllows.any { allow ->
overlaps(ban.start, ban.end, allow.start, allow.end)
}
}
private fun overlaps(s1: Int, e1: Int, s2: Int, e2: Int) = s1 <= e2 && s2 <= e15. Lessons Learned
알고리즘 도입으로 실제로 얼마나 차이를 만드는지 JMH로 직접 측정해보기 전까지는 확신이 없었습니다. 수치로 확인하고 나서야 기술 선택이 서비스 품질과 직결된다는 걸 체감했습니다.
처음으로 다른 개발자가 쓸 라이브러리를 만들어보면서, 내가 편한 구조와 쓰는 사람이 편한 구조가 다르다는 걸 알았습니다. API 설계와 Jitpack 배포 과정이 생각보다 신경 쓸 게 많았지만, 이 과정이 재미있게 느껴졌습니다.