@ccr_team/sg-word-filter-node
v1.0.2
Published
금칙어 필터링 모듈 - 데이터 주입 방식
Maintainers
Readme
sg-word-filter-node
고성능 금칙어 필터링 모듈 (Node.js) - 정확 일치 및 부분 문자열 검색 지원
✨ 특징
- ⚡ 빠른 검색: Set 기반 O(1) 정확 일치 + Aho-Corasick 부분 문자열 검색
- 🔒 싱글톤 패턴: 앱 라이프사이클 동안 단 한 번만 초기화
- 🛡️ 타입 안전: 완전한 에러 처리 및 검증
- 🌐 다국어 지원: UTF-8 완벽 지원 (한글, 영어, 일본어 등)
- 📦 경량: 핵심 기능만 포함 (압축 크기 ~6KB)
- 🧪 완벽한 테스트: 100% 코드 커버리지
📦 설치
npm (공개 레지스트리)
npm install sg-word-filter-nodeGitHub Packages (인하우스)
.npmrc파일 설정:
@yourorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PAT- 설치:
npm install @yourorg/sg-word-filter-node자세한 배포 가이드는 DEPLOY.md를 참조하세요.
🚀 빠른 시작
1. 금칙어 파일 준비 (config/blocklist.json)
["나쁜말", "욕설", "비속어", "금지어"]2. 모듈 초기화 및 사용
const sgWordFilter = require('sg-word-filter-node');
const path = require('path');
// 애플리케이션 시작 시 초기화 (단 한 번만!)
const blocklistPath = path.join(__dirname, 'config', 'blocklist.json');
sgWordFilter.init(blocklistPath);
// ===== 정확 일치 검색 =====
console.log(sgWordFilter.isForbidden('나쁜말')); // true
console.log(sgWordFilter.isForbidden('정상단어')); // false
// 텍스트에서 금칙어 찾기
const words = sgWordFilter.findForbiddenWords('이것은 나쁜말 과 욕설 입니다');
console.log(words); // ['나쁜말', '욕설']
// 금칙어 포함 여부만 확인
const hasBad = sgWordFilter.hasForbiddenWords('이것은 나쁜말입니다');
console.log(hasBad); // true
// ===== 부분 문자열 검색 (Aho-Corasick) =====
const results = sgWordFilter.findForbiddenSubstrings('나쁜말과욕설입니다');
console.log(results);
// [
// { word: '나쁜말', index: 0 },
// { word: '욕설', index: 5 }
// ]
// 부분 문자열 포함 여부
const hasSubstring = sgWordFilter.hasForbiddenSubstrings('나쁜것');
console.log(hasSubstring); // true (금칙어 목록에 "나쁜"이 있다면)📖 API 레퍼런스
초기화 및 상태 관리
init(filePath, options)
금칙어 목록을 파일에서 로드하여 초기화합니다. 앱 라이프사이클 중 단 한 번만 호출되어야 합니다.
sgWordFilter.init('./config/blocklist.json', { silent: true });Parameters:
filePath(string): JSON 배열 형태의 금칙어 파일 경로options(object, optional):silent(boolean): true일 경우 콘솔 로그 출력 억제 (기본값: false)
Throws:
FilterError: 파일을 찾을 수 없거나, JSON 형식이 잘못되었거나, 배열이 아닌 경우
getStatus()
모듈의 현재 상태 정보를 반환합니다.
const status = sgWordFilter.getStatus();
console.log(status);
// {
// initialized: true,
// count: 10,
// filePath: '/absolute/path/to/blocklist.json',
// loadedAt: '2025-11-18T10:30:00.000Z'
// }Returns: { initialized, count, filePath, loadedAt }
reset()
모듈을 초기화 이전 상태로 되돌립니다. 주로 테스트 용도로 사용됩니다.
sgWordFilter.reset();정확 일치 검색
isForbidden(word)
단어가 금칙어 목록에 정확히 일치하는지 확인합니다.
sgWordFilter.isForbidden('나쁜말'); // true
sgWordFilter.isForbidden('나쁜'); // false (부분 일치는 false)Parameters:
word(string): 확인할 단어
Returns: boolean
findForbiddenWords(text, options)
텍스트를 단어 단위로 분리하여 금칙어를 찾습니다.
const words = sgWordFilter.findForbiddenWords('나쁜말 과 욕설');
// ['나쁜말', '욕설']
// 사용자 정의 구분자
const words2 = sgWordFilter.findForbiddenWords('나쁜말,욕설,정상', { delimiter: ',' });
// ['나쁜말', '욕설']Parameters:
text(string): 검색할 텍스트options(object, optional):delimiter(string|RegExp): 단어 구분자 (기본값:/\s+/)
Returns: string[] - 발견된 금칙어 배열
hasForbiddenWords(text, options)
텍스트에 금칙어가 포함되어 있는지 확인합니다.
sgWordFilter.hasForbiddenWords('이것은 나쁜말입니다'); // trueParameters:
text(string): 검색할 텍스트options(object, optional):findForbiddenWords()와 동일
Returns: boolean
부분 문자열 검색 (Aho-Corasick)
findForbiddenSubstrings(text, options)
Aho-Corasick 알고리즘을 사용하여 텍스트에서 금칙어를 부분 문자열로 검색합니다.
// 금칙어 목록: ['나쁜', '욕']
const results = sgWordFilter.findForbiddenSubstrings('나쁜말과 욕설입니다');
// [
// { word: '나쁜', index: 0 },
// { word: '욕', index: 6 }
// ]
// 대소문자 구분
const results2 = sgWordFilter.findForbiddenSubstrings('Bad WORST', { caseSensitive: true });Parameters:
text(string): 검색할 텍스트options(object, optional):caseSensitive(boolean): 대소문자 구분 여부 (기본값: false)whitelist(string[]): 예외 처리할 정상 단어 목록 (기본값: DEFAULT_WHITELIST)useWhitelist(boolean): 화이트리스트 사용 여부 (기본값: true)
Returns: Array<{word: string, index: number}> - 발견된 금칙어와 위치 배열
화이트리스트 기능: 금칙어가 정상 단어(예: "홈페이지", "결제") 내부에 포함된 경우 오탐을 방지합니다.
// 금칙어 목록: ['페이', '결재']
// 기본 화이트리스트: ['홈페이지', '웹페이지', '결제', '페이스북', ...]
// 화이트리스트 적용 (기본)
sgWordFilter.findForbiddenSubstrings('홈페이지 방문');
// [] - "페이"가 "홈페이지" 내부에 있으므로 제외됨
// 화이트리스트 비활성화
sgWordFilter.findForbiddenSubstrings('홈페이지 방문', { useWhitelist: false });
// [{ word: '페이', index: 2 }] - 모든 패턴 감지
// 커스텀 화이트리스트
sgWordFilter.findForbiddenSubstrings('내페이지', {
whitelist: ['내페이지']
});
// [] - 커스텀 화이트리스트 적용시간 복잡도: O(n + m) - n: 텍스트 길이, m: 매칭 결과 수
hasForbiddenSubstrings(text, options)
텍스트에 금칙어 부분 문자열이 포함되어 있는지 확인합니다.
// 금칙어 목록: ['나쁜']
sgWordFilter.hasForbiddenSubstrings('나쁜말'); // true
sgWordFilter.hasForbiddenSubstrings('좋은말'); // falseParameters:
text(string): 검색할 텍스트options(object, optional):findForbiddenSubstrings()와 동일
Returns: boolean
에러 클래스
FilterError
모듈에서 발생하는 모든 에러는 FilterError 클래스를 사용합니다.
try {
sgWordFilter.init('/invalid/path.json');
} catch (error) {
if (error instanceof sgWordFilter.FilterError) {
console.error(`에러 코드: ${error.code}`);
console.error(`메시지: ${error.message}`);
}
}에러 코드:
FILE_NOT_FOUND: 파일을 찾을 수 없음INVALID_JSON: JSON 파싱 실패INVALID_FORMAT: 배열 형태가 아님NOT_INITIALIZED: 초기화되지 않은 상태에서 함수 호출INVALID_INPUT: 잘못된 입력 타입UNEXPECTED_ERROR: 예상치 못한 오류
🎯 사용 사례
Express.js 미들웨어
const express = require('express');
const sgWordFilter = require('sg-word-filter-node');
const app = express();
app.use(express.json());
// 앱 시작 시 초기화
sgWordFilter.init('./config/blocklist.json');
// 미들웨어로 사용
app.use((req, res, next) => {
if (req.body.comment) {
const badWords = sgWordFilter.findForbiddenWords(req.body.comment);
if (badWords.length > 0) {
return res.status(400).json({
error: '금칙어가 포함되어 있습니다',
words: badWords
});
}
}
next();
});
app.post('/comment', (req, res) => {
res.json({ success: true });
});
app.listen(3000);채팅 시스템
const sgWordFilter = require('sg-word-filter-node');
class ChatSystem {
constructor() {
sgWordFilter.init('./config/blocklist.json', { silent: true });
}
sendMessage(userId, message) {
// 부분 문자열 검색으로 우회 시도 방지
const violations = sgWordFilter.findForbiddenSubstrings(message);
if (violations.length > 0) {
return {
success: false,
error: '메시지에 부적절한 내용이 포함되어 있습니다',
violations: violations
};
}
// 메시지 전송 로직
return { success: true };
}
}🔬 알고리즘 비교
| 기능 | 정확 일치 (Set) | 부분 문자열 (Aho-Corasick) | |------|----------------|---------------------------| | 시간 복잡도 | O(1) | O(n + m) | | 공간 복잡도 | O(k) | O(k * avg_len) | | 사용 케이스 | "나쁜말" 전체 검출 | "나쁜" → "나쁜말", "나쁜것" 검출 | | 우회 방지 | 약함 | 강함 |
- k: 금칙어 개수
- n: 텍스트 길이
- m: 매칭 결과 수
- avg_len: 평균 금칙어 길이
📊 성능
// 벤치마크 (10,000개 금칙어, 1,000자 텍스트)
init(): ~50ms (최초 1회만)
isForbidden(): ~0.001ms (Set lookup)
findForbiddenWords(): ~0.5ms (텍스트 분리 + Set lookup)
findForbiddenSubstrings(): ~2ms (Aho-Corasick)🧪 테스트
# 전체 테스트 실행
npm test
# 커버리지 확인
npm run test:coverage
# Watch 모드
npm run test:watch📝 변경 로그
CHANGELOG.md 참조
🤝 기여
이슈 제기 및 풀 리퀘스트를 환영합니다!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
📄 라이센스
MIT License - LICENSE 파일 참조
👥 관련 프로젝트
- sg-word-filter-python - Python 버전
📞 지원
- 🐛 버그 리포트: Issues
- 💬 질문 및 토론: Discussions
- 📧 이메일: [email protected]
Made with ❤️ by Your Organization
