CATI 서비스 데이터베이스 설계 문서
목차
1.
2.
3.
1. 설문 관리
surveys
설명: 설문지의 기본 정보를 저장하는 메인 테이블
컬럼명 | 타입 | 설명 |
idx | INT | 기본키 (Auto Increment) |
survey_uuid | VARCHAR(255) | 설문 고유 식별자 |
ver | INT | 설문 버전 |
title | JSON | 설문 제목 (다국어 지원) |
survey_type | VARCHAR(255) | 설문 유형 |
translation | JSON | 번역 데이터 |
logicflow | JSON | 로직 플로우 정의 |
design | JSON | 디자인 설정 |
setting | JSON | 설문 설정 |
enabled | TINYINT | 활성화 여부 (1: 활성, 0: 비활성) |
category | VARCHAR(255) | 카테고리 |
subcategory | VARCHAR(255) | 서브카테고리 |
category_id | VARCHAR(255) | 카테고리 ID |
created_at | TIMESTAMP | 생성일시 |
주요 인덱스:
•
unique_survey_uuid_ver: survey_uuid + ver 조합의 유일성 보장
•
idx_survey_uuid: survey_uuid 검색 최적화
survey_question
설명: 각 설문에 속한 질문들을 저장
컬럼명 | 타입 | 설명 |
idx | INT | 기본키 (Auto Increment) |
survey_id | INT | surveys 테이블 참조 (FK) |
id | VARCHAR(255) | 질문 고유 ID |
title | JSON | 질문 제목 (다국어 지원) |
description | JSON | 질문 설명 |
addinfo | JSON | 추가 정보 |
question_type | ENUM | 질문 유형 ('short-text', 'single-choice') |
question_list | JSON | 선택지 리스트 (single-choice용) |
options | JSON | 질문 옵션 설정 |
attachments | JSON | 첨부파일 정보 |
sort_order | INT | 질문 순서 |
created_at | TIMESTAMP | 생성일시 |
is_unique | TINYINT(1) | 고유성 체크 (computed column) |
주요 인덱스:
•
unique_id_with_survey: 동일 설문 내 질문 ID 중복 방지
외래키:
•
survey_id → surveys(survey_id) ON DELETE CASCADE
survey_session
설명: 실제 통화 세션 정보를 관리
컬럼명 | 타입 | 설명 |
id | INT | 기본키 (Auto Increment) |
session_key | VARCHAR(255) | 세션 고유 키 |
campaign_uuid | CHAR(36) | 캠페인 UUID (NULL 가능) |
target_uuid | CHAR(36) | 타겟 UUID (NULL 가능) |
phone_number | VARCHAR(20) | 호출된 전화번호 |
survey_uuid | VARCHAR(255) | 설문 UUID |
ver | INT | 설문 버전 |
useridx | INT | 사용자 인덱스 (NULL 가능) |
progress | VARCHAR(150) | 현재 진행 상태 |
history_stack | JSON | 질문 이동 히스토리 |
status | ENUM | 세션 상태 ('in-progress', 'completed', 'expired', 'initiated', 'terminated', 'failed') |
started_at | TIMESTAMP | 시작 시각 |
completed_at | TIMESTAMP | 완료 시각 |
call_duration_seconds | INT | 통화 시간 (초) |
termination_reason | VARCHAR(255) | 종료 사유 |
last_activity_at | TIMESTAMP | 마지막 활동 시각 |
created_at | TIMESTAMP | 생성일시 |
주요 인덱스:
•
unique_session_key: 세션 키 유일성 보장
•
idx_campaign_uuid: 캠페인별 세션 조회
•
idx_phone_number: 전화번호별 조회
2. 전화번호 관리
contact_groups
설명: 전화번호를 그룹으로 관리
컬럼명 | 타입 | 설명 |
group_id | INT | 기본키 (Auto Increment) |
group_uuid | CHAR(36) | 그룹 고유 식별자 |
name | VARCHAR(255) | 그룹명 |
description | TEXT | 그룹 설명 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
사용 예시:
•
"VIP 고객"
•
"신규 회원"
•
"재구매 타겟"
contacts
설명: 개별 전화번호 정보를 저장
컬럼명 | 타입 | 설명 |
contact_id | INT | 기본키 (Auto Increment) |
contact_uuid | CHAR(36) | 연락처 고유 식별자 |
phone_number | VARCHAR(20) | 전화번호 |
name | VARCHAR(255) | 이름 (선택) |
metadata | JSON | 추가 메타데이터 (예: 나이, 지역 등) |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
주요 인덱스:
•
idx_phone_number: 전화번호 검색 최적화
contact_group_members
설명: 그룹과 연락처의 N:N 관계 매핑
컬럼명 | 타입 | 설명 |
id | INT | 기본키 (Auto Increment) |
group_uuid | CHAR(36) | 그룹 UUID (FK) |
contact_uuid | CHAR(36) | 연락처 UUID (FK) |
created_at | TIMESTAMP | 생성일시 |
주요 인덱스:
•
unique_group_contact: 동일 그룹 내 중복 연락처 방지
외래키:
•
group_uuid → contact_groups(group_uuid) ON DELETE CASCADE
•
contact_uuid → contacts(contact_uuid) ON DELETE CASCADE
3. 캠페인 관리
survey_campaigns
설명: 설문 캠페인의 기본 정보 관리
컬럼명 | 타입 | 설명 |
campaign_id | INT | 기본키 (Auto Increment) |
campaign_uuid | CHAR(36) | 캠페인 고유 식별자 |
survey_uuid | VARCHAR(255) | 대상 설문 UUID |
name | VARCHAR(255) | 캠페인명 |
status | ENUM | 캠페인 상태 ('scheduled', 'running', 'paused', 'completed', 'cancelled') |
scheduled_at | TIMESTAMP | 예약 시각 |
started_at | TIMESTAMP | 시작 시각 |
completed_at | TIMESTAMP | 완료 시각 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
워크플로우:
1.
scheduled → running
2.
running → paused (일시정지 가능)
3.
paused → running (재개)
4.
running → completed (정상 완료)
5.
•
→ cancelled (중단)
campaign_targets
설명: 캠페인별 실제 호출 대상 목록 (스냅샷)
컬럼명 | 타입 | 설명 |
target_id | INT | 기본키 (Auto Increment) |
target_uuid | CHAR(36) | 타겟 고유 식별자 |
campaign_uuid | CHAR(36) | 캠페인 UUID (FK) |
contact_uuid | CHAR(36) | 연락처 UUID (FK) |
phone_number | VARCHAR(20) | 전화번호 (스냅샷) |
name | VARCHAR(255) | 이름 (스냅샷) |
status | ENUM | 호출 상태 ('pending', 'calling', 'completed', 'failed', 'skipped') |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
참고:
•
캠페인 생성 시 contacts 데이터를 복사하여 저장
•
이후 contacts 변경되어도 캠페인 데이터는 영향받지 않음
외래키:
•
campaign_uuid → survey_campaigns(campaign_uuid) ON DELETE CASCADE
•
contact_uuid → contacts(contact_uuid) ON DELETE CASCADE
campaign_call_queue
설명: 호출 대기열 및 순서 관리
컬럼명 | 타입 | 설명 |
queue_id | INT | 기본키 (Auto Increment) |
campaign_uuid | CHAR(36) | 캠페인 UUID (FK) |
target_uuid | CHAR(36) | 타겟 UUID (FK) |
priority | INT | 우선순위 (높을수록 우선) |
status | ENUM | 큐 상태 ('queued', 'processing', 'completed', 'failed') |
retry_count | INT | 재시도 횟수 |
max_retry | INT | 최대 재시도 횟수 (기본 3) |
next_retry_at | TIMESTAMP | 다음 재시도 시각 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
주요 인덱스:
•
idx_campaign_status: 캠페인별 상태 조회
•
idx_next_retry: 재시도 스케줄링
외래키:
•
campaign_uuid → survey_campaigns(campaign_uuid) ON DELETE CASCADE
•
target_uuid → campaign_targets(target_uuid) ON DELETE CASCADE
4. 응답 데이터 & 호출 관리
survey_responses
설명: 설문 응답 전체 정보
컬럼명 | 타입 | 설명 |
response_id | INT | 기본키 (Auto Increment) |
response_uuid | CHAR(36) | 응답 고유 식별자 |
session_key | VARCHAR(255) | 세션 키 |
survey_uuid | VARCHAR(255) | 설문 UUID |
ver | INT | 설문 버전 |
is_completed | TINYINT | 완료 여부 (1: 완료, 0: 미완료) |
completed_at | TIMESTAMP | 완료일시 |
created_at | TIMESTAMP | 생성일시 |
주요 인덱스:
•
idx_session_key: 세션별 응답 조회
•
idx_survey_uuid_ver: 설문별 응답 집계
response_answers
설명: 각 질문별 개별 답변
컬럼명 | 타입 | 설명 |
answer_id | INT | 기본키 (Auto Increment) |
answer_uuid | CHAR(36) | 답변 고유 식별자 |
response_uuid | CHAR(36) | 응답 UUID (FK) |
question_id | VARCHAR(255) | 질문 ID |
answer_text | TEXT | 답변 텍스트 (최종 정제된 답변) |
answer_value | VARCHAR(500) | 답변 값 (선택지 ID 등) |
stt_raw_text | TEXT | STT 원본 텍스트 (검증용) |
answered_at | TIMESTAMP | 답변일시 |
데이터 예시:
{
"question_id": "q1",
"answer_text": "매우 만족",
"answer_value": "choice_1",
"stt_raw_text": "저는 매우 만족했어요"
}
JSON
복사
외래키:
•
response_uuid → survey_responses(response_uuid) ON DELETE CASCADE
call_logs
설명: 개별 호출 시도 이력
컬럼명 | 타입 | 설명 |
log_id | INT | 기본키 (Auto Increment) |
session_key | VARCHAR(255) | 세션 키 (성공 시) |
campaign_uuid | CHAR(36) | 캠페인 UUID (FK) |
target_uuid | CHAR(36) | 타겟 UUID (FK) |
phone_number | VARCHAR(20) | 호출 전화번호 |
call_status | ENUM | 호출 결과 ('success', 'failed', 'no_answer', 'busy', 'rejected') |
failure_reason | VARCHAR(500) | 실패 사유 |
call_duration_seconds | INT | 통화 시간 (초) |
created_at | TIMESTAMP | 호출 시각 |
활용:
•
호출 성공/실패 통계
•
실패 원인 분석
•
재시도 전략 수립
외래키:
•
campaign_uuid → survey_campaigns(campaign_uuid) ON DELETE CASCADE
•
target_uuid → campaign_targets(target_uuid) ON DELETE CASCADE
call_retry_queue
설명: 실패한 호출의 재시도 관리
컬럼명 | 타입 | 설명 |
retry_id | INT | 기본키 (Auto Increment) |
target_uuid | CHAR(36) | 타겟 UUID (FK) |
campaign_uuid | CHAR(36) | 캠페인 UUID (FK) |
phone_number | VARCHAR(20) | 전화번호 |
retry_count | INT | 현재 재시도 횟수 |
max_retry | INT | 최대 재시도 횟수 (기본 3) |
last_attempt_at | TIMESTAMP | 마지막 시도 시각 |
next_retry_at | TIMESTAMP | 다음 시도 예정 시각 |
status | ENUM | 재시도 상태 ('pending', 'processing', 'exhausted') |
created_at | TIMESTAMP | 생성일시 |
재시도 로직:
1.
호출 실패 시 → status='pending'
2.
retry_count < max_retry → 재시도 스케줄링
3.
retry_count >= max_retry → status='exhausted'
외래키:
•
campaign_uuid → survey_campaigns(campaign_uuid) ON DELETE CASCADE
•
target_uuid → campaign_targets(target_uuid) ON DELETE CASCADE
5. TTS 캐시 & 음성 설정
tts_cache
설명: TTS 음성 파일 캐싱 시스템
컬럼명 | 타입 | 설명 |
cache_id | INT | 기본키 (Auto Increment) |
cache_uuid | CHAR(36) | 캐시 고유 식별자 |
survey_uuid | VARCHAR(255) | 설문 UUID |
question_id | VARCHAR(255) | 질문 ID |
text_content | TEXT | 원본 텍스트 |
text_hash | CHAR(64) | 텍스트 해시 (중복 방지) |
voice_profile_uuid | CHAR(36) | 음성 프로필 UUID |
audio_file_path | VARCHAR(500) | 음성 파일 경로 |
audio_format | VARCHAR(10) | 파일 포맷 (기본: mp4) |
duration_ms | INT | 재생 시간 (밀리초) |
file_size_bytes | INT | 파일 크기 (바이트) |
created_at | TIMESTAMP | 생성일시 |
캐싱 전략:
1.
설문 생성/수정 시 모든 질문 TTS 사전 생성
2.
text_hash로 동일 텍스트 재사용
3.
실시간 TTS 생성 없이 캐시 파일 직접 재생
주요 인덱스:
•
idx_text_hash: 중복 텍스트 확인
•
idx_survey_question: 설문-질문 조합 조회
voice_profiles
설명: 음성 커스터마이징 프로필
컬럼명 | 타입 | 설명 |
profile_id | INT | 기본키 (Auto Increment) |
profile_uuid | CHAR(36) | 프로필 고유 식별자 |
survey_uuid | VARCHAR(255) | 설문 UUID (NULL이면 전역) |
name | VARCHAR(255) | 프로필명 |
speed | DECIMAL(3,1) | 재생 속도 (0.5 ~ 2.0) |
gender | ENUM | 성별 ('male', 'female') |
voice_type | ENUM | 음성 타입 ('conversational', 'narration', 'educational') |
is_default | TINYINT | 기본 프로필 여부 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
설정 예시:
{
"name": "친근한 여성 음성",
"speed": 1.0,
"gender": "female",
"voice_type": "conversational"
}
JSON
복사
주요 인덱스:
•
idx_is_default: 기본 프로필 빠른 조회
session_logs
설명: 세션 중 발생하는 모든 이벤트 로깅
컬럼명 | 타입 | 설명 |
log_id | INT | 기본키 (Auto Increment) |
session_key | VARCHAR(255) | 세션 키 |
event_type | ENUM | 이벤트 타입 ('question_played', 'answer_received', 'retry', 'no_response', 'error', 'terminated') |
question_id | VARCHAR(255) | 질문 ID (관련 질문이 있는 경우) |
event_data | JSON | 이벤트 상세 데이터 |
created_at | TIMESTAMP | 발생일시 |
이벤트 예시:
{
"event_type": "retry",
"question_id": "q3",
"event_data": {
"retry_count": 2,
"reason": "no_response",
"wait_time_seconds": 10
}
}
JSON
복사
활용:
•
사용자 행동 패턴 분석
•
문제 질문 식별
•
시스템 오류 추적
주요 인덱스:
•
idx_session_key: 세션별 로그 조회
•
idx_event_type: 이벤트 유형별 분석
6. Security & Analytics
question_logic_rules
설명: 조건부 질문 분기 로직 (Phase 2)
컬럼명 | 타입 | 설명 |
rule_id | INT | 기본키 (Auto Increment) |
rule_uuid | CHAR(36) | 규칙 고유 식별자 |
survey_uuid | VARCHAR(255) | 설문 UUID |
source_question_id | VARCHAR(255) | 조건 판단 질문 ID |
condition_type | ENUM | 조건 타입 ('equals', 'contains', 'greater_than', 'less_than', 'in_range') |
condition_value | JSON | 조건 값 |
target_question_id | VARCHAR(255) | 이동할 질문 ID |
action | ENUM | 실행 액션 ('show', 'skip', 'jump_to') |
priority | INT | 우선순위 (높을수록 우선) |
is_active | TINYINT | 활성화 여부 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
로직 예시:
{
"source_question_id": "q1",
"condition_type": "equals",
"condition_value": {"answer": "yes"},
"target_question_id": "q2",
"action": "jump_to"
}
JSON
복사
시나리오:
•
Q1: 제품을 사용해보셨나요? → "예" 선택
•
→ Q2로 이동: 만족도를 평가해주세요
•
Q1: 제품을 사용해보셨나요? → "아니오" 선택
•
→ Q3로 이동: 관심 있으신 이유는?
pii_masking_rules
설명: 개인정보 자동 마스킹 규칙
컬럼명 | 타입 | 설명 |
rule_id | INT | 기본키 (Auto Increment) |
rule_uuid | CHAR(36) | 규칙 고유 식별자 |
rule_name | VARCHAR(255) | 규칙명 |
pattern | VARCHAR(500) | 정규식 패턴 |
mask_type | ENUM | 마스킹 타입 ('phone', 'ssn', 'card', 'email', 'custom') |
replacement | VARCHAR(100) | 대체 문자열 (기본: ***) |
is_active | TINYINT | 활성화 여부 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
마스킹 예시:
원본: "제 전화번호는 010-1234-5678입니다"
마스킹: "제 전화번호는 010-****-****입니다"
원본: "카드번호 1234-5678-9012-3456으로 결제했어요"
마스킹: "카드번호 ****-****-****-3456으로 결제했어요"
Plain Text
복사
주요 인덱스:
•
idx_mask_type: 타입별 규칙 조회
profanity_filters
설명: 욕설 및 부적절한 언어 필터
컬럼명 | 타입 | 설명 |
filter_id | INT | 기본키 (Auto Increment) |
filter_uuid | CHAR(36) | 필터 고유 식별자 |
word | VARCHAR(255) | 필터링 단어 |
severity | ENUM | 심각도 ('low', 'medium', 'high') |
language | VARCHAR(10) | 언어 코드 (기본: ko) |
is_active | TINYINT | 활성화 여부 |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
심각도 정책:
•
low: 경고 로그만 기록
•
medium: 응답 재요청
•
high: 세션 즉시 종료
주요 인덱스:
•
unique_word_language: 언어별 단어 중복 방지
survey_analytics
설명: 일별 설문/캠페인 통계
컬럼명 | 타입 | 설명 |
analytics_id | INT | 기본키 (Auto Increment) |
survey_uuid | VARCHAR(255) | 설문 UUID |
campaign_uuid | CHAR(36) | 캠페인 UUID (NULL 가능) |
date | DATE | 통계 날짜 |
total_calls | INT | 총 호출 수 |
successful_calls | INT | 성공 호출 수 |
failed_calls | INT | 실패 호출 수 |
completed_surveys | INT | 완료된 설문 수 |
avg_duration_seconds | DECIMAL(10,2) | 평균 통화 시간 (초) |
completion_rate | DECIMAL(5,2) | 완료율 (%) |
created_at | TIMESTAMP | 생성일시 |
updated_at | TIMESTAMP | 수정일시 |
계산 공식:
completion_rate = (completed_surveys / successful_calls) * 100
Plain Text
복사
주요 인덱스:
•
unique_survey_campaign_date: 날짜별 중복 방지
•
idx_date: 기간별 조회
system_metrics
설명: 시스템 성능 지표 측정
컬럼명 | 타입 | 설명 |
metric_id | INT | 기본키 (Auto Increment) |
metric_type | ENUM | 측정 타입 ('tts_latency', 'stt_latency', 'llm_latency', 'total_turn_time', 'error_rate') |
avg_value_ms | DECIMAL(10,2) | 평균 값 (밀리초) |
min_value_ms | DECIMAL(10,2) | 최소 값 (밀리초) |
max_value_ms | DECIMAL(10,2) | 최대 값 (밀리초) |
sample_count | INT | 샘플 수 |
measured_at | TIMESTAMP | 측정 시각 |
목표 지표:
Metric | Target |
TTS 재생 | < 100ms |
STT 처리 | < 500ms |
LLM 응답 검증 | < 2000ms |
전체 턴 시간 | < 2초 |
주요 인덱스:
•
idx_metric_type: 타입별 조회
•
idx_measured_at: 시계열 분석
ERD 관계도 요약
surveys (1) ──< (N) survey_question
surveys (1) ──< (N) survey_session
surveys (1) ──< (N) survey_campaigns
contact_groups (1) ──< (N) contact_group_members (N) ──> (1) contacts
survey_campaigns (1) ──< (N) campaign_targets
survey_campaigns (1) ──< (N) campaign_call_queue
survey_campaigns (1) ──< (N) call_logs
survey_session (1) ──< (1) survey_responses (1) ──< (N) response_answers
survey_campaigns (1) ──< (N) survey_session
campaign_targets (1) ──< (N) survey_session
Plain Text
복사
인덱싱 전략
성능 최적화를 위한 주요 인덱스
1.
UUID 인덱스: 모든 UUID 컬럼에 인덱스 설정
2.
복합 인덱스: 자주 함께 조회되는 컬럼 (survey_uuid + ver)
3.
상태 인덱스: ENUM 타입의 상태 컬럼
4.
시간 인덱스: created_at, measured_at 등 시계열 데이터
5.
외래키 인덱스: JOIN 성능 향상
추가 할일
1. 파티셔닝
대용량 데이터 처리를 위해 다음 테이블은 파티셔닝 고려:
•
session_logs: created_at 기준 월별 파티셔닝
•
call_logs: created_at 기준 월별 파티셔닝
•
system_metrics: measured_at 기준 일별 파티셔닝
2. 아카이빙
오래된 데이터는 별도 아카이브 테이블로 이동:
•
6개월 이상 된 session_logs
•
1년 이상 된 call_logs
3. 백업 전략
•
일별 전체 백업
•
시간별 증분 백업
•
중요 테이블 (surveys, survey_responses) 실시간 복제
•
최종 수정일: 2025-12-23
