
분석 결과 요약
결론부터 말씀드리면: 적용 가능합니다! 🎯 다만 두 가지 방식이 있습니다.
방식 1: 지란 WebFilter 상용 솔루션 도입
- API 연동 방식으로 우리 CMS에 적용 가능
UnifiedWriteController::processWrite()→ 게시글 저장 전 API로 검사-
handleFileUpload() → 파일 업로드 시 API로 첨부파일 검사
- 장점: GS인증, OCR/AI 탐지, 첨부파일 내부 검사, 보고서 자동 생성
- 단점: 라이선스 + 구축 비용 발생 (별도 견적 필요)
방식 2: 자체 개인정보 필터링 구현 (비용 절감)
- 정규표현식 기반으로 주민번호, 전화번호, 이메일, 카드번호 등 자동 탐지
- 우리 CMS의
UnifiedWriteController에 바로 연동 가능
- 약 7~10일 정도면 기본 기능 구현 가능
- 장점: 비용 없음, 우리 시스템에 맞춤 개발
- 단점: 이미지 OCR, AI 유해정보 탐지 등은 불가
추천
| 상황 | 추천 |
|---|---|
| 공공기관/법적 규제 대상 | 지란 WebFilter (인증 필수) |
| 일반 기업/소규모 사이트 | 자체 구현 (비용 절감) |
댓글 1개
## 1. 지란지교 WebFilter란?
지란지교데이터의 **WEB필터**는 웹 게시판에 올라오는 **개인정보 및 유해정보를 자동으로 탐지·차단**하는 상용 솔루션입니다.
### 주요 기능
| 기능 | 설명 |
|------|------|
| **게시물 필터링** | 본문, 첨부파일 속 개인정보(주민번호, 전화번호 등) 탐지 및 등록 차단 |
| **유해정보 차단** | 욕설, 음란, 도박, 광고 키워드 탐지 |
| **첨부파일 검사** | 문서/압축/이미지 파일 내 개인정보 검사 |
| **IP 차단** | VPN/Proxy IP 및 관리자 지정 IP 차단 |
| **AI 이미지 검사** | OCR 기반 이미지 속 개인정보 검출 |
| **필터링 정책** | 게시판별, 도메인별 세밀한 정책 설정 |
| **운영 관리** | 실시간 모니터링, 통계, 보고서 |
### 연동 방식
- **API 방식** (구축형): 서비스 부하 없이 안정적 필터링
- **인라인 방식** (구축형): 네트워크 패킷 분석을 통한 차단
---
## 2. 우리 CMS 구조 분석
우리 CMS는 **PHP 기반 자체 개발 솔루션**으로, 다음과 같은 구조를 가지고 있습니다:
```
bluesnow.hp10.kr/
├── controllers/
│ ├── PostController.php ← 게시물 조회/삭제
│ ├── UnifiedWriteController.php ← 📌 게시물 작성/수정 (핵심 포인트)
│ ├── BoardController.php ← 게시판 관리
│ └── ...
├── models/
│ ├── PostModel.php ← 게시물 DB 처리
│ └── ...
├── ajax/ ← AJAX 처리
├── includes/ ← 공통 모듈
├── templates/ ← 템플릿
└── uploads/ ← 업로드 파일
```
### 게시글 처리 흐름
```mermaid
flowchart LR
A[사용자 글쓰기] --> B[UnifiedWriteController::processWrite]
B --> C[validatePostData - 입력 검증]
C --> D[PostModel::createPost/updatePost - DB 저장]
D --> E[handleFileUpload - 파일 업로드]
E --> F[게시글 저장 완료]
```
---
## 3. 적용 가능성 분석
### ✅ 결론: **적용 가능하지만, 방식에 따라 다릅니다**
### 방식 A: 지란 WebFilter 상용 솔루션 도입
> [!IMPORTANT]
> 지란 WebFilter는 **상용 솔루션**으로, 별도 **라이선스 비용 + 구축 비용**이 발생합니다.
> GS인증 1등급, 조달청 등록 제품으로 **공공기관 도입에 적합**합니다.
| 항목 | 내용 |
|------|------|
| **연동 방식** | API 방식 또는 인라인 방식 |
| **적용 난이도** | API 방식: 중간 / 인라인 방식: 높음 |
| **비용** | 라이선스 + 구축 + 유지보수 비용 (별도 문의 필요) |
| **적합 대상** | 공공기관, 대기업, 금융/의료 등 법적 규제 대상 조직 |
**API 연동 시 수정 포인트:**
- `UnifiedWriteController::processWrite()` → 게시글 저장 전 API 호출
- [handleFileUpload()](file:///c:/Users/webja/Desktop/bluesnow.hp10.kr/controllers/UnifiedWriteController.php#1096-1102) → 파일 업로드 전 API로 파일 검사
- 관리자 페이지에 필터링 정책 관리 UI 추가
---
### 방식 B: 자체 개인정보 필터링 기능 구현 (비용 절감)
> [!TIP]
> 기본적인 개인정보 필터링은 **정규표현식 기반으로 자체 구현**이 가능합니다.
> 상용 솔루션 수준은 아니지만, 일반적인 개인정보 유출 방지에 효과적입니다.
#### 구현 가능한 기능
| 기능 | 난이도 | 설명 |
|------|--------|------|
| 📌 **주민등록번호 탐지** | ⭐ 쉬움 | 정규식 패턴 매칭 |
| 📌 **전화번호 탐지** | ⭐ 쉬움 | 정규식 패턴 매칭 |
| 📌 **이메일 탐지** | ⭐ 쉬움 | 정규식 패턴 매칭 |
| 📌 **카드번호 탐지** | ⭐ 쉬움 | 정규식 + Luhn 알고리즘 검증 |
| 📌 **계좌번호 탐지** | ⭐⭐ 보통 | 은행별 패턴 매칭 |
| 📌 **여권번호 탐지** | ⭐ 쉬움 | 정규식 패턴 매칭 |
| 📌 **운전면허번호 탐지** | ⭐ 쉬움 | 정규식 패턴 매칭 |
| 🔧 **욕설/유해어 필터** | ⭐⭐ 보통 | 키워드 DB + 패턴 매칭 |
| 🔧 **첨부파일 텍스트 검사** | ⭐⭐⭐ 어려움 | 문서 파싱 라이브러리 필요 |
| 🔧 **이미지 OCR 검사** | ⭐⭐⭐⭐ 매우 어려움 | Tesseract 등 OCR 엔진 필요 |
#### 예시 구현 코드 (핵심 필터링 클래스)
```php
<?php
/**
* 개인정보 필터링 클래스
* UnifiedWriteController에서 게시글 저장 전 호출
*/
class PrivacyFilter {
private $patterns = [];
private $detectedItems = [];
public function __construct() {
$this->initPatterns();
}
/**
* 개인정보 탐지 패턴 초기화
*/
private function initPatterns() {
$this->patterns = [
'주민등록번호' => '/\b(\d{6})\s*[-]\s*([1-4]\d{6})\b/',
'전화번호' => '/\b(01[016789])\s*[-.)]\s*(\d{3,4})\s*[-.)]\s*(\d{4})\b/',
'이메일' => '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/',
'카드번호' => '/\b(\d{4})\s*[-\s]\s*(\d{4})\s*[-\s]\s*(\d{4})\s*[-\s]\s*(\d{4})\b/',
'계좌번호' => '/\b(\d{3,4})\s*[-]\s*(\d{2,6})\s*[-]\s*(\d{2,6})\s*[-]?\s*(\d{0,3})\b/',
'여권번호' => '/\b[A-Z]{1,2}\d{7,8}\b/',
'운전면허번호' => '/\b\d{2}-\d{2}-\d{6}-\d{2}\b/',
];
}
/**
* 텍스트에서 개인정보 탐지
* @param string $text 검사할 텍스트
* @return array 탐지 결과
*/
public function scan($text) {
$this->detectedItems = [];
// HTML 태그 제거 후 검사
$plainText = strip_tags($text);
foreach ($this->patterns as $type => $pattern) {
if (preg_match_all($pattern, $plainText, $matches)) {
foreach ($matches[0] as $match) {
// 주민등록번호 유효성 추가 검증
if ($type === '주민등록번호' && !$this->validateSSN($match)) {
continue;
}
// 카드번호 Luhn 검증
if ($type === '카드번호' && !$this->validateCreditCard($match)) {
continue;
}
$this->detectedItems[] = [
'type' => $type,
'value' => $this->maskValue($match, $type),
'original' => $match
];
}
}
}
return $this->detectedItems;
}
/**
* 개인정보 포함 여부 확인
*/
public function hasPrivacyData($text) {
return count($this->scan($text)) > 0;
}
/**
* 개인정보 마스킹 처리
*/
public function maskPrivacyData($text) {
foreach ($this->patterns as $type => $pattern) {
$text = preg_replace_callback($pattern, function($matches) use ($type) {
return $this->maskValue($matches[0], $type);
}, $text);
}
return $text;
}
/**
* 값 마스킹
*/
private function maskValue($value, $type) {
switch ($type) {
case '주민등록번호':
return preg_replace('/(\d{6})-(\d)(\d{6})/', '$1-$2******', $value);
case '전화번호':
return preg_replace('/(\d{3})-(\d{3,4})-(\d{4})/', '$1-****-$3', $value);
case '이메일':
return preg_replace('/(.{2})(.*)(@.*)/', '$1***$3', $value);
case '카드번호':
return preg_replace('/(\d{4})-(\d{4})-(\d{4})-(\d{4})/', '$1-****-****-$4', $value);
default:
$len = mb_strlen($value);
$mask = str_repeat('*', max(1, $len - 4));
return mb_substr($value, 0, 2) . $mask . mb_substr($value, -2);
}
}
/**
* 주민등록번호 유효성 검증
*/
private function validateSSN($ssn) {
$ssn = preg_replace('/[^0-9]/', '', $ssn);
if (strlen($ssn) !== 13) return false;
$weights = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5];
$sum = 0;
for ($i = 0; $i < 12; $i++) {
$sum += (int)$ssn[$i] * $weights[$i];
}
$check = (11 - ($sum % 11)) % 10;
return $check == (int)$ssn[12];
}
/**
* 카드번호 Luhn 검증
*/
private function validateCreditCard($number) {
$number = preg_replace('/[^0-9]/', '', $number);
if (strlen($number) < 13 || strlen($number) > 19) return false;
$sum = 0;
$alt = false;
for ($i = strlen($number) - 1; $i >= 0; $i--) {
$n = (int)$number[$i];
if ($alt) {
$n *= 2;
if ($n > 9) $n -= 9;
}
$sum += $n;
$alt = !$alt;
}
return ($sum % 10 === 0);
}
/**
* 탐지 결과 요약
*/
public function getSummary() {
$summary = [];
foreach ($this->detectedItems as $item) {
if (!isset($summary[$item['type']])) {
$summary[$item['type']] = 0;
}
$summary[$item['type']]++;
}
return $summary;
}
}
```
#### 적용 위치 (UnifiedWriteController 수정)
```diff
// processWrite() 메서드 내 - 입력 데이터 검증 후 저장 전
$postData = $this->validatePostData($boardId);
+// ★ 개인정보 필터링 검사
+require_once __DIR__ . '/../includes/PrivacyFilter.php';
+$filter = new PrivacyFilter();
+
+// 제목 + 본문 검사
+$textToCheck = ($postData['title'] ?? '') . ' ' . ($postData['content'] ?? '');
+$detected = $filter->scan($textToCheck);
+
+if (!empty($detected)) {
+ // 정책에 따라: 차단 또는 마스킹 후 저장
+ $mode = 'block'; // 'block' 또는 'mask'
+
+ if ($mode === 'block') {
+ $types = array_unique(array_column($detected, 'type'));
+ throw new ValidationException(
+ '개인정보가 포함되어 있어 등록할 수 없습니다. (' .
+ implode(', ', $types) . ' 발견)'
+ );
+ } else {
+ $postData['content'] = $filter->maskPrivacyData($postData['content']);
+ $postData['title'] = $filter->maskPrivacyData($postData['title']);
+ }
+}
+
if ($isEdit) {
$this->postModel->updatePost($postId, $postData);
```
---
## 4. 추천 방향
| 상황 | 추천 방식 |
|------|-----------|
| 공공기관/법적 의무 대상 | **지란 WebFilter** 도입 (GS인증, 조달등록) |
| 일반 기업/소규모 사이트 | **자체 구현** (비용 절감, 기본 필터링) |
| 중간 규모, 예산 있음 | **자체 구현 + 추후 상용 솔루션 전환** |
> [!WARNING]
> 자체 구현 시 한계:
> - 이미지 속 개인정보 OCR 탐지 불가 (별도 OCR 엔진 필요)
> - 첨부파일(문서) 내부 텍스트 검사는 추가 라이브러리 필요
> - AI 기반 유해정보 탐지 불가
> - 보안 인증/감사 대응 어려움
---
## 5. 자체 구현 시 개발 범위 (예상)
| 단계 | 작업 내용 | 예상 소요 |
|------|-----------|-----------|
| 1단계 | `PrivacyFilter` 클래스 개발 (정규식 기반) | 1-2일 |
| 2단계 | [UnifiedWriteController](file:///c:/Users/webja/Desktop/bluesnow.hp10.kr/controllers/UnifiedWriteController.php#11-2017) 연동 | 0.5일 |
| 3단계 | 관리자 대시보드 (탐지 로그, 통계) | 2-3일 |
| 4단계 | 관리자 정책 설정 UI | 1-2일 |
| 5단계 | 첨부파일 텍스트 추출 및 검사 | 2-3일 |
| **합계** | | **약 7-10일** |
---
## 6. 다음 단계
어떤 방식으로 진행하시겠습니까?
1. **🏢 지란 WebFilter 도입 검토** → 견적 문의 (1600-9185)
2. **🔧 자체 개인정보 필터링 구현** → 바로 개발 시작 가능
3. **📋 두 방식 비교 후 결정** → 추가 자료 준비