mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 17:26:23 +00:00
✨ 주요 기능 - Azure 기반 물리아키텍처 설계 (개발환경/운영환경) - 7개 마이크로서비스 물리 구조 설계 - 네트워크 아키텍처 다이어그램 작성 (Mermaid) - 환경별 비교 분석 및 마스터 인덱스 문서 📁 생성 파일 - design/backend/physical/physical-architecture.md (마스터) - design/backend/physical/physical-architecture-dev.md (개발환경) - design/backend/physical/physical-architecture-prod.md (운영환경) - design/backend/physical/*.mmd (4개 Mermaid 다이어그램) 🎯 핵심 성과 - 비용 최적화: 개발환경 월 $143, 운영환경 월 $2,860 - 확장성: 개발환경 100명 → 운영환경 10,000명 (100배) - 가용성: 개발환경 95% → 운영환경 99.9% - 보안: 다층 보안 아키텍처 (L1~L4) 🛠️ 기술 스택 - Azure Kubernetes Service (AKS) - Azure Database for PostgreSQL Flexible - Azure Cache for Redis Premium - Azure Service Bus Premium - Application Gateway + WAF 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
374 lines
15 KiB
Plaintext
374 lines
15 KiB
Plaintext
-- ============================================================
|
|
-- Analytics Service Database Schema
|
|
-- ============================================================
|
|
-- Database: analytics_db
|
|
-- DBMS: PostgreSQL 16.x
|
|
-- Description: 이벤트 분석 및 통계 데이터 관리
|
|
-- ============================================================
|
|
|
|
-- ============================================================
|
|
-- 1. 데이터베이스 생성 (필요 시)
|
|
-- ============================================================
|
|
-- CREATE DATABASE analytics_db
|
|
-- WITH
|
|
-- OWNER = postgres
|
|
-- ENCODING = 'UTF8'
|
|
-- LC_COLLATE = 'en_US.UTF-8'
|
|
-- LC_CTYPE = 'en_US.UTF-8'
|
|
-- TABLESPACE = pg_default
|
|
-- CONNECTION LIMIT = -1;
|
|
|
|
-- ============================================================
|
|
-- 2. 테이블 생성
|
|
-- ============================================================
|
|
|
|
-- 2.1 event_stats (이벤트 통계)
|
|
DROP TABLE IF EXISTS event_stats CASCADE;
|
|
|
|
CREATE TABLE event_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(36) NOT NULL UNIQUE,
|
|
event_title VARCHAR(255) NOT NULL,
|
|
user_id VARCHAR(36) NOT NULL,
|
|
total_participants INTEGER NOT NULL DEFAULT 0,
|
|
total_views INTEGER NOT NULL DEFAULT 0,
|
|
estimated_roi DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
|
target_roi DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
|
sales_growth_rate DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
|
total_investment DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
|
expected_revenue DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- 제약 조건
|
|
CONSTRAINT chk_event_stats_participants CHECK (total_participants >= 0),
|
|
CONSTRAINT chk_event_stats_views CHECK (total_views >= 0),
|
|
CONSTRAINT chk_event_stats_estimated_roi CHECK (estimated_roi >= 0),
|
|
CONSTRAINT chk_event_stats_target_roi CHECK (target_roi >= 0),
|
|
CONSTRAINT chk_event_stats_investment CHECK (total_investment >= 0),
|
|
CONSTRAINT chk_event_stats_revenue CHECK (expected_revenue >= 0),
|
|
CONSTRAINT chk_event_stats_status CHECK (status IN ('ACTIVE', 'ENDED', 'ARCHIVED'))
|
|
);
|
|
|
|
-- event_stats 인덱스
|
|
CREATE INDEX idx_event_stats_user_id ON event_stats (user_id);
|
|
CREATE INDEX idx_event_stats_status ON event_stats (status);
|
|
CREATE INDEX idx_event_stats_created_at ON event_stats (created_at DESC);
|
|
|
|
-- event_stats 주석
|
|
COMMENT ON TABLE event_stats IS '이벤트별 통계 집계 데이터';
|
|
COMMENT ON COLUMN event_stats.event_id IS '이벤트 ID (UUID, Event Service 참조)';
|
|
COMMENT ON COLUMN event_stats.user_id IS '사용자 ID (UUID, User Service 참조)';
|
|
COMMENT ON COLUMN event_stats.total_participants IS '총 참여자 수';
|
|
COMMENT ON COLUMN event_stats.total_views IS '총 조회 수';
|
|
COMMENT ON COLUMN event_stats.estimated_roi IS '예상 ROI (%)';
|
|
COMMENT ON COLUMN event_stats.target_roi IS '목표 ROI (%)';
|
|
COMMENT ON COLUMN event_stats.sales_growth_rate IS '매출 성장률 (%)';
|
|
COMMENT ON COLUMN event_stats.total_investment IS '총 투자 금액 (원)';
|
|
COMMENT ON COLUMN event_stats.expected_revenue IS '예상 수익 (원)';
|
|
COMMENT ON COLUMN event_stats.status IS '이벤트 상태 (ACTIVE, ENDED, ARCHIVED)';
|
|
|
|
-- ============================================================
|
|
|
|
-- 2.2 channel_stats (채널 통계)
|
|
DROP TABLE IF EXISTS channel_stats CASCADE;
|
|
|
|
CREATE TABLE channel_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(36) NOT NULL,
|
|
channel_name VARCHAR(50) NOT NULL,
|
|
channel_type VARCHAR(20) NOT NULL,
|
|
impressions INTEGER NOT NULL DEFAULT 0,
|
|
views INTEGER NOT NULL DEFAULT 0,
|
|
clicks INTEGER NOT NULL DEFAULT 0,
|
|
participants INTEGER NOT NULL DEFAULT 0,
|
|
conversions INTEGER NOT NULL DEFAULT 0,
|
|
distribution_cost DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
|
likes INTEGER NOT NULL DEFAULT 0,
|
|
comments INTEGER NOT NULL DEFAULT 0,
|
|
shares INTEGER NOT NULL DEFAULT 0,
|
|
total_calls INTEGER NOT NULL DEFAULT 0,
|
|
completed_calls INTEGER NOT NULL DEFAULT 0,
|
|
average_duration INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- 제약 조건
|
|
CONSTRAINT uk_channel_stats_event_channel UNIQUE (event_id, channel_name),
|
|
CONSTRAINT chk_channel_stats_impressions CHECK (impressions >= 0),
|
|
CONSTRAINT chk_channel_stats_views CHECK (views >= 0),
|
|
CONSTRAINT chk_channel_stats_clicks CHECK (clicks >= 0),
|
|
CONSTRAINT chk_channel_stats_participants CHECK (participants >= 0),
|
|
CONSTRAINT chk_channel_stats_conversions CHECK (conversions >= 0),
|
|
CONSTRAINT chk_channel_stats_cost CHECK (distribution_cost >= 0),
|
|
CONSTRAINT chk_channel_stats_calls CHECK (total_calls >= 0),
|
|
CONSTRAINT chk_channel_stats_completed CHECK (completed_calls >= 0 AND completed_calls <= total_calls),
|
|
CONSTRAINT chk_channel_stats_duration CHECK (average_duration >= 0),
|
|
CONSTRAINT chk_channel_stats_type CHECK (channel_type IN ('TV', 'SNS', 'VOICE'))
|
|
);
|
|
|
|
-- channel_stats 인덱스
|
|
CREATE INDEX idx_channel_stats_event_id ON channel_stats (event_id);
|
|
CREATE INDEX idx_channel_stats_channel_type ON channel_stats (channel_type);
|
|
CREATE INDEX idx_channel_stats_participants ON channel_stats (participants DESC);
|
|
|
|
-- channel_stats 주석
|
|
COMMENT ON TABLE channel_stats IS '채널별 성과 데이터 (외부 API 연동)';
|
|
COMMENT ON COLUMN channel_stats.event_id IS '이벤트 ID (UUID)';
|
|
COMMENT ON COLUMN channel_stats.channel_name IS '채널명 (WooriTV, GenieTV, RingoBiz, SNS 등)';
|
|
COMMENT ON COLUMN channel_stats.channel_type IS '채널 타입 (TV, SNS, VOICE)';
|
|
COMMENT ON COLUMN channel_stats.impressions IS '노출 수';
|
|
COMMENT ON COLUMN channel_stats.views IS '조회 수';
|
|
COMMENT ON COLUMN channel_stats.clicks IS '클릭 수';
|
|
COMMENT ON COLUMN channel_stats.participants IS '참여자 수';
|
|
COMMENT ON COLUMN channel_stats.conversions IS '전환 수';
|
|
COMMENT ON COLUMN channel_stats.distribution_cost IS '배포 비용 (원)';
|
|
COMMENT ON COLUMN channel_stats.likes IS '좋아요 수 (SNS)';
|
|
COMMENT ON COLUMN channel_stats.comments IS '댓글 수 (SNS)';
|
|
COMMENT ON COLUMN channel_stats.shares IS '공유 수 (SNS)';
|
|
COMMENT ON COLUMN channel_stats.total_calls IS '총 통화 수 (VOICE)';
|
|
COMMENT ON COLUMN channel_stats.completed_calls IS '완료 통화 수 (VOICE)';
|
|
COMMENT ON COLUMN channel_stats.average_duration IS '평균 통화 시간 (초, VOICE)';
|
|
|
|
-- ============================================================
|
|
|
|
-- 2.3 timeline_data (시계열 데이터)
|
|
DROP TABLE IF EXISTS timeline_data CASCADE;
|
|
|
|
CREATE TABLE timeline_data (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
event_id VARCHAR(36) NOT NULL,
|
|
timestamp TIMESTAMP NOT NULL,
|
|
participants INTEGER NOT NULL DEFAULT 0,
|
|
views INTEGER NOT NULL DEFAULT 0,
|
|
engagement INTEGER NOT NULL DEFAULT 0,
|
|
conversions INTEGER NOT NULL DEFAULT 0,
|
|
cumulative_participants INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- 제약 조건
|
|
CONSTRAINT chk_timeline_participants CHECK (participants >= 0),
|
|
CONSTRAINT chk_timeline_views CHECK (views >= 0),
|
|
CONSTRAINT chk_timeline_engagement CHECK (engagement >= 0),
|
|
CONSTRAINT chk_timeline_conversions CHECK (conversions >= 0),
|
|
CONSTRAINT chk_timeline_cumulative CHECK (cumulative_participants >= 0)
|
|
);
|
|
|
|
-- timeline_data 인덱스
|
|
CREATE INDEX idx_timeline_event_timestamp ON timeline_data (event_id, timestamp ASC);
|
|
CREATE INDEX idx_timeline_timestamp ON timeline_data (timestamp ASC);
|
|
|
|
-- timeline_data BRIN 인덱스 (시계열 최적화)
|
|
CREATE INDEX idx_timeline_brin_timestamp ON timeline_data USING BRIN (timestamp);
|
|
|
|
-- timeline_data 주석
|
|
COMMENT ON TABLE timeline_data IS '시간별 참여 추이 데이터 (시계열 분석)';
|
|
COMMENT ON COLUMN timeline_data.event_id IS '이벤트 ID (UUID)';
|
|
COMMENT ON COLUMN timeline_data.timestamp IS '기록 시간';
|
|
COMMENT ON COLUMN timeline_data.participants IS '해당 시간 참여자 수';
|
|
COMMENT ON COLUMN timeline_data.views IS '해당 시간 조회 수';
|
|
COMMENT ON COLUMN timeline_data.engagement IS '참여도 (상호작용 수)';
|
|
COMMENT ON COLUMN timeline_data.conversions IS '해당 시간 전환 수';
|
|
COMMENT ON COLUMN timeline_data.cumulative_participants IS '누적 참여자 수';
|
|
|
|
-- ============================================================
|
|
-- 3. 트리거 생성 (updated_at 자동 업데이트)
|
|
-- ============================================================
|
|
|
|
-- updated_at 자동 업데이트 함수
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- event_stats 트리거
|
|
CREATE TRIGGER trigger_event_stats_updated_at
|
|
BEFORE UPDATE ON event_stats
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- channel_stats 트리거
|
|
CREATE TRIGGER trigger_channel_stats_updated_at
|
|
BEFORE UPDATE ON channel_stats
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- timeline_data 트리거
|
|
CREATE TRIGGER trigger_timeline_data_updated_at
|
|
BEFORE UPDATE ON timeline_data
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- ============================================================
|
|
-- 4. 샘플 데이터 (개발 및 테스트용)
|
|
-- ============================================================
|
|
|
|
-- 4.1 event_stats 샘플 데이터
|
|
INSERT INTO event_stats (
|
|
event_id, event_title, user_id,
|
|
total_participants, total_views, estimated_roi, target_roi,
|
|
sales_growth_rate, total_investment, expected_revenue, status
|
|
) VALUES
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'신메뉴 출시 기념 이벤트',
|
|
'987fcdeb-51a2-43f9-8765-fedcba987654',
|
|
150, 1200, 18.50, 20.00,
|
|
12.30, 5000000.00, 5925000.00, 'ACTIVE'
|
|
),
|
|
(
|
|
'223e4567-e89b-12d3-a456-426614174002',
|
|
'여름 시즌 특가 이벤트',
|
|
'987fcdeb-51a2-43f9-8765-fedcba987654',
|
|
320, 2500, 22.00, 25.00,
|
|
15.80, 8000000.00, 9760000.00, 'ACTIVE'
|
|
);
|
|
|
|
-- 4.2 channel_stats 샘플 데이터
|
|
INSERT INTO channel_stats (
|
|
event_id, channel_name, channel_type,
|
|
impressions, views, clicks, participants, conversions,
|
|
distribution_cost, likes, comments, shares
|
|
) VALUES
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'WooriTV', 'TV',
|
|
50000, 8000, 1500, 80, 60,
|
|
2000000.00, 0, 0, 0
|
|
),
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'GenieTV', 'TV',
|
|
40000, 6000, 1200, 50, 40,
|
|
1500000.00, 0, 0, 0
|
|
),
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'Instagram', 'SNS',
|
|
30000, 5000, 800, 20, 15,
|
|
1000000.00, 1500, 200, 300
|
|
);
|
|
|
|
-- 4.3 timeline_data 샘플 데이터
|
|
INSERT INTO timeline_data (
|
|
event_id, timestamp,
|
|
participants, views, engagement, conversions, cumulative_participants
|
|
) VALUES
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'2025-01-29 10:00:00',
|
|
10, 100, 50, 5, 10
|
|
),
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'2025-01-29 11:00:00',
|
|
15, 150, 70, 8, 25
|
|
),
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'2025-01-29 12:00:00',
|
|
20, 200, 90, 10, 45
|
|
),
|
|
(
|
|
'123e4567-e89b-12d3-a456-426614174001',
|
|
'2025-01-29 13:00:00',
|
|
25, 250, 110, 12, 70
|
|
);
|
|
|
|
-- ============================================================
|
|
-- 5. 데이터베이스 사용자 권한 설정
|
|
-- ============================================================
|
|
|
|
-- 5.1 읽기 전용 사용자 (선택 사항)
|
|
-- CREATE USER analytics_readonly WITH PASSWORD 'secure_password_readonly';
|
|
-- GRANT CONNECT ON DATABASE analytics_db TO analytics_readonly;
|
|
-- GRANT USAGE ON SCHEMA public TO analytics_readonly;
|
|
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO analytics_readonly;
|
|
-- ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO analytics_readonly;
|
|
|
|
-- 5.2 읽기/쓰기 사용자 (애플리케이션용)
|
|
-- CREATE USER analytics_readwrite WITH PASSWORD 'secure_password_readwrite';
|
|
-- GRANT CONNECT ON DATABASE analytics_db TO analytics_readwrite;
|
|
-- GRANT USAGE ON SCHEMA public TO analytics_readwrite;
|
|
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO analytics_readwrite;
|
|
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO analytics_readwrite;
|
|
-- ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO analytics_readwrite;
|
|
-- ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO analytics_readwrite;
|
|
|
|
-- ============================================================
|
|
-- 6. 데이터베이스 통계 수집 (성능 최적화)
|
|
-- ============================================================
|
|
|
|
ANALYZE event_stats;
|
|
ANALYZE channel_stats;
|
|
ANALYZE timeline_data;
|
|
|
|
-- ============================================================
|
|
-- 7. 파티셔닝 설정 (선택 사항 - 대용량 시계열 데이터)
|
|
-- ============================================================
|
|
|
|
-- 월별 파티셔닝 예시 (timeline_data 테이블)
|
|
--
|
|
-- DROP TABLE IF EXISTS timeline_data CASCADE;
|
|
--
|
|
-- CREATE TABLE timeline_data (
|
|
-- id BIGSERIAL,
|
|
-- event_id VARCHAR(36) NOT NULL,
|
|
-- timestamp TIMESTAMP NOT NULL,
|
|
-- participants INTEGER NOT NULL DEFAULT 0,
|
|
-- views INTEGER NOT NULL DEFAULT 0,
|
|
-- engagement INTEGER NOT NULL DEFAULT 0,
|
|
-- conversions INTEGER NOT NULL DEFAULT 0,
|
|
-- cumulative_participants INTEGER NOT NULL DEFAULT 0,
|
|
-- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
-- updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
-- PRIMARY KEY (id, timestamp)
|
|
-- ) PARTITION BY RANGE (timestamp);
|
|
--
|
|
-- CREATE TABLE timeline_data_2025_01 PARTITION OF timeline_data
|
|
-- FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
|
|
--
|
|
-- CREATE TABLE timeline_data_2025_02 PARTITION OF timeline_data
|
|
-- FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
|
|
--
|
|
-- -- 자동 파티션 생성은 pg_cron 또는 별도 스크립트 활용
|
|
|
|
-- ============================================================
|
|
-- 8. 백업 및 복구 명령어 (참조용)
|
|
-- ============================================================
|
|
|
|
-- 백업
|
|
-- pg_dump -U postgres -F c -b -v -f /backup/analytics_$(date +%Y%m%d).dump analytics_db
|
|
|
|
-- 복구
|
|
-- pg_restore -U postgres -d analytics_db -v /backup/analytics_20250129.dump
|
|
|
|
-- ============================================================
|
|
-- 9. 데이터베이스 설정 확인
|
|
-- ============================================================
|
|
|
|
-- 테이블 목록 확인
|
|
-- \dt
|
|
|
|
-- 테이블 구조 확인
|
|
-- \d event_stats
|
|
-- \d channel_stats
|
|
-- \d timeline_data
|
|
|
|
-- 인덱스 확인
|
|
-- \di
|
|
|
|
-- 제약 조건 확인
|
|
-- SELECT conname, contype, pg_get_constraintdef(oid)
|
|
-- FROM pg_constraint
|
|
-- WHERE conrelid = 'event_stats'::regclass;
|
|
|
|
-- ============================================================
|
|
-- END OF SCRIPT
|
|
-- ============================================================
|