인프런 커뮤니티 질문&답변

김세현님의 프로필 이미지
김세현

작성한 질문수

BigQuery(SQL) 활용편(퍼널 분석, 리텐션 분석)

[인프런 빅쿼리 빠짝스터디 3주차] 리텐션 과제

작성

·

33

0

1번 문제

WITH 
-- 1단계: 필수 데이터만 추출하기
base_events AS (
    SELECT 
        user_pseudo_id,
        DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul") AS event_date
    FROM advanced.app_logs
),

-- 2단계: 사용자별 첫 방문 주차와 각 활동 주차 구하기
user_weeks AS (
    SELECT DISTINCT  -- 중복 제거
        user_pseudo_id,
        -- 사용자별 첫 방문 주차 (월요일 기준)
        DATE_TRUNC(MIN(event_date) OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
        -- 실제 방문한 주차 (월요일 기준)
        DATE_TRUNC(event_date, WEEK(MONDAY)) AS visit_week
    FROM base_events
),

-- 3단계: 첫 방문 이후 몇 주차인지 계산하기
week_numbers AS (
    SELECT 
        user_pseudo_id,
        -- 첫 방문 이후 경과된 주차 계산
        DATE_DIFF(visit_week, first_week, WEEK) AS week_number
    FROM user_weeks
    -- 최대 12주까지만 분석
    WHERE DATE_DIFF(visit_week, first_week, WEEK) <= 12
),

-- 4단계: 주차별 총 사용자 수 계산하기
weekly_users AS (
    SELECT 
        week_number,
        COUNT(DISTINCT user_pseudo_id) AS user_count
    FROM week_numbers
    GROUP BY week_number
)

-- 5단계: 최종 리텐션 계산하기
SELECT 
    week_number,
    user_count as active_users,
    FIRST_VALUE(user_count) OVER(ORDER BY week_number) as first_week_users,
    ROUND(100.0 * user_count / FIRST_VALUE(user_count) OVER(ORDER BY week_number), 2) as retention_rate
FROM weekly_users
ORDER BY week_number;

 

스크린샷 2024-11-14 오전 12.06.50.png

2번 문제

WITH 
-- 1단계: 사용자별 주차 데이터 준비
user_weeks AS (
    SELECT DISTINCT
        user_pseudo_id,
        -- 첫 방문 주차
        DATE_TRUNC(MIN(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul")) 
            OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
        -- 활동 주차
        DATE_TRUNC(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"), 
            WEEK(MONDAY)) AS event_week
    FROM advanced.app_logs
),

-- 2단계: 사용자 상태 확인
user_status AS (
    SELECT 
        user_pseudo_id,
        event_week,
        first_week,
        -- 이전 방문 주차
        LAG(event_week) OVER(PARTITION BY user_pseudo_id ORDER BY event_week) AS prev_week
    FROM user_weeks
),

-- 3단계: 상태 분류
weekly_status AS (
    SELECT 
        event_week,
        user_pseudo_id,
        CASE
            WHEN event_week = first_week THEN 'New'
            WHEN DATE_DIFF(event_week, prev_week, WEEK) = 1 THEN 'Current'
            WHEN DATE_DIFF(event_week, prev_week, WEEK) > 1 THEN 'Resurrected'
        END as user_type
    FROM user_status
),

-- 4단계: 각 주차별 전체 유저 수
total_users AS (
    SELECT 
        fwd.event_week,
        COUNT(DISTINCT user_pseudo_id) AS total_user_count
    FROM user_status
    CROSS JOIN (SELECT DISTINCT event_week FROM user_weeks) AS fwd
    WHERE first_week <= fwd.event_week
    GROUP BY event_week
),

-- 5단계: 주차별 활성 유저 수 계산
active_users AS (
    SELECT 
        event_week,
        COUNTIF(user_type = 'New') AS new_users,
        COUNTIF(user_type = 'Current') AS current_users,
        COUNTIF(user_type = 'Resurrected') AS resurrected_users,
        COUNT(DISTINCT user_pseudo_id) AS retain_users
    FROM weekly_status 
    GROUP BY event_week
)

-- 6단계: 최종 결과
SELECT 
    FORMAT_DATE('%Y-%m-%d', a.event_week) as week_start,
    new_users,
    current_users,
    resurrected_users,
    (t.total_user_count - a.retain_users) as dormant_users,
    ROUND(100.0 * current_users / NULLIF(LAG(new_users) 
        OVER(ORDER BY a.event_week), 0), 1) as retention_rate,
    retain_users as active_users,
    t.total_user_count as total_users
FROM active_users a
JOIN total_users t ON a.event_week = t.event_week
ORDER BY a.event_week;

 

스크린샷 2024-11-14 오전 12.28.12.png


3번 문제

WITH 
-- 1단계: 기본 데이터 준비
user_weeks AS (
    SELECT DISTINCT
        user_pseudo_id,
        DATE_TRUNC(MIN(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul")) 
            OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
        DATE_TRUNC(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"), 
            WEEK(MONDAY)) AS event_week
    FROM advanced.app_logs
),

-- 2단계: 사용자별 방문 주차 수 계산
user_visit_frequency AS (
    SELECT 
        user_pseudo_id,
        COUNT(DISTINCT event_week) as total_visit_weeks,
        DATE_DIFF(MAX(event_week), MIN(event_week), WEEK) + 1 as weeks_since_first,
        MIN(event_week) as first_visit_week
    FROM user_weeks
    GROUP BY user_pseudo_id
),

-- 3단계: 사용자별 리텐션 점수 계산
user_retention_score AS (
    SELECT 
        user_pseudo_id,
        total_visit_weeks,
        weeks_since_first,
        ROUND(100.0 * total_visit_weeks / weeks_since_first, 2) as visit_rate,
        FORMAT_DATE('%Y-%m', first_visit_week) as cohort_month
    FROM user_visit_frequency
    WHERE weeks_since_first >= 4  -- 최소 4주 이상 경과된 사용자만
),

-- 4단계: 사용자 행동 데이터 분석
user_behavior AS (
    SELECT 
        r.user_pseudo_id,
        r.visit_rate,
        r.cohort_month,
        COUNT(DISTINCT DATE(TIMESTAMP_MICROS(l.event_timestamp), "Asia/Seoul")) as active_days,
        COUNT(DISTINCT l.event_timestamp) as total_events,
        COUNT(DISTINCT l.event_name) as unique_event_types
    FROM user_retention_score r
    JOIN advanced.app_logs l ON r.user_pseudo_id = l.user_pseudo_id
    GROUP BY r.user_pseudo_id, r.visit_rate, r.cohort_month
),

-- 5단계: 리텐션 세그먼트별 행동 패턴 분석
retention_segments AS (
    SELECT 
        cohort_month,
        CASE 
            WHEN visit_rate >= 75 THEN 'Very High (75%+)'
            WHEN visit_rate >= 50 THEN 'High (50-74%)'
            WHEN visit_rate >= 25 THEN 'Medium (25-49%)'
            ELSE 'Low (<25%)'
        END as retention_segment,
        COUNT(DISTINCT user_pseudo_id) as user_count,
        ROUND(AVG(visit_rate), 2) as avg_retention_rate,
        ROUND(AVG(active_days), 1) as avg_active_days,
        ROUND(AVG(total_events), 1) as avg_total_events,
        ROUND(AVG(unique_event_types), 1) as avg_unique_events
    FROM user_behavior
    GROUP BY 
        cohort_month,
        CASE 
            WHEN visit_rate >= 75 THEN 'Very High (75%+)'
            WHEN visit_rate >= 50 THEN 'High (50-74%)'
            WHEN visit_rate >= 25 THEN 'Medium (25-49%)'
            ELSE 'Low (<25%)'
        END
)

-- 최종 결과
SELECT 
    cohort_month,
    retention_segment,
    user_count,
    avg_retention_rate,
    avg_active_days,
    avg_total_events,
    avg_unique_events,
    ROUND(100.0 * user_count / SUM(user_count) OVER (PARTITION BY cohort_month), 2) as segment_percentage
FROM retention_segments
ORDER BY 
    cohort_month,
    CASE retention_segment
        WHEN 'Very High (75%+)' THEN 1
        WHEN 'High (50-74%)' THEN 2
        WHEN 'Medium (25-49%)' THEN 3
        WHEN 'Low (<25%)' THEN 4
    END;

 

스크린샷 2024-11-14 오전 12.35.04.png

 

 

 

4번

WITH 
-- 1단계: 결제 이벤트 기본 데이터
payment_base AS (
    SELECT DISTINCT
        user_pseudo_id,
        DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul") AS event_date
    FROM advanced.app_logs
    WHERE event_name = "click_payment"
),

-- 2단계: 사용자별 첫 결제일과 주차 계산
user_weeks AS (
    SELECT 
        user_pseudo_id,
        -- 첫 결제 주차
        DATE_TRUNC(MIN(event_date) OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
        -- 실제 결제 주차
        DATE_TRUNC(event_date, WEEK(MONDAY)) AS event_week
    FROM payment_base
),

-- 3단계: 주차별 상태 계산
week_status AS (
    SELECT 
        user_pseudo_id,
        event_week,
        first_week,
        -- 첫 주차와의 차이
        DATE_DIFF(event_week, first_week, WEEK) AS week_number
    FROM user_weeks
    WHERE DATE_DIFF(event_week, first_week, WEEK) <= 12  -- 최대 12주까지만 분석
),

-- 4단계: 주차별 활성 사용자 수 계산
weekly_users AS (
    SELECT 
        week_number,
        COUNT(DISTINCT user_pseudo_id) AS active_users
    FROM week_status
    GROUP BY week_number
),

-- 5단계: 첫 주 사용자 수 (코호트 크기) 구하기
first_week_users AS (
    SELECT 
        COUNT(DISTINCT user_pseudo_id) AS cohort_size
    FROM week_status 
    WHERE week_number = 0
)

-- 최종 결과
SELECT 
    week_number as week,
    active_users,
    cohort_size,
    ROUND(100.0 * active_users / cohort_size, 2) as retention_rate
FROM weekly_users
CROSS JOIN first_week_users
ORDER BY week_number;

스크린샷 2024-11-14 오전 12.40.13.png

 

 

 

서비스 성장세

  • 총 사용자: 52,823명까지 증가

  • 8월부터 12월까지 꾸준한 성장

  • 10월에 가장 높은 신규 유저 유입 (4,048명)

리텐션 개선

  • 전체 리텐션: 8월 2.6% → 1월 47.9%로 큰 폭 개선

  • 12월부터 40% 이상의 안정적인 리텐션 유지

  • 재방문 사용자 비중이 지속적으로 증가

     

     

개선 필요점

  • 결제 리텐션이 매우 낮음 (1% 수준)

  • 신규 사용자 유입이 감소 추세

  • 휴면 사용자가 지속적으로 증가

 

답변 1

0

안녕하세요, 인프런 AI 인턴입니다. 김세현님께서 제공한 여러 문제를 풀면서 리텐션 데이터를 분석하신 것 같습니다. 여기에 대한 유사한 질문과 해결 방법을 찾아보았지만, 해당 과제의 복잡성과 세부 사항에 맞춘 명확한 기존 답변을 직접적으로 찾기 어려웠습니다. 그럼에도 불구하고, 아래 제공된 몇 가지 정보를 통해 비슷한 맥락의 문제 해결을 위한 방향성을 제안드리겠습니다:

  1. 리텐션 데이터 분석의 기초: 리텐션 분석을 위한 적절한 SQL 쿼리를 통해 각 주차별로 사용자 활동을 추적하는 방법은 강의의 기본 사항입니다. 이때, 사용자 데이터를 파티셔닝하여 첫 방문 주차와 이후의 활동 주차를 비교하는 방식은 효과적인 리텐션 분석을 위한 핵심 기술입니다.

  2. 사용자 상태 분석: 유사한 질문에서 사용자 상태를 구분하기 위해 ‘New’, ‘Current’, ‘Resurrected’로 상태를 분리하여 분석하는 방법이 설명된 바 있습니다. 이는 리텐션 과제_연습문제 2번에서 다룬 내용과 비슷합니다. 해당 질문의 답변을 통해 상태 분류를 보다 명확하게 이해할 수 있을 것입니다.

  3. 세그먼트 분석: 리텐션 세그먼트를 월별로 구분하고, 사용자의 행동 패턴에 따라 세분화하는 과정은 리텐션을 더욱 깊이있게 이해하는 데 필수적입니다. 이를 통해 각 세그먼트 별로 사용자 행동을 관찰하고, 전략적인 인사이트를 도출할 수 있습니다. 이러한 접근은 같은 강의에서 제공된 예제를 잘 참조하여 학습할 수 있습니다.

  4. 결제 리텐션: 이는 주차별 결제 사용자 수와 처음 결제를 한 사용자 수를 비교하여 리텐션 값을 계산하는 방법입니다. 만약 결제 리텐션이 낮다면, 이는 전환율 개선 및 마케팅 전략 수정이 필요할 수 있다는 것을 의미할 수 있습니다. 리텐션 과제 연습문제 4번(core_event)와 같은 문제는 결제 관련 지표를 어떻게 설정하고 해석할지를 탐구하며, 저조한 전환율을 개선할 수 있는 전략적 인사이트를 제공합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

김세현님의 프로필 이미지
김세현

작성한 질문수

질문하기