Table of contents
Issue Point
기능 도입 성과 (11/17 기준 전후 4주)
1.
부진 기사 메인 노출 시간 변화
2.
메인 기사 교체 빈도 변화
3.
운영 시간대 vs 비운영 시간대 메인 기사 성과 차이
디자인 적용 성과 (3월 초 쯤)
1.
부진기사 메인 노출시간 추가 감소 여부
2.
메인기사 초기 반응속도 변화
Task
API 설계
•
엔드포인트
→ GET /api/v2/articles/effectiveness?launch_date=2026-01-15&period_days=30
파라미터 | 타입 | 필수 | 설명 |
launch_date | string | Y | 대시보드 도입일 |
period_days | int | N | 비교 기간 일수 |
•
비교 구간
◦
before: launch_date - period_days ~ launch_date (도입 전)
◦
after: launch_date ~ launch_date + period_days (도입 후)
•
응답 구조
{
"launch_date": "2026-01-15",
"period_days": 30,
"before_period": { "start": "2025-12-16", "end": "2026-01-15" },
"after_period": { "start": "2026-01-15", "end": "2026-02-14" },
"underperforming_exposure": {
"before": {
"avg_exposure_minutes": 180.5,
"median_exposure_minutes": 155.0,
"article_count": 45
},
"after": {
"avg_exposure_minutes": 120.3,
"median_exposure_minutes": 98.0,
"article_count": 52
},
"change_percent": -33.3
},
"replacement_frequency": {
"before": {
"avg_daily_replacements": 15.2,
"total_articles": 456
},
"after": {
"avg_daily_replacements": 22.8,
"total_articles": 684
},
"change_percent": 50.0
},
"time_slot_comparison": {
"operating": {
"before": {
"avg_exposure_minutes": 150.0,
"avg_final_view_count": 5000,
"article_count": 280
},
"after": {
"avg_exposure_minutes": 100.0,
"avg_final_view_count": 6500,
"article_count": 310
}
},
"non_operating": {
"before": {
"avg_exposure_minutes": 200.0,
"avg_final_view_count": 3000,
"article_count": 176
},
"after": {
"avg_exposure_minutes": 195.0,
"avg_final_view_count": 3200,
"article_count": 174
}
}
}
}
JSON
복사
지표 산출 로직
•
부진 기사 노출 시간 (underperforming_exposure)
◦
기사별 최종 조회수 = MAX(view_count) from naver_main_articles_view_count
◦
기사별 노출 시간 = updated_at - created_at from naver_main_articles
◦
부진 기사 정의: 해당 기간 내 최종 조회수 하위 20% (PERCENT_RANK)
◦
before/after 각각 하위 20% 기사의 평균 노출 시간 비교
•
메인 기사 교체 빈도 (replacement_frequency)
일별 신규 메인 기사 수 = COUNT(*) by DATE(created_at) from naver_main_articles
before / after 일 평균 비교
•
운영 / 비운영 시간대 비교 (time_slot_comparison)
운영 : HOUR(created_at) 9~17시
비운영 : 나머지 시간대
각 시간대별 평균 노출 시간 + 평균 최종 조회수 비교
파일 추가
•
__init__.py
◦
router import
•
schema.py - 엔티티 정의
PeriodRange: start, end
ExposureMetrics: avg_exposure_minutes, median_exposure_minutes, article_countUnderperformingExposure: before, after, change_percent
ReplacementMetrics: avg_daily_replacements, total_articles
ReplacementFrequency: before, after, change_percent
TimeSlotMetrics: avg_exposure_minutes, avg_final_view_count, article_count
TimeSlotPeriod: before, after
TimeSlotComparison: operating, non_operating
EffectivenessResponse: 전체 응답
Plain Text
복사
•
query.py - 도메인 로직
◦
get_article_stats (db_session, start_date, end_date)
◦
get_daily_article_counts (db_session, start_date, end_date)
◦
get_time_slot_stats (db_session, start_date, end_date)
•
service.py - 비즈니스 로직
1.
before/after 기간 계산
2.
각 기간별로 3개 쿼리 함수 호출 (총 6회)
3.
부진 기사: article_stats에서 하위 20% 필터 → 평균 노출 시간
4.
교체 빈도: daily_counts 평균
5.
시간대: time_slot_stats 집계
6.
change_percent 계산: (after - before) / before * 100
7.
EffectivenessResponse 반환
•
router.py - 컨트롤러