Summary 테이블은 말 그대로 요약 테이블로, 어떤 Raw 테이블에서 필요한 정보만 뽑아 집계한 테이블을 의미합니다. 보통 대시보드에서 일, 주, 월별로 어떤 사건이 일어났는지를 집계하게 되는데요.
예를 들어, 넷플릭스 관련 Summary 테이블을 쌓는다고 가정하면
등의 지표를 바탕으로 넷플릭스의 active 유저의 규모, 허들, 어뷰징 등 다양한 주제로 풀어 대시보드로 시각화할 수 있겠죠.
태블로에서 대시보드를 시각화할 땐 저는 아래와 같은 과정을 거칩니다.
물론 1번의 과정에서 Summary 테이브를 집계하지 않고 Raw 테이블에서 SQL 쿼리를 직접 사용하여 Summary 테이블 (GROUP BY 쿼리 사용)을 만드는 것도 가능하나, 경험상 1만 건 이상 넘어가는 테이블을 불러오면 로딩 시간이 길어 활용성이 적었습니다.
그렇기 때문에 빠르게 데이터를 불러오고 시각화를 하기 위해서 Raw 테이블이 아닌 Summary 테이블을 애용하게 됩니다.
그러면 적당히 Raw 데이터에서 Summary 용 테이블을 만들고 적재하면 될텐데 공수를 어떻게 줄인다는 것일까요?
제 기준은
테이블을 새로 만들 필요없이 UPDATE 문 만으로도 테이블을 갱신할 수 있거나 혹은 Airflow에서 재처리 없이 자동 반영되는 구조라면 공수가 적다고 생각합니다.
우선 테이블을 새로 만드는 건 생각보다 매우 귀찮은 작업입니다. 기존의 Summary 테이블이 A, 새로 만들 테이블이 B라고 하면
또한 Airflow에서 재처리하는 것도 시간이 꽤 걸리는 작업일 수도 있습니다. 만일 집계가 잘못되어 재처리해야하는 일자가 1년이 넘어간다면 하루 당 10분만 걸린다고 해도 3,650분 = 61시간 = 2.5일 정도 소요되는 작업입니다.
그래서 공수를 줄이는 Summary 테이블을 잘 만들면 이 두 작업 없이
UPDATE
문 만으로 10분 안에 데이터 재처리가 가능해지고위에서 언급한 세 가지 상황에 대해 아래와 같이 솔루션을 정리했습니다.
구체적으로 알아볼까요~?
데이터 집계가 잘못되는 경우는 보통 대시보드를 오픈하기 전이나 직후인 경우가 많습니다. 저는 기획팀에서 대시보드 기획안을 전달주시면 저희 팀에서 데이터를 집계하고 시각화해 리포트 링크를 전달드렸던 경험이 많은데요. 기획팀에서 의도했던 지표와 제가 집계한 지표의 의미 차이가 있을 때, 이 싱크를 맞추는 과정에서 집계를 다시 하는 과정이 필요했습니다.
이건 제가 많은 환경을 접해보지 못한 것일 수도 있지만, 경험 상 Airflow로 데이터를 재처리하는 것보다
DB 환경에서 Raw 테이블을 직접 땡겨와서 Summary 테이블을 UPDATE
해주는 방식이 더 빨랐기 때문에 Raw 테이블을 쌓았습니다.
또한 실제 집계한 지표 값이 맞는지 Raw 데이터를 직접 요청하는 경우도 있기 때문에 검증 차원에서 Raw 테이블을 쌓는 것이 좋습니다.
Raw 테이블을 쌓은 후 Summary 테이블에 잘못된 집계 값을 수정하고자 한다면 JOIN
과 UPDATE
문으로도 데이터를 수정할 수 있습니다.
예를 들어,
#summary_table
)과 Raw 테이블 (#raw_table
)을 집계한 결과를 INNER JOIN
하고UPDATE
해주면 됩니다.-- SQL Server 쿼리 기준UPDATE ASET A.id_cnt = B.id_cntFROM #summary_table AINNER JOIN (SELECT sdate, COUNT(DISTINCT id) as id_cntFROM #raw_tableGROUP BY sdate, id) BON A.sdate = B.sdateWHERE A.sdate >= '2022-09-01'
WIDE와 LONG 테이블의 차이를 아시나요?
예를 들어, 한 날짜에 보고 싶은 지표가 3개라면 WIDE 테이블은 행이 1개면 되지만, LONG 테이블은 행이 3개인 셈입니다.
얼핏보면 WIDE 테이블이 더 직관적이고 태블로에서 구현하고 싶은 최종 형태이기 때문에 더 좋아보일 수 있습니다.
그러나, 테이블의 행을 늘리는 것은 쉽지만 열을 늘리는 것은 어렵습니다. 테이블에 새로운 열을 추가한다는 것은 스키마가 변경되는 작업이기 때문이죠. 따라서 WIDE 테이블로 Summary 테이블을 말고 나서 새로운 지표가 추가될 경우, 테이블 구조를 수동으로 변경해주어야 하기 때문에 공수가 많이 듭니다.
또한, 태블로에서 구현 할 때에도 WIDE 형태라면 “열” 부분에 원하는 지표들을 일일이 드래그해서 넣어줘야하는 공수가 있는 반면,
LONG 형태라면 “열” 부분에 지표 명 컬럼 (위 예시에서는 Variable
)만 넣어주면 되기 때문에 지표가 10개 이상 있다면 LONG 이 훨씬 더 이득입니다 :)
이와 더불어 대시보드에서 일/주/월 지표를 구현할 때도 테이블을 3개 만드는 것보단 1개 안에 일/주/월을 구분하는 컬럼을 넣어주고 LONG 형태로 쌓는 것이 관리 상 이점이 있습니다.
이 부분은 제가 가장 강조하고 싶은 부분입니다!
예전에는 Summary 테이블에서 서비스를 구분하는 ID 컬럼은 제외하고 서비스 명에 따른 지표 값만 적재하는 경우가 종종 있었습니다.
제 회사의 경우, 여러 게임의 지표를 한 번에 보고 싶은 니즈가 종종 있는데요.
이렇게 게임 ID를 적재하지 않고 게임 명에 따른 지표 값만 적재했을 때의 문제는 게임 명이 변경될 경우, 일일이 업데이트를 쳐줘야 한다는 점입니다.
최근 실제로 이런 경험을 했어서 매우 공수가 많이 드는 작업임을 체감했는데요.
과거의 Summary 테이블은 Dimension 테이블과 조인할 key가 없기 때문에 이전 게임 명 -> 변경할 게임 명을 모두 UPDATE 쳤던 경험이 있습니다.
예를 들어, Summary 테이블에 서비스 ID (serviceid
) 컬럼이 있는데 서비스 명이 변경되어서 소급 적용을 해줘야 하는 경우 아래처럼 한 번만 업데이트를 하면 되는데요.
UPDATE ASET A.servicename = B.servicenameFROM #summary_table AINNER JOIN (SELECT *FROM #dimension_tableWHERE loadingtime >= '2022-09-01' -- 9월 1일 이후에 몇몇 서비스 명이 변경됐다 가정) BON A.serviceid = B.serviceid
서비스 ID 컬럼이 없다면 아래처럼 변경된 서비스 명에 대해 모두 찾고 업데이트를 해줘야 합니다. 넷플릭스에서 특정 드라마에서 띄어쓰기가 없었는데 있는게 정식 명칭이기 때문에 Summary 테이블에도 반영을 해줘야 한다고 가정을 하면 아래처럼 업데이트 구문을 쭉~~ 써줘야 할 것입니다.
UPDATE ASET A.servicename = '작은 아씨들' -- 띄어쓰기 반영FROM #summary_table AWHERE A.servicename = '작은아씨들'UPDATE ASET A.servicename = '오징어 게임 2' -- 띄어쓰기 반영FROM #summary_table AWHERE A.servicename = '오징어게임2'
언뜻 보면 쉬워보이지만 변경되는 서비스 명과 Summary 테이블이 1개일까요? 제 최근 경험을 비춰보면 12개의 서비스 명을 변경했고, 20개 정도의 테이블이 이에 영향을 받았습니다.
즉, 변경되는 서비스 명이 10개, Summary 테이블이 10개라면 100번을 업데이트해주어야 하는 비효율이 있는 것입니다. 또 수동 작업이기 때문에 UPDATE
되는 서비스 명도 잘못 적을 위험도 있습니다.
또한 INNER JOIN`` 대신
LEFT JOIN`을 하는 것이 좋다고 하는 이유는 신규 서비스가 출시되었을 때 즉시 재처리가 가능하기 때문입니다.
신규 서비스가 출시되고 바로 다음 날 지표를 보고 싶은 니즈가 있다고 가정합시다.
그랬을 때 Dimension 테이블과 INNER JOIN
을 한다면, 신규 서비스의 로그가 적재되었음에도 불구하고 Dimension 테이블에서는 없었기 때문에 Summary 테이블에 반영이 되지 않을 것입니다.
이에 비해 LEFT JOIN
을 하고, 게임 명을 서비스 ID와 동일하게 넣는다면 추후 Airflow까지 가지 않고도 데이터를 업데이트해줄 수 있겠죠.
예를 들어 “수리남”이라는 넷플릭스 드라마가 나왔는데 아직 Dimension 테이블이 업데이트되지 않았다고 가정합시다.
그럴 경우 아래처럼 INNER JOIN
을 한다면 “수리남”의 데이터는 며칠간 집계가 안되게 되겠죠.
이를 재처리하려면 Airflow에서 진행을 해주어야 합니다.
SELECT A.*, B.servicename as servicename -- 디멘전 테이블에 없는 건 적재 XFROM #summary_table AINNER JOIN #dimension_table BON A.serviceid = B.serviceid
이에 비해 LEFT JOIN
을 하게 되면 아래처럼 바로 DB 안에서 UPDATE
가 가능하게 됩니다.
SELECT A.*, coalesce(B.servicename, A.serviceid) as servicename -- 디멘전 테이블에 아직 없어도 serviceid로라도 적재FROM #summary_table ALEFT JOIN #dimension_table BON A.serviceid = B.serviceid-- 수리남을 DB에서 바로 업데이트UPDATE ASET A.servicename = '수리남'FROM #summary_table AWHERE A.serviceid = 12345 -- 수리남의 serviceid
사실 업무를 하지 않고 있는 입장에서 이걸 본다면 “무슨 말이야?”라고 치부할 수도 있을 것 같고, 업무를 하는 입장에서는 “걍 하면 되지”라고도 생각하실 수 있습니다. 다만, 이렇게 간단한 일이라도 한 번 정리해놓고 적용하려고 얘쓴다면 재처리하는 순간에 아주 빛을 발할 것으로 생각합니다.
제가 만든 대시보드는 평생 제가 관리합니다. (제 회사만 그런걸까요 ㅎㅎㅎ) 테이블을 마음가는대로 설계하고 나서 재처리할 순간이 온다면 Airflow의 네모 칸들이 채워져가는걸 계속 트랙킹 해야할 수도 있겠죠. 생각보다 새로운 지표가 추가되는 일도, 신규 서비스가 나오는 것도 잦다는 것…을 잊지마시며 두 번째 라떼를 호로록 마셔보시길 바랍니다 :>.