Glin Profanity: 콘텐츠 모더레이션을 위한 실용적인 툴킷
Source: Dev.to
위에 제공된 소스 링크 외에 번역할 텍스트가 포함되어 있지 않습니다. 번역을 원하는 본문을 제공해 주시면 한국어로 번역해 드리겠습니다.
Glin‑Profanity란?
Glin‑Profanity는 JavaScript/TypeScript와 Python용 오픈‑소스 콘텐츠‑모더레이션 라이브러리입니다.
기본 단어 목록 필터와 달리, 실제 사용자가 시도하는 회피 기법을 처리합니다:
- 리트스피크 치환 – 예:
f4ck,5h1t - 유니코드 동형문자 – 라틴 문자처럼 보이는 키릴 문자
- 문자 분리 트릭
주요 기능
- 리트스피크 및 유니코드 정규화 (
@$$,fսck,sh!t등을 포착) - 23개 언어 사전 내장
- TensorFlow.js / TensorFlow 를 통한 선택적 ML 독성 감지
- LRU 캐싱을 활용한 초당 21 M+ 연산 처리량
- Node.js, 브라우저, Python 모두에서 동작
실시간 체험
브라우저에서 직접 필터를 테스트해 보세요 — 설치가 필요 없습니다.
빠른 참고
| 기능 | JavaScript / TypeScript | Python |
|---|---|---|
| 설치 | npm install glin-profanity | pip install glin-profanity |
| 지원 언어 | 23 지원 | 23 지원 |
| 성능 | 21 M ops / sec | 네이티브 C 확장 |
| ML 지원 | TensorFlow.js | TensorFlow |
| 번들 크기 | ~45 KB (트리‑쉐이킹 가능) | 해당 없음 |
설치
JavaScript / TypeScript
npm install glin-profanity
Python
pip install glin-profanity
선택적 ML 독성 지원 (JavaScript 전용)
npm install glin-profanity @tensorflow/tfjs
코드 템플릿
템플릿 1 – 기본 욕설 검사
자바스크립트
import { checkProfanity } from 'glin-profanity';
const result = checkProfanity('user input here', {
languages: ['english']
});
if (result.containsProfanity) {
console.log('Blocked words:', result.profaneWords);
}
파이썬
from glin_profanity import Filter
filter = Filter({"languages": ["english"]})
result = filter.check_profanity("user input here")
if result.contains_profanity:
print(f"Blocked words: {result.profane_words}")
템플릿 2 – Leetspeak 및 유니코드 회피 탐지
포착: f4ck, 5h1t, @$$, fսck (Cyrillic), s.h" "i.t
import { Filter } from 'glin-profanity';
const filter = new Filter({
detectLeetspeak: true,
leetspeakLevel: 'aggressive', // 'basic' | 'moderate' | 'aggressive'
normalizeUnicode: true
});
filter.isProfane('f4ck'); // true
filter.isProfane('5h1t'); // true
filter.isProfane('@$$'); // true
filter.isProfane('fսck'); // true (Cyrillic 'ս')
Leetspeak 레벨
| 레벨 | 설명 |
|---|---|
basic | 일반적인 대체 (4→a, 3→e, 1→i, 0→o) |
moderate | + 확장된 기호 (@→a, $→s, !→i) |
aggressive | + 분리된 문자, 혼합 패턴 |
템플릿 3 – 다국어 감지
// Detect a specific set of languages
const filter = new Filter({
languages: ['english', 'spanish', 'french', 'german']
});
// Detect all supported languages
const filterAll = new Filter({ allLanguages: true });
지원 언어
arabic, chinese, czech, danish, dutch, english, esperanto,
finnish, french, german, hindi, hungarian, italian, japanese,
korean, norwegian, persian, polish, portuguese, russian,
spanish, swedish, thai, turkish
템플릿 4 – 자동 욕설 교체
const filter = new Filter({
replaceWith: '***',
detectLeetspeak: true
});
const result = filter.checkProfanity('What the f4ck');
console.log(result.processedText); // "What the ***"
사용자 정의 교체 패턴
// Asterisks matching word length
{ replaceWith: '*' } // "f**k" → "****"
// Fixed replacement
{ replaceWith: '[FILTERED]' } // "f**k" → "[FILTERED]"
// Character‑based
{ replaceWith: '#' } // "f**k" → "####"
템플릿 5 – 심각도 기반 중재
import { Filter, SeverityLevel } from 'glin-profanity';
const filter = new Filter({ detectLeetspeak: true });
const result = filter.checkProfanity(userInput);
switch (result.maxSeverity) {
case SeverityLevel.HIGH:
blockMessage(result);
notifyModerators(result);
break;
case SeverityLevel.MEDIUM:
sendFiltered(result.processedText);
flagForReview(result);
break;
case SeverityLevel.LOW:
sendFiltered(result.processedText);
break;
default:
send(userInput);
}
템플릿 6 – 실시간 입력을 위한 React Hook
import { useProfanityChecker } from 'glin-profanity';
function ChatInput() {
const { result, checkText, isChecking } = useProfanityChecker({
detectLeetspeak: true,
languages: ['english']
});
return (
<div>
<input
type="text"
onChange={e => checkText(e.target.value)}
placeholder="Type a message..."
disabled={isChecking}
/>
{result?.containsProfanity && (
<p style={{ color: 'red' }}>
Please remove inappropriate language.
</p>
)}
</div>
);
}
템플릿 7 – ML 독성 감지 (v3+)
명시적인 욕설 없이 독성 콘텐츠를 포착, 예시:
- “당신은 최악의 플레이어입니다”
- “아무도 당신을 원하지 않아요”
- “그냥 그만두세요”
import { loadToxicityModel, checkToxicity } from 'glin-profanity/ml';
// Load once on app startup
await loadToxicityModel({ threshold: 0.9 });
// Check any text
const result = await checkToxicity("You're terrible at this");
Source: …
f (result.isToxic) {
console.log('Toxic content detected');
}
예시 출력
console.log(result);
// {
// toxic: true,
// categories: {
// toxicity: 0.92,
// insult: 0.87,
// threat: 0.12,
// identity_attack: 0.08,
// obscene: 0.45
// }
// }
Note: 머신러닝 모델은 100 % 로컬에서 실행됩니다. API 호출이 없으며, 데이터가 서버를 떠나지 않습니다.
템플릿 8 – 전체 채팅 모더레이션 파이프라인
import { Filter, SeverityLevel } from 'glin-profanity';
import { loadToxicityModel, checkToxicity } from 'glin-profanity/ml';
// Setup
const filter = new Filter({
languages: ['english', 'spanish'],
detectLeetspeak: true,
leetspeakLevel: 'moderate',
normalizeUnicode: true,
replaceWith: '***',
});
await loadToxicityModel({ threshold: 0.85 });
// Moderation function
async function moderateMessage(text) {
// 1️⃣ Fast rule‑based check
const profanity = filter.checkProfanity(text);
// 2️⃣ ML toxicity check
const toxicity = await checkToxicity(text);
// 3️⃣ Decision logic
if (profanity.maxSeverity === SeverityLevel.HIGH) {
return { action: 'block', reason: 'severe_profanity' };
}
if (toxicity.toxic) {
return {
action: 'flag',
text: profanity.processedText,
reason: 'toxic_content',
};
}
if (profanity.containsProfanity) {
return { action: 'filter', text: profanity.processedText };
}
return { action: 'allow', text };
}
// Usage
const result = await moderateMessage('User message here');
템플릿 9 – Express.js 미들웨어
import { Filter } from 'glin-profanity';
import express from 'express';
import { commentHandler } from './handlers/commentHandler.js';
import { getNestedValue } from './utils/getNestedValue.js';
const app = express();
const filter = new Filter({
detectLeetspeak: true,
languages: ['english'],
});
function profanityMiddleware(req, res, next) {
// Fields (dot‑notation) that should be scanned for profanity
const fieldsToCheck = ['body.message', 'body.comment', 'body.bio'];
for (const field of fieldsToCheck) {
const value = getNestedValue(req, field);
if (value && filter.isProfane(value)) {
return res.status(400).json({
error: 'Content contains inappropriate language',
});
}
}
next();
}
// Route example
app.post('/api/comments', profanityMiddleware, commentHandler);
작동 방식
- 프로필 필터 초기화 –
glin-profanity는 leetspeak를 감지하고 영어 사전을 사용하도록 설정됩니다. - 미들웨어 정의 –
profanityMiddleware는 사용자 생성 텍스트가 포함될 수 있는 필드 목록을 순회합니다. - 중첩 값 추출 –
getNestedValue(req, field)는 점 표기법 경로(e.g.,req.body.message)에서 값을 안전하게 읽어옵니다. - 욕설 검사 – 어느 필드에든 부적절한 언어가 포함되면, 요청은 400 Bad Request 응답으로 거부됩니다.
- 클린일 경우 진행 – 욕설이 발견되지 않으면
next()가 다음 핸들러(commentHandler예시)로 제어를 넘깁니다.
다른 라우트에 미들웨어 추가
app.put('/api/profile', profanityMiddleware, profileUpdateHandler);
app.post('/api/posts', profanityMiddleware, postCreateHandler);
유틸리티: getNestedValue
// utils/getNestedValue.js
export function getNestedValue(obj, path) {
return path.split('.').reduce((acc, key) => (acc ? acc[key] : undefined), obj);
}
템플릿 10 – 사용자 정의 화이트리스트 / 블랙리스트
const filter = new Filter({
languages: ['english'],
ignoreWords: ['hell', 'damn'], // Allow these words
customWords: ['badword', 'toxic'] // Add custom blocked words
});
아키텍처
Performance Benchmarks
| 작업 | 속도 |
|---|---|
| 간단한 검사 | 21 M ops/sec |
| 레트스피크 사용 (보통) | 8.5 M ops/sec |
| 다국어 (3개 언어) | 18 M ops/sec |
| 유니코드 정규화 | 15 M ops/sec |
결과는 LRU 전략을 사용하여 캐시됩니다.
API 빠른 참조
필터 옵션
interface FilterOptions {
languages?: string[]; // ['english', 'spanish', …]
allLanguages?: boolean; // Check all 23 languages
detectLeetspeak?: boolean; // Enable leetspeak detection
leetspeakLevel?: 'basic' | 'moderate' | 'aggressive';
normalizeUnicode?: boolean; // Handle Unicode homoglyphs
replaceWith?: string; // Replacement character/string
ignoreWords?: string[]; // Whitelist
customWords?: string[]; // Additional blocked words
}
결과 객체
interface CheckResult {
containsProfanity: boolean;
profaneWords: string[];
processedText: string; // Text after replacements are applied
maxSeverity: SeverityLevel;
matches: MatchDetail[];
}
리소스
- Live Demo – Link
- GitHub Repository – Link
- npm Package – Link
- PyPI Package – Link
- Full Documentation – Link
태그: javascript, typescript, python, react, opensource, webdev, contentmoderation, npm, profanityfilter

