Glin Profanity: 콘텐츠 모더레이션을 위한 실용적인 툴킷

발행: (2025년 12월 31일 오전 08:48 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크 외에 번역할 텍스트가 포함되어 있지 않습니다. 번역을 원하는 본문을 제공해 주시면 한국어로 번역해 드리겠습니다.

Glin‑Profanity란?

Glin‑Profanity는 JavaScript/TypeScriptPython용 오픈‑소스 콘텐츠‑모더레이션 라이브러리입니다.
기본 단어 목록 필터와 달리, 실제 사용자가 시도하는 회피 기법을 처리합니다:

  • 리트스피크 치환 – 예: f4ck, 5h1t
  • 유니코드 동형문자 – 라틴 문자처럼 보이는 키릴 문자
  • 문자 분리 트릭

주요 기능

  • 리트스피크 및 유니코드 정규화 (@$$, fսck, sh!t 등을 포착)
  • 23개 언어 사전 내장
  • TensorFlow.js / TensorFlow 를 통한 선택적 ML 독성 감지
  • LRU 캐싱을 활용한 초당 21 M+ 연산 처리량
  • Node.js, 브라우저, Python 모두에서 동작

실시간 체험

브라우저에서 직접 필터를 테스트해 보세요 — 설치가 필요 없습니다.

Glin‑Profanity Live Demo

인터랙티브 데모 열기

빠른 참고

기능JavaScript / TypeScriptPython
설치npm install glin-profanitypip install glin-profanity
지원 언어23 지원23 지원
성능21 M ops / sec네이티브 C 확장
ML 지원TensorFlow.jsTensorFlow
번들 크기~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);

작동 방식

  1. 프로필 필터 초기화glin-profanity는 leetspeak를 감지하고 영어 사전을 사용하도록 설정됩니다.
  2. 미들웨어 정의profanityMiddleware는 사용자 생성 텍스트가 포함될 수 있는 필드 목록을 순회합니다.
  3. 중첩 값 추출getNestedValue(req, field)는 점 표기법 경로(e.g., req.body.message)에서 값을 안전하게 읽어옵니다.
  4. 욕설 검사 – 어느 필드에든 부적절한 언어가 포함되면, 요청은 400 Bad Request 응답으로 거부됩니다.
  5. 클린일 경우 진행 – 욕설이 발견되지 않으면 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
});

아키텍처

Glin Profanity Processing Flow

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 DemoLink
  • GitHub RepositoryLink
  • npm PackageLink
  • PyPI PackageLink
  • Full DocumentationLink

태그: javascript, typescript, python, react, opensource, webdev, contentmoderation, npm, profanityfilter

Back to Blog

관련 글

더 보기 »