레거시 DB 인벤토리 — pms (테스트 서버)
조사일: 2026-05-18 대상:
db.malgn.co.kr:3306/pms(테스트 서버, 읽기 권한 계정) 목적: AI 챗봇 학습 자산화 가능성 평가 및 인제스트 매핑 정의
자격 증명은 본 문서에 기록하지 않는다. 운영 시크릿 채널로 관리.
1. 결론 요약
- 이 DB는 CS 전용 DB가 아니라 맑은소프트의 사내 PMS(프로젝트 관리 시스템).
- 그러나
tb_post+tb_post_comment에 고객 문의와 맑은소프트 직원의 답변이 함께 누적되어 있어 CS Q&A 자산으로 가치가 있다. - 합리적 필터를 적용하면 약 1,358건의 Q&A 후보가 확보된다 (Site 1: 1,146, Site 2: 212).
- HTML 본문·테스트 글·이미지 전용 글 등 정제가 필요. 정제 후 OpenSearch 인덱싱 대상으로 활용 가능.
2. DB 메타
| 항목 | 값 |
|---|---|
| Engine | MySQL 8.0.42 |
| Charset | utf8mb3 |
| Tables | 26개 |
| 성격 | PMS(게시판·프로젝트·채팅·일정·투표 통합) |
3. 테이블 인벤토리 (행수 기준 상위)
| 테이블 | 행수 | 용도 추정 | CS AI 활용성 |
|---|---|---|---|
tb_post_read | 364,140 | 게시글 읽음 처리 | ✕ 사용 안 함 |
tb_post_file | 79,180 | 게시글 첨부파일 메타 | ◎ 텍스트 문서는 본문 부착, 이미지/동영상은 별도 정책 (§6 첨부파일 처리 전략) |
tb_post_share | 68,380 | 게시글 공유 이력 | ✕ |
tb_project_user | 22,292 | 프로젝트-사용자 매핑 | ✕ |
tb_diligence | 7,137 | 근태/근면 데이터 | ✕ |
tb_post_comment | 5,684 | 게시글 댓글 (답변 본문) | ◎ 핵심 자산 |
tb_project | 4,448 | 프로젝트 메타 | △ 컨텍스트 (project_id 매핑) |
tb_post_label | 3,778 | 게시글 라벨(태그) | △ 카테고리 후보 |
tb_user | 3,037 | 사용자 | △ 작성자 메타용 |
tb_post | 2,437 | 게시글 본문 (질문/요청) | ◎ 핵심 자산 |
tb_post_bookmark | 2,046 | 즐겨찾기 | ✕ |
tb_chat_message | 1,814 | 사내 실시간 채팅 | ✕ CS와 무관, 짧은 잡담 위주 |
tb_post_task | 1,464 | 게시글 → 작업 변환 | ✕ |
tb_session | 686 | 로그인 세션 | ✕ |
tb_user_log | 457 | 사용자 활동 로그 | ✕ |
tb_post_alram | 187 | 게시글 알림 | ✕ |
tb_post_template | 90 | 문서 템플릿 (회의록/보고서 등) | △ 양식이라 답변 데이터는 아님 |
tb_post_poll_user | 90 | 투표 응답 | ✕ |
tb_chat_file | 81 | 채팅 첨부 | ✕ |
tb_site | 63 | 사이트(테넌트) 정보 | ○ 필터 키 |
tb_post_poll | 33 | 게시글 투표 | ✕ |
tb_sequence | 22 | 시퀀스 카운터 | ✕ |
tb_group_user, tb_group | 8/6 | 그룹 | ✕ |
tb_post_confirm | 0 | (미사용) | ✕ |
tb_chat_message샘플: "네~^^", "감사합니다ㅎㅎ" 등 사내 메신저 대화 — CS 자산으로 활용 부적합.
4. 핵심 테이블 스키마 (관련 컬럼만)
tb_post (질문/요청 본문)
| 컬럼 | 타입 | 의미 / 활용 |
|---|---|---|
id | int | 게시글 PK |
site_id | int | 테넌트(사이트) — 필터 키 |
project_id | int | 프로젝트 컨텍스트 |
type, ctype | int | 게시글 구분 — 필터 후보 |
template_id | int | 사용된 템플릿 |
label_id, label | int/varchar | 카테고리 후보 |
writer | varchar | 작성자(질문자) |
subject | varchar(255) | 제목 = 질문 요약 |
content | longtext | 본문 = 질문 상세 (HTML) |
is_notice | int | 공지 여부 — 제외 |
is_task/is_schedule/is_poll | int | 작업/일정/투표 — 제외 |
comm_cnt | int | 댓글 수 — Q&A 페어 여부 |
reg_date | varchar(14) | YYYYMMDDHHMMSS — 신선도 |
status | int | 1 = 정상 |
tb_post_comment (답변 본문)
| 컬럼 | 타입 | 의미 / 활용 |
|---|---|---|
id | int | 댓글 PK |
post_id | int | 부모 게시글 FK |
writer | varchar | 작성자(답변자) |
content | longtext | 답변 본문 (HTML) |
private_yn | char(1) | 'Y' 비공개 댓글 — 제외 |
reg_date | varchar(14) | 등록일 |
status | int | 1 = 정상 |
subject | varchar | 보통 비어 있음 |
tb_site, tb_project, tb_user, tb_post_label
- 메타데이터 결합용 — 작성자명·사이트명·프로젝트명·라벨명을 인덱스 메타 필드로 첨부.
5. Q&A 후보 데이터 규모
적용 필터
WHERE p.status = 1
AND p.comm_cnt >= 1
AND p.is_task = 0
AND p.is_schedule = 0
AND p.is_poll = 0
AND p.is_notice = 0
AND p.subject NOT LIKE '테스트%'
AND CHAR_LENGTH(p.content) >= 20
결과
| Site | Site 이름 | 후보 게시글 |
|---|---|---|
| 1 | (주)맑은소프트 | 1,146 |
| 2 | 맑은소프트 내부 | 212 |
| — | 합계 | 1,358 |
Site 1이 외부 고객 문의 + 내부 글이 섞여 있을 가능성 있음. 표본 검수 후 site별 채택 여부 재결정 필요.
6. 데이터 품질 이슈
| 이슈 | 처리 방안 |
|---|---|
본문이 HTML (<p>...) | 인덱싱 전 HTML 정제 (태그 제거, 엔티티 디코드, <img> 제거 후 alt만 보존) |
| 일부 글은 이미지만 있음 (스크린샷 캡처) | 아래 이미지 처리 전략 참조 — 단계별 도입 |
<img src="/data/..."> 내부 경로 | 메타 필드로 보존 후 외부 접근 가능한 URL로 치환 (R2 서명 URL 또는 프록시) |
테스트 글(테스트%)이 많음 | 제목·작성자·날짜 기반 필터, 화이트리스트 검토 |
private_yn = 'Y' 비공개 댓글 | 반드시 제외 |
reg_date가 varchar(14) (YYYYMMDDHHMMSS) | 인덱싱 시 DATETIME/epoch 변환, 신선도 가중치 적용 |
| 동일 질문에 여러 댓글 | 모든 댓글을 후보로 보되 마지막 답변 또는 staff 답변 우선 등 정책 결정 필요 |
| 작성자가 동일한 Q&A (자기 답변) | 셀프 답변은 신뢰도 낮춤 또는 제외 |
| PII 가능성 (이메일·전화·이름) | 인제스트 단계에서 마스킹 또는 사내용으로만 사용. 공개 챗봇에 그대로 노출 금지 |
| 사용자 작성 IP 컬럼 보유 | 인덱싱 대상에서 제외 |
| Site별로 도메인이 다를 수 있음 | 사이트 1(맑은소프트 운영)만 우선 채택 후 확장 |
| 댓글이 1건 ↔ 다건(스레드)으로 섞임 | 아래 스레드 처리 전략 참조 |
비공개 댓글(private_yn = 'Y')이 9.3% 존재 | 아래 비공개 댓글 처리 전략 참조 — 단순 제외 아님 |
| 첨부파일이 Q&A 후보 64.9%에 존재 (총 78,784건) | 아래 첨부파일 처리 전략 참조 — 파일 종류별 분기 |
이미지 처리 전략
본 DB의 게시글 본문은 <p> 텍스트와 <img src="/data/..."> 이미지가 함께 들어 있다. 가치 있는 Q&A는 보통 텍스트 본문이 충분하고, 이미지만 있는 글은 테스트·사내 스크린샷이 대부분이라 단계별 도입으로 비용과 복잡도를 통제한다.
| 단계 | 처리 방식 | 비용 | 적용 시점 |
|---|---|---|---|
| A. 텍스트만 (MVP) | HTML·<img> 태그 제거 후 본문 길이가 임계값(예: 30자) 미만이면 인덱싱 제외. 이미지 URL은 메타 필드로만 보존 | 0 | Phase 1 즉시 |
| B. OCR 추가 | 텍스트와 이미지가 함께 있는 글에서 스크린샷의 텍스트(에러 메시지·관리자 화면 등)만 추출해 본문 뒤에 부착 | 낮음 (Cloudflare AI / 외부 OCR) | Phase 1 후반 ~ Phase 2 |
| C. Vision LLM 캡션 | Claude Vision으로 이미지 내용 요약 캡션 생성 → 의미 검색 강화 | 중간 (토큰 비용) | Phase 2 품질 고도화 |
공통 원칙
- 모든 단계에서 이미지 URL을 메타 필드로 보존 → 챗봇 답변에서 "관련 화면 참고: <링크>"로 인용 가능.
- 이미지 경로(
/data/...)는 외부 접근 가능한 URL로 치환 (R2 서명 URL 또는 프록시). - 단계 전환은 미커버 질문 분석 결과로 결정. 이미지 정보 없이 풀리지 않는 질문이 의미 있게 쌓이면 B 도입.
트레이드오프: 텍스트만은 단순·저비용이지만 "이 화면에서 어떻게 해요?" 같은 시각 의존 질문을 놓침. OCR/Vision은 잡을 수 있지만 복잡도·비용 증가.
스레드 처리 전략
게시글당 댓글 수 분포 (status=1 + 테스트 제외 후, 1,358건 후보 기준):
| 댓글 수 | 게시글 수 | 비율 |
|---|---|---|
| 1 (단일 응답) | 487 | 36% |
| 2~3 (짧은 스레드) | 486 | 36% |
| 4~6 (중간 스레드) | 238 | 18% |
| 7~15 (긴 스레드) | 129 | 9% |
| 16+ (매우 긴) | 18 | 1% |
약 64%가 스레드형 — 단일 Q&A 페어로 가정하면 안 된다. 후속 질의·재확인·해결 과정을 모두 활용해야 답변 품질이 살아난다.
원칙: 스레드를 하나의 Q&A 도큐먼트로 압축해서 인덱싱한다.
| 항목 | 처리 |
|---|---|
| 단위 | 1개 게시글(+ 그 게시글의 모든 가시 댓글) = 1 도큐먼트 |
| 본문 구성 | 질문(Q) → 답변1(A) → 추가질문(Q2) → 답변2(A2) → ... 시간순 결합 |
| 화자 표기 | 각 턴 앞에 작성자/역할 라벨 (고객:, 상담사: 등) 부착 — LLM이 화자 추적 가능하게 |
| 잡담 필터 | 본문 길이 5자 미만, "ddddd" 같은 반복 문자 등 노이즈 댓글 제거 (post 94221 사례 참조) |
| 임베딩 입력 | 결합된 전체 텍스트 (Q+A 누적) |
| BM25 인덱스 | 제목·질문·답변 분리 필드 + 결합 필드 둘 다 보유해 가중치 조절 가능 |
| 청킹 | 결합 본문이 임계값(예: 2000자) 초과 시 의미 단위 분할, 단 동일 doc_id로 묶음 |
| 메타 | turn_count, last_reply_at 보존 → 신선도·복잡도 가중치 |
비잡담 검증: 댓글 노이즈("ddddd", "xxxxxx") 검출 룰 — 동일 문자 반복, 길이 5자 미만, 의미 없는 단일 단어 등.
비공개 댓글 처리 전략
tb_post_comment.private_yn = 'Y' 댓글은 활성 댓글 5,021건 중 513건 (약 9.3%).
관찰: 일부 스레드는 알맹이 답변이 비공개에만 존재한다 (예: post 94221 — 공개 댓글은 잡담, 비공개 댓글이 실제 답변). 따라서 단순 제외하면 답변 자산을 잃는다. 그러나 비공개는 의도적으로 고객 비노출이므로 챗봇 노출은 금지.
해결: 가시성(visibility) 메타 필드로 계층 인덱싱.
| 인덱스 도큐먼트의 visibility | 포함 댓글 |
|---|---|
internal | 공개 + 비공개 댓글 모두 결합 |
public | 공개 댓글만 결합 |
같은 게시글에 대해 두 개 변형을 만들지 않고, 하나의 도큐먼트에 두 본문 필드를 유지하는 방식도 가능:
| 인덱스 필드 | 내용 |
|---|---|
body_public | 공개 댓글만으로 구성한 결합 본문 (Phase 2 챗봇용 BM25/임베딩) |
body_internal | 비공개 포함 결합 본문 (Phase 1 상담사 보조용 BM25/임베딩) |
embedding_public / embedding_internal | 각 본문 기준 벡터 |
has_internal_only_answer | 공개 본문에는 답이 없고 비공개에만 있는 경우 표시 (Phase 2에서 자동 에스컬레이션 후보) |
Phase별 사용:
| Phase | 검색 대상 |
|---|---|
| Phase 1 (상담사 보조) | body_internal / embedding_internal — 내부 지식 활용 정당 |
| Phase 2 (고객 챗봇) | body_public / embedding_public — 공개 자산만 사용. has_internal_only_answer = true인 케이스는 "확인 후 답변 드리겠습니다" 또는 에스컬레이션 |
금지: 비공개 댓글 본문이 Phase 2 챗봇 응답에 직접 인용되거나 출처로 노출되어선 안 됨.
첨부파일 처리 전략
tb_post_file은 게시글/댓글에 첨부된 파일 메타. 활성 78,784건이 존재하고 Q&A 후보 1,358건 중 881건(64.9%)이 첨부 보유 — 무시 시 답변 자산의 절반 손실.
스키마 핵심: module(post/editor) + module_id(게시글 id) + realname(원본 파일명) + filetype(file/image/movie) + filesize.
파일 종류 분포 (확장자 상위):
| 분류 | 확장자 | 건수 | 평균/최대 크기 |
|---|---|---|---|
| 이미지 | png, jpg, gif, jpeg, bmp | 38,063 | 평균 ~수백KB |
| 텍스트 문서 | pdf(11k), pptx(7k), hwp(5k), xlsx(5k), docx(2k), ppt(1.4k), doc(0.7k), txt(0.3k), csv(0.2k) | 34,125 | pdf 평균 2.3MB, pptx 평균 6.7MB / 최대 530MB |
| 압축 | zip | 3,374 | 평균 35MB / 최대 1.1GB |
| 동영상 | mp4, mov, swf | 566 | mp4 평균 113MB / 최대 1.1GB |
| 디자인 | psd, ai | 1,294 | 평균 수MB ~ 수십MB |
| 기타 | exe, sql, r, ttf 등 | 소량 | — |
한국어 환경 특이점: HWP(한컴) 5,094건 — 별도 추출기(
hwp.js등) 필요.
종류별 처리 정책
| 파일 종류 | Phase 1 MVP | Phase 1 후반 | Phase 2 |
|---|---|---|---|
인라인 이미지 (module=editor) | §6 이미지 처리 전략 Stage A | Stage B (OCR) | Stage C (Vision LLM) |
| 이미지 첨부 (png/jpg/gif/jpeg/bmp) | 동일 — 메타만 보존 | OCR 텍스트 본문 부착 | Vision 캡션 |
| 텍스트 문서 (pdf/docx/hwp/txt/csv/xlsx) | 추출 텍스트를 본문에 부착 → 검색 대상 | 청킹·구조 보존 개선 | — |
| 슬라이드 (pptx/ppt) | 텍스트 추출 (크기 상한 50MB) | 슬라이드 단위 청킹 | — |
| 압축파일 (zip) | 미추출, 메타만 | 사례 검토 후 선택 도입 | Queue로 처리 검토 |
| 동영상 (mp4/mov/swf) | 미추출, 메타만 | — | Queue + 트랜스크립트 (Whisper 등) |
| 디자인/실행 (psd/ai/exe) | 파일명만 메타 | — | — |
공통 원칙
- 원본은 R2에 보관. DB의
realname/filename을 R2 키로 매핑. 답변 인용 시 R2 서명 URL 발급. - 가시성 상속: 댓글에 첨부된 파일은 그 댓글의
private_yn값을 상속.- 공개 댓글 첨부 → 텍스트는
body_public·body_internal모두에 부착 - 비공개 댓글 첨부 →
body_internal에만 부착, Phase 2 챗봇에서 노출 금지 - 게시글 본문 첨부 → 공개 취급
- 공개 댓글 첨부 → 텍스트는
- 크기 상한: Phase 1에서는 50MB 초과 파일은 Queue 도입 전까지 보류. 메타만 인덱스에 보존.
- 추출 실패 허용: 손상된 파일·미지원 포맷은 메타만 남기고
extraction_status: failed. 인덱싱 중단 사유 아님. - PII: 추출 텍스트에도 PII 검출·마스킹 적용 (이메일·전화·주민번호).
- 챗봇 인용: 답변에 "참고: filename.pdf" 형식으로 출처 첨부 명시.
라이브러리 후보 (선정 보류)
| 포맷 | 후보 |
|---|---|
pdf-parse, pdfjs-dist | |
| DOCX | mammoth, docx |
| HWP | hwp.js (한국 환경 필수) |
| XLSX/XLS | xlsx (SheetJS) |
| PPTX | 자체 unzip + slide XML 파싱 또는 외부 변환 서비스 |
| TXT/CSV | 직접 읽기 |
Cloudflare Workers의 CPU/메모리 제한으로 동기 추출은 일부 포맷·소형 파일만 가능. 대용량/복잡 추출은 별도 인덱서(로컬 백필 스크립트 또는 Phase 2 Queue Worker)에서 처리.
트레이드오프
- 텍스트 문서 추출만 도입해도 답변 자산이 크게 늘어남(881건의 첨부 중 상당수가 매뉴얼·로그·샘플).
- 그러나 추출 라이브러리·실패 케이스·대용량 처리 인프라 비용 증가.
- 동영상 트랜스크립트는 즉시 도입 시 비용이 가장 크므로 Phase 2 Queue 도입과 정합.
7. 권장 인제스트 매핑 (초안)
OpenSearch chunks 인덱스 한 도큐먼트당:
| 인덱스 필드 | 출처 | 비고 |
|---|---|---|
doc_id | legacy:post:{tb_post.id} | 안정적 식별자 |
source_type | "legacy_pms_qna" | 출처 구분 |
site_id / site_name | tb_post.site_id + tb_site.name | 필터·메타 |
project_id / project_name | tb_post.project_id + tb_project.name | 컨텍스트 |
label | tb_post_label.name | 카테고리 |
title | tb_post.subject | 제목 |
question_text | 정제된 tb_post.content | 질문 본문 |
body_public | 공개 댓글만으로 구성한 스레드 결합 본문 (Q→A→Q2→A2…, 화자 라벨 포함) | Phase 2 검색·임베딩 |
body_internal | 비공개 포함 스레드 결합 본문 | Phase 1 검색·임베딩 |
embedding_public | title + question_text + body_public 기준 벡터 | Phase 2 k-NN |
embedding_internal | title + question_text + body_internal 기준 벡터 | Phase 1 k-NN |
has_internal_only_answer | 공개 본문에는 답이 없고 비공개에만 있는 경우 true | Phase 2 자동 에스컬레이션 트리거 |
image_urls | 본문/댓글에서 추출한 이미지 URL 배열 | 인용용 메타 (외부 접근 URL로 치환) |
attachments_public | 공개 영역(게시글 본문/공개 댓글)의 첨부 배열 — {filename, r2_url, filetype, size, visibility, extraction_status} | Phase 2 인용 가능 |
attachments_internal | 비공개 댓글 첨부까지 포함한 전체 배열 | Phase 1 전용 |
extracted_text_public | 공개 첨부에서 추출한 텍스트(body_public에 부착 전 원본) | 청킹·재처리 용 |
extracted_text_internal | 비공개 포함 첨부 추출 텍스트 | Phase 1 전용 |
turn_count | 게시글 + 가시 댓글 수 | 가중치·신선도 보조 |
asker | tb_post.writer | 메타 |
answerers | 가시 댓글 작성자 배열 | 메타 |
reg_date_at / last_reply_at | tb_post.reg_date / 마지막 댓글 reg_date 변환 | 신선도 |
comment_count | tb_post.comm_cnt | 메타 |
청킹 전략: 스레드를 1 도큐먼트로 압축. 결합 본문(스레드 + 추출 첨부 텍스트)이 2000자 이상이면 의미 단위 분할 (동일 doc_id 유지). 짧은 단일 Q&A는 분할 없이 그대로. 첨부 추출 텍스트는 본문 결합 시 [첨부:{filename}] 헤더 뒤에 부착해 LLM이 출처를 인지 가능하게.
Phase별 쿼리: Phase 1은 body_internal + embedding_internal, Phase 2는 body_public + embedding_public을 검색 대상으로. 색인은 같은 도큐먼트라 운영 동기화 비용 1배수.
8. 표준 답변 후보 식별 전략
- 동일/유사 질문에 같은 답변이 반복 등장 → 표준 답변 후보로 자동 추출.
- 댓글 작성자가 맑은소프트 직원(
tb_user에서 staff 플래그/도메인으로 식별) → 신뢰도 가중치 상향. - 후보 답변은 ROADMAP.md 1.9 피드백 루프의 자동 추천 흐름과 연결.
9. 다음 단계 (실행 항목)
- 표본 검수: site 1의 Q&A 후보 30~50건 추출 → 실제 CS 답변/내부 잡담 비율 확인 → 추가 필터 도출. 스레드형/단일형 분포와 비공개 댓글 영향도 함께 평가.
- HTML 정제 PoC: 5건 정도를 직접 정제해서 토큰 수·내용 유효성 측정.
- 노이즈 댓글 필터 룰 작성: 5자 미만, 동일 문자 반복(
ddddd등), 의미 없는 단일 단어 탐지. - PII 검출 룰 작성: 이메일/전화/주민번호 정규식 + 마스킹 규칙.
- 가시성 정책 합의: Phase 1에서 비공개 댓글 활용 범위·노출 경로(상담사 화면에서만) 명문화. 첨부파일도 동일 정책 상속.
- 첨부 추출기 선정 PoC: PDF/DOCX/HWP/XLSX 추출 라이브러리 후보 평가 (한국어·표·이미지 포함 PDF에서의 정확도, Worker 적합성).
- R2 마이그레이션 계획: 78,784개 첨부의 R2 이관 전략 (전체 백필? 인덱싱 대상만? URL 매핑 테이블 유지?).
- 인제스트 파이프라인 설계: 일회성 백필 + 증분 동기화 (
reg_date기준) 정책. 스레드는 마지막 댓글 갱신 시 재인덱싱. 첨부 추출 실패는 메타만 보존. - 사이트 채택 범위 합의: site 1만? 1+2? 다른 site는 외부 고객사 데이터라 사용 가능 여부 별도 확인 필요.
- 법적/내부 검토: 고객 문의 본문에 포함된 회사명·개인정보 사용 가능 범위, 비공개 댓글의 학습 자산화 가능 여부, 첨부 파일(특히 고객사 자료) 활용 가능 범위 확인.
10. 미사용 결정
다음은 본 DB에서 사용하지 않음으로 결론:
tb_chat_message— 짧은 사내 메신저, CS 자산 아님tb_post_template— 양식/템플릿이라 답변 데이터 부재- 이미지만 있는 게시글 (Stage A 한정) — §6 이미지 처리 전략 참조. Stage B/C 도입 시 재평가
- 노이즈 댓글 (5자 미만, 동일 문자 반복 등) — §6 스레드 처리 전략의 노이즈 필터로 제외
- 압축파일(zip)·동영상·디자인/실행 파일 (Phase 1 MVP 한정) — §6 첨부파일 처리 전략 참조. zip은 검토 후 결정, 동영상은 Phase 2 Queue 도입 시 트랜스크립트 처리
비공개 댓글·첨부는 사용하지 않음이 아님. Phase 1 한정으로 활용. §6 비공개 댓글 / 첨부파일 처리 전략 참조.