Skip to main content

금칙어 필터링 모듈 - 데이터 주입 방식

Project description

sg-word-filter-python

PyPI version Python Versions License: MIT

고성능 금칙어 필터링 모듈 (Python) - 정확 일치 및 부분 문자열 검색 지원

✨ 특징

  • 빠른 검색: Set 기반 O(1) 정확 일치 + Aho-Corasick 부분 문자열 검색
  • 🔒 싱글톤 패턴: 앱 라이프사이클 동안 단 한 번만 초기화
  • 🛡️ 스레드 안전: Double-checked locking으로 멀티스레드 환경 지원
  • 🌐 다국어 지원: UTF-8 완벽 지원 (한글, 영어, 일본어 등)
  • 📦 경량: 핵심 기능만 포함 (~15KB)
  • 🧪 완벽한 테스트: 100% 코드 커버리지
  • 🔤 타입 힌트: 완전한 타입 어노테이션 포함

📦 설치

PyPI (공개 레지스트리)

pip install sg-word-filter-python

GitHub (인하우스)

Git에서 직접 설치

# 최신 버전
pip install git+https://github.com/yourorg/sg-word-filter-python.git

# 특정 버전/태그
pip install git+https://github.com/yourorg/sg-word-filter-python.git@v1.0.0

# 특정 브랜치
pip install git+https://github.com/yourorg/sg-word-filter-python.git@main

GitHub Releases에서 Wheel 설치

pip install https://github.com/yourorg/sg-word-filter-python/releases/download/v1.0.0/sg_word_filter-1.0.0-py3-none-any.whl

requirements.txt에 추가

# Git repository
sg-word-filter-python @ git+https://github.com/yourorg/sg-word-filter-python.git@v1.0.0

# 또는 Release wheel
sg-word-filter-python @ https://github.com/yourorg/sg-word-filter-python/releases/download/v1.0.0/sg_word_filter-1.0.0-py3-none-any.whl

자세한 배포 가이드는 DEPLOY.md를 참조하세요.

🚀 빠른 시작

1. 금칙어 파일 준비 (config/blocklist.json)

["나쁜말", "욕설", "비속어", "금지어"]

2. 모듈 초기화 및 사용

import sg_word_filter

# 애플리케이션 시작 시 초기화 (단 한 번만!)
sg_word_filter.init('./config/blocklist.json')

# ===== 정확 일치 검색 =====
print(sg_word_filter.is_forbidden('나쁜말'))  # True
print(sg_word_filter.is_forbidden('정상단어'))  # False

# 텍스트에서 금칙어 찾기
words = sg_word_filter.find_forbidden_words('이것은 나쁜말 과 욕설 입니다')
print(words)  # ['나쁜말', '욕설']

# 금칙어 포함 여부만 확인
has_bad = sg_word_filter.has_forbidden_words('이것은 나쁜말입니다')
print(has_bad)  # True

# ===== 부분 문자열 검색 (Aho-Corasick) =====
results = sg_word_filter.find_forbidden_substrings('나쁜말과욕설입니다')
print(results)
# [
#   {'word': '나쁜말', 'index': 0},
#   {'word': '욕설', 'index': 5}
# ]

# 부분 문자열 포함 여부
has_substring = sg_word_filter.has_forbidden_substrings('나쁜것')
print(has_substring)  # True (금칙어 목록에 "나쁜"이 있다면)

📖 API 레퍼런스

초기화 및 상태 관리

init(filepath, *, silent=False)

금칙어 목록을 파일에서 로드하여 초기화합니다. 앱 라이프사이클 중 단 한 번만 호출되어야 합니다.

sg_word_filter.init('./config/blocklist.json', silent=True)

Parameters:

  • filepath (str): JSON 배열 형태의 금칙어 파일 경로
  • silent (bool, keyword-only): True일 경우 콘솔 로그 출력 억제 (기본값: False)

Raises:

  • FilterError: 파일을 찾을 수 없거나, JSON 형식이 잘못되었거나, 배열이 아닌 경우

get_status()

모듈의 현재 상태 정보를 반환합니다.

status = sg_word_filter.get_status()
print(status)
# {
#   'initialized': True,
#   'count': 10,
#   'file_path': '/absolute/path/to/blocklist.json',
#   'loaded_at': '2025-11-18T10:30:00.000000'
# }

Returns: dict - {'initialized', 'count', 'file_path', 'loaded_at'}

reset()

모듈을 초기화 이전 상태로 되돌립니다. 주로 테스트 용도로 사용됩니다.

sg_word_filter.reset()

정확 일치 검색

is_forbidden(word)

단어가 금칙어 목록에 정확히 일치하는지 확인합니다.

sg_word_filter.is_forbidden('나쁜말')  # True
sg_word_filter.is_forbidden('나쁜')    # False (부분 일치는 False)

Parameters:

  • word (str): 확인할 단어

Returns: bool

find_forbidden_words(text, *, delimiter=None)

텍스트를 단어 단위로 분리하여 금칙어를 찾습니다.

words = sg_word_filter.find_forbidden_words('나쁜말 과 욕설')
# ['나쁜말', '욕설']

# 사용자 정의 구분자
words2 = sg_word_filter.find_forbidden_words('나쁜말,욕설,정상', delimiter=',')
# ['나쁜말', '욕설']

# 정규표현식 구분자
import re
words3 = sg_word_filter.find_forbidden_words('나쁜말.욕설!정상', delimiter=re.compile(r'[.,!]'))

Parameters:

  • text (str): 검색할 텍스트
  • delimiter (str|re.Pattern, keyword-only): 단어 구분자 (기본값: r'\s+')

Returns: List[str] - 발견된 금칙어 리스트

has_forbidden_words(text, *, delimiter=None)

텍스트에 금칙어가 포함되어 있는지 확인합니다.

sg_word_filter.has_forbidden_words('이것은 나쁜말입니다')  # True

Parameters:

  • text (str): 검색할 텍스트
  • delimiter (str|re.Pattern, keyword-only): find_forbidden_words()와 동일

Returns: bool

부분 문자열 검색 (Aho-Corasick)

find_forbidden_substrings(text, *, case_sensitive=False)

Aho-Corasick 알고리즘을 사용하여 텍스트에서 금칙어를 부분 문자열로 검색합니다.

# 금칙어 목록: ['나쁜', '욕']
results = sg_word_filter.find_forbidden_substrings('나쁜말과 욕설입니다')
# [
#   {'word': '나쁜', 'index': 0},
#   {'word': '욕', 'index': 6}
# ]

# 대소문자 구분
results2 = sg_word_filter.find_forbidden_substrings('Bad WORST', case_sensitive=True)

Parameters:

  • text (str): 검색할 텍스트
  • case_sensitive (bool, keyword-only): 대소문자 구분 여부 (기본값: False)

Returns: List[Dict[str, Any]] - [{'word': str, 'index': int}, ...]

시간 복잡도: O(n + m) - n: 텍스트 길이, m: 매칭 결과 수

has_forbidden_substrings(text, *, case_sensitive=False)

텍스트에 금칙어 부분 문자열이 포함되어 있는지 확인합니다.

# 금칙어 목록: ['나쁜']
sg_word_filter.has_forbidden_substrings('나쁜말')  # True
sg_word_filter.has_forbidden_substrings('좋은말')  # False

Parameters:

  • text (str): 검색할 텍스트
  • case_sensitive (bool, keyword-only): find_forbidden_substrings()와 동일

Returns: bool

예외 클래스

FilterError

모듈에서 발생하는 모든 예외는 FilterError 클래스를 사용합니다.

try:
    sg_word_filter.init('/invalid/path.json')
except sg_word_filter.FilterError as e:
    print(f'에러 코드: {e.code}')
    print(f'메시지: {e}')

에러 코드:

  • FILE_NOT_FOUND: 파일을 찾을 수 없음
  • INVALID_JSON: JSON 파싱 실패
  • INVALID_FORMAT: 배열 형태가 아님
  • NOT_INITIALIZED: 초기화되지 않은 상태에서 함수 호출
  • INVALID_INPUT: 잘못된 입력 타입
  • UNEXPECTED_ERROR: 예상치 못한 오류

🎯 사용 사례

FastAPI 애플리케이션

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import sg_word_filter

app = FastAPI()

# 앱 시작 시 초기화
@app.on_event("startup")
async def startup_event():
    sg_word_filter.init('./config/blocklist.json')

class Comment(BaseModel):
    text: str

@app.post("/comment")
async def create_comment(comment: Comment):
    bad_words = sg_word_filter.find_forbidden_words(comment.text)
    
    if bad_words:
        raise HTTPException(
            status_code=400,
            detail={
                'error': '금칙어가 포함되어 있습니다',
                'words': bad_words
            }
        )
    
    return {'success': True}

Django 미들웨어

# middleware.py
import sg_word_filter

class ProfanityFilterMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        sg_word_filter.init('./config/blocklist.json', silent=True)

    def __call__(self, request):
        if request.method == 'POST' and request.POST.get('comment'):
            comment = request.POST['comment']
            
            # 부분 문자열 검색으로 우회 시도 방지
            violations = sg_word_filter.find_forbidden_substrings(comment)
            
            if violations:
                from django.http import JsonResponse
                return JsonResponse({
                    'error': '메시지에 부적절한 내용이 포함되어 있습니다',
                    'violations': violations
                }, status=400)
        
        response = self.get_response(request)
        return response

데코레이터 패턴

import sg_word_filter
from functools import wraps

sg_word_filter.init('./config/blocklist.json', silent=True)

def filter_profanity(func):
    @wraps(func)
    def wrapper(text, *args, **kwargs):
        if sg_word_filter.has_forbidden_words(text):
            raise ValueError('텍스트에 금칙어가 포함되어 있습니다')
        return func(text, *args, **kwargs)
    return wrapper

@filter_profanity
def process_comment(text):
    print(f'댓글 처리: {text}')
    return True

# 사용
process_comment('정상적인 댓글')  # OK
process_comment('나쁜말 포함')    # ValueError 발생

🔬 알고리즘 비교

기능 정확 일치 (Set) 부분 문자열 (Aho-Corasick)
시간 복잡도 O(1) O(n + m)
공간 복잡도 O(k) O(k * avg_len)
사용 케이스 "나쁜말" 전체 검출 "나쁜" → "나쁜말", "나쁜것" 검출
우회 방지 약함 강함
  • k: 금칙어 개수
  • n: 텍스트 길이
  • m: 매칭 결과 수
  • avg_len: 평균 금칙어 길이

📊 성능

# 벤치마크 (10,000개 금칙어, 1,000자 텍스트)
init():                   ~40ms  (최초 1회만)
is_forbidden():           ~0.001ms (Set lookup)
find_forbidden_words():   ~0.4ms  (텍스트 분리 + Set lookup)
find_forbidden_substrings(): ~1.5ms (Aho-Corasick)

🧪 테스트

# 전체 테스트 실행
pytest tests/ -v

# 커버리지 확인
pytest tests/ --cov=sg_word_filter --cov-report=html

# 특정 테스트만 실행
pytest tests/test_filter.py::TestInit -v

💻 개발 환경 설정

# Repository 클론
git clone https://github.com/yourorg/sg-word-filter-python.git
cd sg-word-filter-python

# 가상환경 생성 및 활성화
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 개발 모드로 설치
pip install -e ".[dev]"

# 테스트 실행
pytest tests/ -v

📝 변경 로그

CHANGELOG.md 참조

🤝 기여

이슈 제기 및 풀 리퀘스트를 환영합니다!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📄 라이센스

MIT License - LICENSE 파일 참조

👥 관련 프로젝트

📞 지원


Made with ❤️ by Your Organization

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sg_word_filter_python-1.0.1.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sg_word_filter_python-1.0.1-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file sg_word_filter_python-1.0.1.tar.gz.

File metadata

  • Download URL: sg_word_filter_python-1.0.1.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.10

File hashes

Hashes for sg_word_filter_python-1.0.1.tar.gz
Algorithm Hash digest
SHA256 402003c239532ed95d04f8309f6549f7ccb13c99b978da9b4b17a93adeb874fa
MD5 3831cb70cee16ca2201c96489f89dfc9
BLAKE2b-256 3fa61997c20cfd8e0048b1e1c7dd236f18252c945e0a9c245009e13426296cd7

See more details on using hashes here.

File details

Details for the file sg_word_filter_python-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for sg_word_filter_python-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ae72bcb8eff629c5af057b922eb378f26c2c65f237228274553fb1c8ccd0b76b
MD5 a7fc18a5458148cb2f6e1ea18930dce7
BLAKE2b-256 d9acb8ba5ea50500945da0a16d42a30184f2a373ab12ba596c2b1b5cbbae202e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page