자바스크립트에도 원본 객체와 같은 참조값을 공유하는 얕은 복사와 원본 객체와 같은 참조값을 공유하지않는 깊은 복사가 존재합니다. 먼저 자바스크립트 변수 자료형에는 원시형(Primitive value 또는 Primitive data type) string number bigint boolean undefined symbol null 참조형(Reference value 또는 Reference data type)이 존재합니다. array object function data 얕은 복사는 복사한 변수들끼리 서로를 참조하고 있기 때문에, 하나의 값을 변경하면 전부 다 같이 변경됩니다. 깊은 복사는 복사한 변수들끼리 서로를 참조하지 않기 때문에, 하나의 값을 변경해도 그 변수만 값이 변경됩니다. 참조형 변수를 복사할 때 무심코 얕은 복사를 한 후, 변수 안의 값을 변경하면,원본 변수 포함 모든 참조형변수의 값이 변경되는 원치 않는 상황이 발생할 수도 있습니다. 필요한 상황에 맞춰 복사 방법을 사용하여 원치않는 상황을 피할 수 있도록 합니다. 제일 중요한 건 복사 하고자 하는 값이 참조형이냐 아니냐에 따라 얕은 복사, 깊은 복사가 결정됩니다. let a1 = [0, 1, 2];let a2 = a1;let a3 = [...a1]; 위와 같은 경우 a2는 얕은 복사가 되어 a2의 원소값을 변경하면 a1도 변경됩니다.하지만 a3는 전개 연산자(...)를 통해 원시 자료형의 숫자(Number)들이 전개되어 복사 했기 때문에 깊은 복사가 이루어져, a3의 원소값을 변경해도 a1은 변하지 않습니다. let a1 = [[0], [1], [2]];let a2 = a1;let a3 = [...a1]; 위와 같은 경우 a2는 얕은 복사가 되어 a2의 원소값을 변경하면 a1도 변경됩니다.a3는 전개 연산자(...)를 통해 참조 자료형인 배열(Array)들이 전개되어 복사 했기 때문에 얕은 복사가 이루어져, a3의 원소값을 변경하면 a1도 변합니다. 객체 복사 함수들(concat, slice, assign 등)을 사용하여도 얕은 복사가 이루어집니다. 얕은 복사를 피해 무조건 깊은 복사를 하는 방법을 알아봅시다. 1. 함수 만들기 위에서 얕은 복사와 깊은 복사의 차이를 알게 되었습니다.typeof 를 이용하여 배열 또는 객체 변수를 인자로 받았을 때, 각 원소 값들의 자료형을 typeof로 확인하여 참조형인 경우에는 안쪽에 원시값이 나올때까지 접근하여 복사하는 방식의 함수를 만들어 사용할 수 있습니다.이 방법은 비추천합니다. 2. 외부 라이브러리 사용하기 lodash, fast-copy 등 여러가지 깊은 복사 라이브러리를 사용하는 방식입니다.외부 라이브러리를 사용할 수 있는 환경이시라면 이 방법을 추천합니다. 3. JSON.parse(JSON.stringify()) 참조형 변수의 값을 통째로 문자열로 변경한 후 다시 파싱을 통하여 참조형 변수로 만들기 때문에 깊은 복사가 됩니다.변수의 원소값들이 단순히 원시자료형이라면 문제가 없지만, 만일 원소 값이 참조형 중 function, data 같은 특수한 경우라면 해당 방법을 사용했을 때 오류가 발생할 수 있습니다.간단한 변수를 복사하는데 사용한다면 추천하지만, 일반적으로 추천하지 않는 방법입니다. 4. structuredClone() 원본 변수를 인자로 넣고 반환값으로 복사할 변수에 대입하면 되는 기본 제공 함수입니다.라이브러를 따로 설치하지 않아도 되고, 3번 방식의 단점도 해결되는 아주 좋은 깊은 복사 방법입니다.브라우저의 버전(22년 이후 버전)만 맞는다면 해당 기능을 가장 추천합니다.(참조 url) 자바스크립트의 변수 자료형(원시형, 참조형)과 해당 변수들을 복사했을 때 발생하는 얕은 복사와 깊은 복사에 대해 정리가 잘 된다면 변수 관리할 때 불편함이 없을 것 입니다.
안녕하세요. 1편에서 Firebase SDK 인스턴스의 setDefaultEventParameters() 메소드를 사용하여 앱의 네이티브 영역 태깅시에 모든 이벤트에 동일한 파라미터를 삽입하는 법에 대해 설명했습니다. 앱의 경우 안드로이드는 Activity, iOS는 View 단위로 앱이 실행되지만, 화면이 넘어갈때마다 해당 메소드로 삽입한 파라미터가 유지되는 이유는 Firebase SDK 인스턴스가 싱글톤 패턴의 인스턴스이기 때문이라고 설명드렸습니다. 이번에는 싱글톤 패턴은 무엇이고, Firebase SDK 인스턴스가 싱글톤임을 검증하는 방법까지 설명해보려고 합니다. 싱글톤(Singletion) 패턴 싱글톤 패턴이란 객체 지향 프로그래밍에서 클래스의 인스턴스를 단 하나만 생성하고, 어디서든 그 인스턴스에 접근할 수 있게 하는 디자인 패턴을 말합니다. 일반적인 클래스는 클래스의 생성자로 인스턴스를 생성하고, 그 인스턴스를 통해서 해당 클래스에 정의된 프로퍼티와 메소드를 사용 가능합니다. 싱글톤 패턴은 이러한 방식으로 인스턴스를 단 하나만 생성하도록 하는 디자인 패턴입니다. 해당 코드에서 singletonObject는 단일 객체를 저장하기 위한 정적 참조 변수이고, static 변수이기 때문에 클래스의 모든 인스턴스가 공유하게 되는데 이때 인스턴스가 단 한개이므로 이 변수를 통해 해당 인스턴스에 접근 혹은 사용 가능하게 되는것입니다. 따라서 Firebase SDK의 클래스가 싱글톤 클래스이므로, 해당 클래스의 인스턴스를 통해 setDefaultEventParameters() 함수를 사용하면 단일 인스턴스에 접근하게 되며, 해당 인스턴스는 전역변수이기 때문에 앱의 어디 부분에서든 사용이 가능하며 내용이 변하지 않는것입니다. Firebase SDK의 인스턴스 싱글턴 검증 그렇다면 Firebase SDK의 인스턴스가 싱글턴 인스턴스인것은 어떻게 알 수 있을까요? 바로 메모리의 주소값을 통해 알수 있습니다. 코드에서 생성되는 모든 변수와 인스턴스는 일시적이든 영구적이든 메모리에 저장되고, 메모리의 주소값을 가지게 됩니다. 그런데 싱글톤 클래스는 인스턴스를 하나만 만들기 때문에, 인스턴스를 여러개 생성해도 모두 하나의 인스턴스를 참조하게 됩니다. 따라서 따라서 변수를 여러개 만들고 그 변수들의 주소를 확인해서 모두 주소가 같다면, 해당 인스턴스는 싱글톤 클래스의 인스턴스인것을 검증할 수 있습니다. FirebaseAnalytics 클래스로 만든 3개의 인스턴스가 모두 같은 주소 값을 가지고 있으므로, 이 클래스는 싱글톤 클래스임을 알 수 있습니다. 감사합니다.
[빅쿼리] BigQuery커뮤니티입니다. BigQuery관련 정보를 공유 해 주세요! 안녕하세요. 이번 글에서는 지난번에 이어서 session_traffic_source_last_click에 어떠한 값들이 있는지 확인을 해볼건데요. session_traffic_source_last_click에는 크게 2가지의 지표가 있다고 보시면됩니다. manual_campaign과 google_ads_campaign 이죠. google_ads_campaign에 대해서 먼저 설명하자면 지난 번 글에서 설명드린 것처럼 구글애즈에서 오토태깅한 값에 대해서 확인할 수 있는 컬럼 값들이에요. 한번 어떤 값들이 있는지 확인해볼까요? 위 사진에서 보이는 것처럼 customer_id와 account_name 그리고 campaign, ad_group에 관한 전반적인 값들을 확인할 수 있죠. 조금 더 활용을 하게된다면 여기서 추출한 데이터를 통해서 Google Ads API를 통해 구글애즈에 관한 더 자세한 데이터까지 확인을 할 수 있겠죠? 하지만 이번 시리즈에서는 GA4에 관한 글이므로 Google Ads에 대해서는 크게 다루지 않고 이정도까지만 말씀드리고 넘어가도록 하겠습니다. 여기서 저희가 확인할 값은 campaign_name 인데요. 이 값은 이전 글에서 봤던 "Session campaign" , 즉 utm에 입력한 campaign값이 아닌 내가 실제로 구글애즈에서 실행했던 camapign 값을 확인할 수 있습니다. 광고성과를 확인하고 싶을 때 좋겠죠? 이번 글에서는 session_traffic_source_last_click의 google_ads_camapign에 대해서 확인해봤습니다. 다음 글에서는 manual_campaign에 대해서 확인해보도록 하겠습니다.
[빅쿼리] BigQuery커뮤니티입니다. BigQuery관련 정보를 공유 해 주세요! Google Analytics4는 BigQuery에 2024년 7월17일부터 빅쿼리에 새로운 컬럼을 추가했습니다. session_traffic_source_last_click 인데요. 이번 글에서는 해당 값을 사용하게되면 어떤 부분에서 좋은 변화가 있었는지 알아보려고합니다. 이 업데이트로 인해 기존에는 세션 범위의 트래픽 소스 정보를 기존보다 더 쉽고 정확하게 추출 할 수 있게되었습니다. 해당 값들을 보기 전에 ga4에 있는 세션범위 트래픽정보부터 확인해보겠습니다. GA4 탐색 분석에 가서 확인을 해보면 아래처럼 "manual" 이라는 것이 포함된 트래픽 디멘션과 포함되지 않은 디멘션, 2개의 종류로 구분됩니다. 해당 디멘션을 쉽게 설명하자면 manual이 포함된 디멘션은 사용자가 유입될 때 들어온 페이지의 url에 있는 utm값들을 확인할 수 있는 값 이고, manual이 포함되지 않은 디멘션은 utm이 붙어 있어도 광고플랫폼에서 오토태깅된 값들을 우선적으로 확인할 수 있습니다. 예를 들어 utm에 있는 소스 / 매체 값은 "youtube / display" 이였지만, 실제로 구글애즈에서 오토태깅된 소스 / 매체 값은 "google / cpc" 일 경우 위 사진처럼 보일 수 있는 것이죠. 이것에 대해서 설명을 드리는 이유는 기존 빅쿼리에서는 구글애즈에서 오토태깅된 캠페인이름 같은 값을 빅쿼리에서는 확인할 수 없고 오로지 utm에서 수집된 값만 확인할 수 있었는데요. 이제는 session_traffic_source_last_click에 구글애즈에 관한 값들이 추가가 되면서 ga4와 동일하게 값을 확인할 수 있습니다. 이번 글에서는 session_traffic_source_last_click을 사용하게되면서 좋아진 부분들에 대해 알아보았는데, 다음 글에서는 session_traffic_source_last_click에는 무슨 값들이 있는지 살펴보도록 하겠습니다. 감사합니다.
GA4에서는 UA와 달리 360버전이 아니더라도 BigQuery를 연동할 수 있습니다. 또한 GA4만으로 Looker Studio등을 사용해 대시보드를 만들었을 때 여러 제한 사항이 있습니다. 그러다보니 BigQuery의 사용량이 자연스럽게 많아진 상태인데요 이 때 가장 큰 문제가 GA4 BigQuery와 GA4 콘솔간의 세션 수 차이입니다. UA때는 GA와 BigQuery의 세션 숫자가 정확하게 맞아 떨어졌던 반면 GA4에서는 세션 수가 무조건 차이가 발생하는 상태인데요 그 이유에 대해 알아보겠습니다. 결론부터 말하자면 GA4 BigQuery가 더 정확한 데이터입니다. GA4의 세션 수를 계산할 때 정확한 세션수를 계산하기 위한 충분한 시간과 리소스가 없기 때문에 더 효율적인 계산 방법(HyperLogLog++ 알고리즘 등)을 적용하여 세션 수를 계산합니다. 하지만 GA4 BigQuery는 테이블이 만들어지기까지 충분한 시간과 리소스가 있기 때문에 더 정확한 값이 집계됩니다. 자세한 내용은 다음 링크를 확인해주세요 -> 링크
안녕하세요, 앱을 태깅할때 모든 이벤트에 동일한 파라미터를 삽입하는 방법에 대해 스터디한 내용을 작성해보려 합니다. 우선 웹 GTM에서는 구성 태그를 통하여 이와 같은 작업을 간편하게 수행할 수 있습니다. 구성 태그의 [공유된 이벤트 설정] 항목의 [이벤트 설정 변수]에 [Google 태그: 이벤트 설정] 유형의 변수를 넣으면, 모든 이벤트에 해당 파라미터들이 삽입되게 됩니다. 또한 이러한 구성태그를 사용하지 않는 앱 하이브리드 웹뷰의 GTM에서는 자바스크립트 탬플릿을 사용하게 되는데, 해당 탬플릿의 변수를 사용하여 똑같은 작업을 수행할 수 있습니다. 앱의 하이브리드 웹뷰 영역이 아닌 네이티브 영역에서는 구조적으로 GTM을 사용하지 않고, Firebase SDK 인스턴스의 메소드인 logEvent()를 사용하여 앱 프로젝트(혹은 다른 개발 환경)내에서 코딩을 통해 태깅하게 됩니다. 이때에는 Firebase SDK 인스턴스가 제공하는 setDefaultEventParameters() 메소드를 사용하여 모든 이벤트에 동일한 파라미터를 삽입할 수 있습니다. (Android Studio 환경의 Kotlin 언어입니다.) FirebaseAnalytics 객체는 Firebase SDK가 제공하는 싱글톤 변수를 import해서 사용하는 것이고, 싱글톤 객체는 애플리케이션이 실행되는 동안 전역 상태에서 접근이 가능하므로 setDefaultEventParameters 함수를 한번만 사용하더라도 앱의 실행 내내 이벤트에 파라미터가 추가되는것을 확인 할 수 있습니다. 2편에서 싱글톤 패턴 및 FirebaseAnalytics 객체가 싱글톤임을 검증하는 내용을 작성하도록 하겠습니다. 혼자서 스터디 한 내용이라 틀린 부분이 있을수도 있습니다. 댓글로 알려주시면 감사하겠습니다! 감사합니다 :)
사용자는 다양한 기기와 브라우저를 교차 사용하며 웹사이트 방문하는 경우가 많습니다. 예를 들어 출근 길에 제품 검색을 통해 앱에 방문하고, 점심시간 회사에서 컴퓨터를 사용하여 제품에 대해 자세히 조사하며, 퇴근 후 집에서 핸드폰으로 구매합니다. 이러한 각 활동은 별도의 세션으로 구분되지만, GA4에서는 사용자 여정을 통합하여 하나의 사용자로 측정할 수 있습니다. GA4가 사용자를 식별하는 방법은 크게 4가지로 나뉩니다. 1. 유저 ID 유저 ID는 특정 사용자를 식별할 수 있는 고유 식별자입니다. 유저 ID를 통해 여러 기기나 브라우저에서 활동한 사용자를 동일인임을 식별할 수 있고, 이러한 활동들을 통합하여 하나의 사용자 여정으로 확인할 수 있도록 해줍니다. 쉽게 말해 같은 유저 ID로 로그인된 상태에서 아이폰-앱, 웹-크롬, 아이폰-사파리를 사용해 방문하였다면, 유저 ID를 통해 한 명의 사용자 행동으로 식별할 수 있다는 것입니다. 이때 유저 ID는 개인정보보호를 위해 개인을 식별할 수 없도록 암호화된 ID를 활용합니다. 2. 구글 신호 데이터 구글 신호 데이터는 구글이 직접 수집하는 사용자의 활동 데이터입니다. 광고 개인 최적화에 동의하고 구글 ID로 로그인된 상태에서 여러 기기나 브라우저로 크롬, 유튜브 같은 구글 서비스를 사용할 때 검색 내용, 방문한 웹사이트, 시청한 동영상 등의 활동 정보를 구글 계정에 저장하여 사용자를 식별합니다. 만약 핸드폰, 태블릿, 데스크톱 모두 같은 구글 ID로 로그인 되어 활동하였다면, GA4에서는 1명의 사용자로 인식합니다. 구글 신호 데이터를 통해 인구통계, 관심분야에 대한 데이터를 얻을 수 있지만, 익명으로 수집되기 때문에 특정 사용자가 어떤 식별자를 가졌는지 확인이 불가능하며, iOS 14 이상 버전의 기기는 지원하지 않습니다. 3. 기기 ID(=CID) 기기 ID는 브라우저/기기 별로 부여되는 쿠키값입니다. 웹의 경우 클라이언트 ID, 앱의 경우 앱 인스턴스 ID로 구분됩니다. 일반적으로 GA4는 클라이언트 ID를 기반으로 사용자를 식별합니다. 웹사이트에 GA 코드 삽입 후 처음 방문했을 때 사용자에게 GA 쿠키가 생성되고, 쿠키에는 임의의 클라이언트 ID가 할당되는데요. CID 확인 방법 CID를 확인하는 방법은 개발자도구 – Application 탭 – Coockies – ‘_ga’ 값 중, CID는 1175402462 입니다. 1702863965는 세션을 식별하기 위한 타임스탬프 값이라고 보시면 됩니다. 쿠키는 기기와 브라우저별로 생성되기 때문에 클라이언트 ID 기준으로 사용자를 식별할 때 같은 기기나 브라우저로 접속한다면 GA4는 1명의 사용자로 인식하고, 다른 기기나 브라우저로 접속한다면 다른 유저로 각각 카운트합니다. 4. 모델링 위의 3가지의 식별자로 유저를 구분할 수 없는 경우, GA에서 자체 머신러닝을 거친 후 사용자를 식별합니다.
GA4 이벤트를 태깅하기 위해서는 이벤트가 자동으로 수집되는지, 아니면 직접 수집을 해야하는지를 이벤트 유형을 정확히 파악할 필요가 있습니다. 이벤트는 크게 4가지가 있는데, 그 중 향상된 측정 이벤트에 대해 알아보겠습니다. GA의 향상된 측정 이벤트는 별도의 개발 작업 없이도 웹사이트나 앱에서 발생하는 일부 이벤트를 자동으로 추적할 수 있도록 도와줍니다. 향상된 측정 기능을 통해 자동으로 추적할 수 있는 이벤트들은 아래와 같습니다. 1. 페이지 조회(page_view) : 페이지가 로드되거나 활성 사이트에서 브라우저 기록 상태가 바뀔 때. 2. 스크롤(scroll) : 각 페이지에서 처음 하단에 도달할 때(세로 기준 페이지의 90% 표시될 때) 3. 외부 연결 링크 클릭(click) : 현재 도메인에서 외부로 나가는 링크를 클릭할 때 4. 사이트 검색(view_search_results) : 검색결과 페이지가 표시될 때마다(URL 쿼리 매개변수가 있을 경우 검색결과 페이지가 표시된 것으로 간주) 5. 동영상 참여(video_start, video_progress, video_complete) : 삽입된 유튜브 동영상에서 JS API 지원이 사용이 설정된 경우 6. 파일 다운로드(file_download) : 문서, 텍스트, PPT, 비디오, 오디오 등의 파일로 연결되는 링크를 클릭할 때 7. 양식 상호작용(form_start, form_submit) : 양식(폼)을 처음 상호작용하고, 해당 양식을 제출할 때 GA4 향상된 측정 향상된 측정 이벤트 유형 향상된 측정 설정은 데이터 스트림에서 가능하고, 페이지 조회를 제외한 6개의 이벤트를 각각 자동으로 수집할 것인지 말 것인지를 세팅할 수 있습니다. 이번 글에서는 스크롤 이벤트를 향상된 측정 이벤트가 아닌 GTM으로 따로 태깅을 해보겠습니다. 일반적으로 향상된 측정 기능을 활용하여 스크롤 이벤트를 자동 수집할 경우 다른 이벤트와 다르게 수집되는 매개변수가 없고, 스크롤 뎁스가 90%일 때만 이벤트가 발생되기 때문에, 25%, 50%, 75% 같은 스크롤 뎁스 매개변수와 함께 추적하기 위해서는 GTM을 사용하여 맞춤 이벤트(Custom Event)를 생성해야 합니다. 스크롤 이벤트를 GTM에서 설정하는 과정은 다음과 같습니다. Step 1. GA4 데이터 스트림 내 향상된 측정에서 스크롤 이벤트 항목 비활성화하여 스크롤 이벤트가 자동 수집되지 않도록 해야 합니다. Step 2. GTM에서 ‘스크롤 깊이 트리거’를 생성합니다. 스크롤 이벤트를 언제 발생시킬 것인지 트리거 유형 중 스크롤 깊이를 선택한 후, 비율에 어느 정도까지 스크롤했는지 추적하고자 하는 값을 입력하시면 됩니다. 25%, 50%, 75%, 95%인 경우에 스크롤 이벤트가 발생하도록 트리거 설정을 하였습니다. Step 3. 스크롤 깊이 트리거를 활용하여 ‘스크롤 이벤트 태그’를 생성합니다. 참고로 스크롤 관련 변수는 별도로 생성할 필요 없이 GTM에서 기본으로 제공합니다. 1. Scroll Depth Threshlod : 사용자가 페이지를 스크롤했을 때, 스크롤 깊이를 기준으로 정의된 임계값입니다.일반적으로 25%, 50%, 75%, 100% 같은 값을 입력할 수 있고, 50이라는 의미는 사용자가 페이지의 50%를 스크롤했다는 것을 나타냅니다. 2. Scroll Depth Units : 스크롤 깊이를 측정하는 단위입니다. 기본적으로 percent(%)로 설정되며, 픽셀 단위로도 표시할 수 있습니다. 3. Scroll Direction : 사용자가 페이지를 스크롤할 때의 방향입니다. 일반적으로는 수직 스크롤이 많이 사용되지만, 경우에 따라 수평 스크롤도 측정할 수 있습니다. 스크롤 태그 내 이벤트 매개변수에서는 percent_scrolled이라는 이름으로 Scroll Depth Threshlod 값을 수집하겠습니다. 따라서 사용자가 페이지에서 세로 스크롤 시, 어디까지 스크롤했는지 그 값을 scroll 이벤트를 통해 확인할 수 있습니다. 이렇게 스크롤 이벤트 태깅을 완료한 뒤, GTM 프리뷰를 통해 스크롤 이벤트가 잘 수집되고 있는지 테스트 할 수 있습니다. 스크롤 이벤트와 유사하게 사이트 검색, 동영상 참여, 파일 다운로드, 양식 상호작용 등의 이벤트도 GTM에서 제공하는 변수를 잘 활용하여 맞춤 수집할 수 있으니 업무에 참고하시면 좋을 것 같습니다.
안녕하세요 :) 앞 글에서 빅쿼리에 있는 독특한 문법이라 할 수 있는 Array / Struct / Unnest 에 대해서 알아보았었는데요, <이전글> 1. 빅쿼리의 STUCT 이란? http://googleanalytics360.com/board/view.php?bo_table=googleanalytics&wr_id=95&page= 2. 빅쿼리의 ARRAY & UNNEST이란? http://googleanalytics360.com/board/view.php?bo_table=googleanalytics&wr_id=94&page= 이번 글에서는 해당 문법들을 실제 GA4 데이터에서 어떤식으로 활용하는지 알아보겠습니다. <목차> 1. Record 의 데이터 조회하기 2. Record + Repeated 데이터 조회하기 3. Record + Repeated 데이터 안에 또 다른 Record 가 있는 경우의 데이터 조회하기 [빅쿼리의 데이터 유형] 우선 GA4 데이터로 가기 전에, 빅쿼리의 데이터 유형들을 봐볼게요. 도움말에서 확인할 수있는 빅쿼리의 스키마 및 데이터 유형은 다음과 같습니다. 뭔가 알아야하는 항목들이 여러개 있어보이지만 정말 자주 쓰이는 것들은 아마 STRING (문자형) , INT64 (정수형) , DATE 날짜 정도 일 것 같아요. 여기서 추가로 저희는 STRUCT (RECORD) 과 ARRAY (REPEATED) 도 알아야겠죠? 아래 이미지는 빅쿼리에 GA4를 연동했을 때 수집되는 데이터들의 스키마입니다. 우측의 유형과 모드에 집중해볼게요. RECORD 면 Struct REPEATED 면 Array 이 두 가지는 꼭 기억해주세요! [ROCORD 데이터를 가져오는 방법] 한번 저희가 device 라는 정보를 빅쿼리를 통해서 조회해보겠습니다. SQL 에서 정보를 가져올 때 가장 기본적인게 SELECT '항목' FROM '데이터 소스' 형식이죠, device 를 아래와 같이 select 하게 되면 어떻게 될까요? SELECT device FROM `데이터 소스' 분명 select 된 것은 device 하나인데 갑자기 열이 엄청 많이 조회되는데요, 이처럼 여러 열이 출력된 이유는 유형이 'Record' 인 항목이 Struct 이기 때문이에요. (struct 은 테이블 안에 또 다른 테이블, 열의 형태로 존재하는 것) 이렇게 device 왼쪽의 세모를 눌러서 펼쳐보게되면 숨겨있던 이름들이 나오게 됩니다. 만약 'device' struct 안에 있는 'category' 정보와 'operating_system' 만 가져오고 싶다면 어떻게 할 수 있을까요? 바로 '.' 을 이용하는 것 입니다. 이렇게 device.category '.' 을 활용했더니 정상적으로 원하는 컬럼을 select 해올 수 있죠. SELECT device.category, device.operating_system FROM `데이터소스' [Record 와 Repeated 가 같이 있는 경우] 위에서는 Record 만 있는 경우였는데, 실제로 ga4 에서 더 많이 쓰는 값들은 Record 와 Repeated 가 같이 있어요. items 로 한번 같이 알아보겠습니다. 우선 items 라는 값들은 어떤 구조일지 생각해볼게요. 한 회사에서 실제로 어떤 상품들이 팔리고 있는지 확인하려면 상품의 이름 ,상품의 가격 , 상품의 옵션 등 각 상품에 대한 정보가 필요할것이고, 사람들은 1개만 구매하기보다 여러 상품을 구매하기 때문에 그 여러 상품들에 대해서 수집을 할 수있어야겠죠? 엑셀로 표현해보면 살짝 이런 느낌이에요. 그럼 이전 글에서 설명할 때, ARRAY 는 엑셀에서 '병합셀' 과 같다고 표현했는데 지금 딱 그 형식인게 보이실까요? 스크립트로 표현하면 이렇게 ITEMS 라는 ARRAY 안에 아이템 정보들을 여러개 넣을 수 있게 됩니다. ITEMS 가 REPEATED 였으니 ARRAY 는 이렇게 쓰인걸 알았고, RECORD 도 있었는데... 그럼 ITEMS 에서 STRUCT 은 어디에 쓰였을까요? 아까 위에서 device 와 동일하게 , 아래와 같이 items 를 select 해보면 SELECT items FROM `데이터 소스' 아까 DEVICE 처럼 너무 많은 열들이 SELECT 되었죠, 이를 통해 ITEMS 도 STRUCT 구조로 이루어져 있다는 것도 확인할 수 있습니다. 그럼 과연 ITEMS 도 아까 DEVICE 예시처럼 '.' 을 사용하면 원하는 열만 가져올 수 있을까요? SELECT items.item_name, items.price FROM `데이터 소스' '.' 을 동일하게 썼는데 위의 쿼리는 실행되지 않고, 다음과 같은 오류가 확인됩니다. Cannot access field item_name on a value with type ARRAY
이번 글에서는 GA4를 사용하다보면 마주치는 데이터 샘플링, 기준점(Threshold) 적용, 카디널리티에 대해 알아보겠습니다. 1. 데이터 샘플링(Data Sampling) 탐색 보고서에서 데이터를 분석하면 아래와 같은 메시지를 볼 수 있습니다. ‘이 보고서는 이용 가능한 데이터의 48.3%를 기반으로 합니다.’ 샘플링이라는 단어에서 알 수 있듯이 전체 중 일부를 사용한다는 것을 유추해볼 수 있는데요. 데이터의 양이 매우 많을 때 탐색 보고서의 속도와 성능을 최적화하기 위해 전체 데이터의 일부만 사용하는 데이터 샘플링이 적용되었다는 것을 말해줍니다. GA4에서는 1,000만개의 이벤트 수를 기반으로 탐색 보고서에서 샘플링이 적용되어, 해당 할당량을 초과하는 경우 전체 데이터 대신 샘플 데이터를 사용하여 전체를 대표하는 보고서를 생성합니다. 데이터가 샘플링 될 때, 데이터 품질 아이콘을 통해 아래와 같은 옵션을 선택할 수 있습니다. - 세부 결과(More detailed results) : 샘플 크기를 최대한 크게 설정하여 전체 데이터를 가장 잘 보여주는 결과를 제공함 - 빠른 결과(Faster results) : 샘플링 크기를 작게 설정하여 결과를 빠르게 제공하는 데에 초점 기본적으로 GA에서는 빠른 결과(Faster results)를 적용되지만, 세부 결과(More detailed results)를 선택하면 더 많은 양의 데이터를 사용하여 조금 더 정확한 값을 확인하실 수 있습니다. 데이터 샘플링을 해결할 수 있는 방법은 다음과 같습니다. - 데이터 조회기간을 줄여서 데이터 모집단의 크기를 줄이기 - 향상된 측정 끄기 - 카디널리티가 높은 맞춤 측정기준 삭제 (3. 카디널리티 참고) 2. 기준점(Threshold) 적용 데이터 기준점은 보고서에서 사용자의 성별, 연령, 지역, 관심분야 등과 같이 특정 사용자를 유추하지 못하도록 특정 데이터를 제외시키는 것입니다. 이는 웹사이트에 방문한 사용자의 개인정보보호를 위해 생긴 기능으로, 기준점이 적용되면 일부 데이터를 확인할 수 없게 됩니다. 기준점 적용은 데이터 샘플링과는 반대로 데이터 양이 너무 적은 경우 발생합니다. 보고서에 인구통계 정보가 포함된 경우 전체 사용자 수가 충분하지 않으면 사용자를 구별할 수 있는 것을 막기 위해 특정 데이터가 제외되는 것입니다. 기준점이 적용되면 일종의 샘플링과 유사하게 정확하지 않은 데이터가 집계될 수 있습니다. 기준점 적용을 해결할 수 있는 방법은 다음과 같습니다. - (조회기간 내 사용자 혹은 이벤트 수가 적은 경우) 조회기간 늘려서 데이터 양 늘리기 - Google 신호 데이터 비활성화 >> Google 신호 데이터는 인구 통계 정보나 구글 애즈에서 잠재고객을 활용한 리마케팅을 할 때 필요한데, 해당 데이터가 필요하다면 활성화시키지 않는 것이 좋습니다. - 보고 ID를 ‘기기 기반’으로 설정 >> 기본적으로 보고 ID는 ‘혼합됨’으로 설정되어 있는데, 보고 ID를 ‘기기 기반’으로 변경하면 기기 ID를 기반으로 사용자를 식별하기 때문에 다른 기기로 사이트를 방문할 경우 다른 사용자로 식별하게 됩니다. (이로 인해 정확한 사용자 수를 측정하는 데 한계가 있는 옵션) 3. 카디널리티(Cardinarlity) 카디널리티는 데이터 측정기준이 가지는 고유한 값의 수를 의미합니다. GA4는 카디널리티가 높은 맞춤 측정기준을 추가하는 것을 권장하지 않는데요. 예를 들어, payment_type(결제 방식) 이라는 측정기준에 ‘N Pay’, ‘계좌이체’, ‘신용카드’, ‘무통장입금’ 4가지의 값이 있다면, 이 경우에 카디널리티는 4입니다. 이정도는 카디널리티가 낮다고 말할 수 있지만, user_id나 item_name과 같은 측정기준은 수만 개 이상의 값을 가지고 있을 수 있기 때문에 카디널리티가 높을 것으로 예상됩니다. 카디널리티가 높다고 말할 수 있는 기준은 측정기준의 고유한 값이 일일 500개를 초과하는 경우입니다. 카디널리티가 높으면 저장해야 할 값이 많기 때문에 속도나 성능 측면에서 부정적인 영향을 미칠 수 있습니다. 보고서의 행 개수가 많아지다보니 행 한도(500개 이상)에 도달하여 일부 데이터가 (other) 행에 분류되고, 카디널리티가 25,000개 이상이라면 데이터 샘플링이 발생하는 점 주의하시길 바랍니다. 단, 탐색 보고서에서는 축약 행이 발생하지 않으므로, GA4 기본 보고서에서 (other)라고 표시된 행이 있다면 탐색 보고서에서 데이터를 확인하시는 것을 권장드립니다. (other) 행이 생기는 예시 카디널리티를 해결할 수 있는 방법은 다음과 같습니다. - 카디널리티가 높은 맞춤 측정기준을 삭제하거나 자제 - user_id는 맞춤 측정기준 대신 User-ID 기능 사용 - 맞춤 측정기준 생성 전에 가능한 기존 측정기준 사용 지금까지 알아본 데이터 샘플링, 기준점 적용, 카디널리티 3가지를 잘 이해하여 데이터 분석을 정확하고 효율적으로 해보시길 바랍니다.
이번 글은 빅쿼리의 Struct 에 대해 정리해보도록 하겠습니다. https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type [빅쿼리의 Struct 이란?] Struct 은 빅쿼리 UI 상에서는 Record 라고 표현됩니다. 이렇게 유형이 'Record'인 항목들은 공통점이 하나 있는데요, 바로 쿼리 결과를 봤을 때, 열의 이름이 aaa.bbb 형식이란 점 입니다. 간단하게 보자면 struct 구조를 통해 전체 테이블 안에 있는 특정 열들이 한 묶음으로 구분될 수 있게 됩니다. [Struct 를 만드는 방법] Struct 을 만들 수 있는 방법은 여러가지가 있습니다. [1] 소괄호 SELECT ('육식동물','초식동물','잡식동물') animal [2] STRUCT <타입> SELECT STRUCT<STRING, INT64,STRING>('정뿌시',26,'직장인') PERSONAL_INFORMATION 아래처럼 TYPE 앞에서 각 열들에 대한 이름을 정의해줄 수 있습니다. [3] ARRAY (SELECT AS STRUCT) 추가로, ARRAY 와 STRUCT 은 혼합하여서도 사용이 가능합니다. 그 중 ARRRAY 안에 STRUCT 있는 형태는 ARRAY (SELECT AS STRUCT ~ ) 으로 쓸 수 있습니다. 이렇게 ARRAY 안에 STRUCT 이 3개 존재하는 거죠. SELECT ARRAY ( SELECT AS STRUCT '정뿌시' AS NAME ,26 AS AGE ,'컨설턴트' AS JOB UNION ALL SELECT AS STRUCT '이요니' AS NAME ,26 AS AGE ,'공무원' AS JOB UNION ALL SELECT AS STRUCT '오현디' AS NAME ,26 AS AGE ,'방송PD' AS JOB ) AS `GROUP1_INFO` 정리하면, STRUCT 은 빅쿼리의 RECORD 유형으로 테이블 안에서 또 다른 테이블을 구성하는 것과 유사하다고 이해할 수 있다. (개념상) ARRAY 가 대괄호였다면 STRUCT은 소괄호로 만들 수 있고, ARRAY 안에도 STRUCT 을 만들 수 있다. 로 정리하겠습니다. 그럼 오늘 글은 여기에서 마치겠습니다 :) 소통할 부분 있다면 언제든 댓글 남겨주세요. 감사합니다!
오늘은 빅쿼리에서 사용하는 Array , 그리고 Array 하면 빠질 수 없는 Unnest 에 대해 알아보겠습니다 [Array 란 무엇일까] 빅쿼리 UI 상으로는 'Repeated' 라고 표현되는 것이 바로 Array 인데요, 예를 들어서 간단하게 아래 같은 구조의 도표가 있다고 생각해볼게요. 사람마다의 취미와 직업이 써져 있죠. 이렇게 각 1 사람당 1개의 취미 , 1개의 직업만 있다면 array 구조는 불필요합니다. 근데 취미가 여러개라면 ? 아래 이미지처럼 name 과 job 이 있던 행이 hobby가 늘어남에 따라 행이 5개였던게 8개로 늘어났죠. 지금은 예시이다 보니 5개 행이 8개로 늘었지만 수가 훨씬 많다면 공간 차지면에서 비효율적일수도 있고, 이후에 데이터를 추출할 때도, hobby 가 여러개 있다는걸 모르는 사람이라면 원하는 것과 다른 데이터를 가지게 될 수 도 있죠. 이럴 때 활용할 수 있는게 array 입니다. 아래 이미지를 보시면 array를 사용하면 hobby 가 여러개 있더라도 name 1명에게만 행이 생성되었습니다. 이렇게 한 개의 행에 특정 데이터들이 여러개 저장되면서, 데이터가 세로로 저장되 것이 Array 입니다. 참고로 빅쿼리에서 JSON 형식으로 아래와 같이 확인할 수 있습니다. [Array 를 만드는 방법] Array 를 만드는 방법은 여러가지가 있습니다. [1] 대괄호 select 'HAYEON' name , ['십자수', '헬스장'] hobby , '공무원' job [2] ' Array <타입>' + [대괄호] select 'HAYEON' name , Array ['십자수', '헬스장'] hobby , '공무원' job [3] Generate 함수 GENERATE_ARRAY (시작 숫자 , 마지막 숫자 , 간격) select GENERATE_ARRAY(2,20,4) GENERATE_ARRAY GENERATE_DATE_ARRAY (시작 날짜 ,마지막 날짜 ,간격) select GENERATE_DATE_ARRAY('2024-07-01','2024-08-01',interval 1 week) GENERATE_DATE_ARRAY [4] Array_agg 사용 ARRAY_AGG with hobby_table as (select '십자수' as hobby union all select '뜨개질' as hobby union all select '클라이밍' as hobby union all select '게임' as hobby union all select '요가' as hobby union all select '영상 제작' as hobby ) select array_agg(hobby) as hobby_array from hobby_table ARRAY_AGG는 나중에 자주 쓰게되는 것 같아서 좀 더 연습해볼게요 :) 위의 예시에서는 array 안에 넣을 항목들을 with 구문에 만들어 두었었는데 만약 저장되어 있는 테이블에서 array_agg 를 써야한다면 어떻게 해야할까요? 가장 처음에 가지고 있던 테이블을 보면 이렇게 되어 있는데, 만약..... 여기 있는 인물들이 직업이 바뀌는 상황이 있었다면? 연도별로 직업이 달라져야하지 않을까요? 또 중간에 취미가 바뀌거나 사라졌을 수도 있겠죠? 한번 아래처럼 표를 수정해보았어요. 2023년에서 2024년이 될 때 Hayeon , Yeji , Hyunji 는 job 에 변화가 있었고, Jiyounh 은 hobby 가 2개에서 1개로 줄었습니다. 이 테이블에 yerar ,name,job 은 group by를 , hobby 와 job엔 array_agg 을 써볼게요 SELECT year, name, job, array_agg(hobby) hobby, FROM `boheetest.SQL_Tableau.example` group by year,name,job 이런식으로 array_agg 를 써줄 수 있습니다 :) [Array 에 있는 값을 가져오는 방법 (1) 배열의 순서로 가져올 때] array 에 있는 값은, 배열의 순서를 지정해서 가져올 수 있습니다. 이때, 숫자를 0부터 셀지 1부터 셀지에 따라 쓰이는 명령어가 다른데 Offset > 0부터 Ordinal > 1부터 입니다. *만약 이 순서에 대해 숫자를 썼는데 그 값이 없었다면 오류가 발생하겠죠? 이런 경우를 대비해선 SAFE_OFFSET, SAFE_ORDINAL 을 사용할 수 있고, 값이 없으면 NULL 로 채워집니다. 아래와 같은 ARRAY 가 있다고 하면 Offset > 0부터/ Ordinal > 1부터니까 array_agg(hobby)[SAFE_OFFSET(0)] as hobby_array > '십자수' array_agg(hobby)[SAFE_OFFSET(1)] as hobby_array > '뜨개질' array_agg(hobby)[SAFE_OFFSET(3)] as hobby_array >'게임' array_agg(hobby)[SAFE_ORDINAL(0)] as hobby_array > 'NULL' array_agg(hobby)[SAFE_ORDINAL(1)] as hobby_array > '십자수' array_agg(hobby)[SAFE_ORDINAL(3)] as hobby_array > '클라이밍' 여기까지 이렇게 array 에 대해 알아보았는데요, array 하면 빠질 수 없는게 바로 unnest 이죠, 이어서 같이 알아보겠습니다! [Array 에 있는 값을 가져오는 방법 (2) Unnest] 위에서 있었던 테이블은 사람마다의 취미와 직업이 있었죠, 그럼 이 중에서 '취미가 게임인 사람' 을 조회하고 싶어서 where 절로 필터를 걸었다면 어떻게 될까요? <오류 발생 화면> No matching signature for operator IN for argument types ARRAY and {STRING} at [13:13] No matching signature for operator = for argument types: ARRAY, STRING. Supported signature: ANY = ANY at [14:7] 아래 쿼리에서는 'in' 을 써도 '=' 를 써도 비슷한 오류가 발생합니다. with table as ( select 'HAYEON' name , Array ['십자수', '헬스장'] hobby , '공무원' job Union all select 'YEJI' name , ['뜨개질','게임'] hobby , '주부' job Union all select 'MINJI' name, ['게임'] hobby , '직장인' job Union all select 'HYUNJI' name , ['필라테스','영상제작'] hobby , '대학생' job Union all select 'JIYOUNG' name , ['요가','클라이밍'] hobby , '직장인' job ) select * from table where hobby in ('게임') 그 이유는 지금 hobby 는 array 에 있는데, '게임' 이라는 string 값과 같은지 다른지조차 비교할 수 없기 때문이에요. 이럴때 필요한 것이 바로 unnest 입니다. unnest 는'평면화'라고도 불리는데요, 뜬금없을 수 있지만 한번 엑셀의 셀 병합을 떠올려볼까요? 좌측이 일반적인 도표이고, 우측은 제가 같은 값들로 셀 병합을 한 상태인데요, 종종 병합된 셀에서 엑셀의 특정 기능을 실행할 때 이런 경고창과 함께 실행이 되지 않았던 경험 한번씩은 있으시죠? 이게 어떻게 보면 unnest 의 필요성 이에요. 현재 셀들이 같은 기준점에서 처리가 불가능한 상태이기 때문에 다 같은 구조로 만들어줘야만 (엑셀에서는 셀의 크기가 동일해야만) 하는 것이죠. 병합된 셀을 다시 기본 형태처럼 , 즉, array 로 묶인 형태 ( = 병합된 셀) 를 푸는 것이 unnest 입니다. [Unnest 사용 시 주의할 점] unnst 를 사용할 때 꼭 유의하셔야 하는 부분은 '행의 갯수가 늘어날 수 있다' 는 점 이에요. 예를 들어서 위에 나온 인물들이 물건을 구매했고, 그 구매한 물건들이 array 형태인 테이블이 있다고 생각해볼게요. 이렇게 보면 각 사람별로 product_price 의 총 합과 total_price 값이 동일하죠? 근데 이 테이블이 만약 unnest 되었다면 아래처럼 total price 가 모든 행마다 붙게 되면서 원치 않는 total_price 값을 얻게될 수도 있어요. 실제로 ga 를 다룰 때 자주 발생하는 이슈이기도 하니 데이터 구조를 잘 파악하면서 사용해야 합니다 :) 그럼 오늘은 여기까지 array 와 unnest 에 대해 알아보았는데요, 다음 글에서는 struct 에 대해 정리해보고, 그 용법들을 ga 데이터에서 활용하는 방법에 대해 공유드리도록 할게요! 함께 소통하면 좋은 부분 있으시면 언제든 댓글 남겨주세요! 감사합니다 :)
오늘은 GA4에서 참여와 이탈에 대해 알아보겠습니다. GA를 사용하시는 분들은 아시겠지만, UA에서 GA4로 전환됨에 따라 이탈의 개념이 달라지고 참여라는 개념이 새롭게 생겼습니다. GA4 이탈을 설명하기 전에, 먼저 UA에서의 이탈 개념을 짚고 넘어가려 합니다. UA에서 이탈이란 사용자가 웹사이트에 방문하여 특정 페이지만 보고 다른 페이지를 이동하지 않은 상태에서 종료된 세션을 의미하고, 이러한 세션이 발생한 비율을 이탈률로 계산합니다. (이탈 세션 수 / 총 세션 수) UA 시절에는 세션 내에서 페이지뷰 혹은 이벤트와 같이 조회가 1번만 발생한 단일 조회 세션을 이탈로 간주했습니다. 예를 들어 2분 동안 특정 상품 상세 페이지에 머물렀다가 나가버리면 이탈한 것으로 집계합니다. 즉, 특정 페이지에서 아무런 상호작용 없이 오래 머무르면 이탈로 쉽게 정의해서, 일반적으로는 이탈률이 높게 나왔습니다. (GA4에서는 이러한 상황을 참여했고 이탈이 아니라고 계산함) GA4에서는 이탈은 오로지 참여의 반대 개념으로만 존재하기 때문에, 참여를 알아야 이탈을 알 수 있습니다. 새롭게 등장한 참여의 정의는 다음과 같고, 조건 중 하나만 만족하더라도 참여에 해당됩니다. 1. 세션 10초 이상 지속 2. 페이지 조회 혹은 화면 조회 2회 이상 발생 3. 전환 이벤트 발생 예를 들어 사용자가 웹사이트 방문 후 9초 동안 페이지를 조회하거나, 페이지뷰가 1회 발생하거나, 구매 이벤트가 발생하지 않는 경우, 즉 참여 세션이 아닌 경우 GA4에서는 이탈로 간주하는 것입니다. 그리고 이러한 세션이 발생한 비율을 참여율로 계산합니다. (참여 세션 수 / 총 세션 수) 따라서 GA4에서 이탈률은 참여하지 않은 세션의 비율을 나타내며, 참여율의 역수입니다. (1 – 참여율 = 이탈률) UA와는 달리 GA4에서는 참여 세션이라는 새로운 측정값을 통해 웹이나 앱에서 적극적으로 상호작용한 세션과 사용자를 보려고 하는 의도를 파악할 있습니다. 한편 GA4에서는 user_engagement 이벤트로 사용자의 참여 정보를 수집하는데요. user_engagement 이벤트는 사용자가 페이지와 상호작용한 후 사이트에서 머문 시간을 측정하기 위해 자동으로 수집됩니다. 아래와 같은 조건일 때 engagement_time_msec 이라는 매개변수가 전송되어 사용자가 웹사이트에 얼마나 오래 머무르고 있는지 측정할 수 있고, engagement 사이의 시차를 밀리초 단위로 계산합니다. 1. 사용자가 앱 화면을 배경으로 이동한 경우 2. 사용자가 웹페이지로부터 포커스를 옮긴 경우 3. 사용자가 앱 화면이나 웹페이지를 벗어난 경우 4. 사이트 또는 앱이 비정상으로 종료된 경우 GA 고객센터에 나와있는 예시를 살펴보겠습니다. engagement_time_msec 매개변수가 수집되려면 이전 engagement와 이후 engagement가 있어야 하는데요. 스크롤, 다른 페이지로 이동 시 발생하는 이벤트에 따라 붙는 engagement_time_msec 파라미터를 통해 사용자가 사이트에 얼마나 머물렀는지 그 시간을 알 수 있습니다. 첫 번째 홈페이지 방문의 경우(first_visit, session_start) 이전 engagement 정보가 없기 때문에 engagement_time_msec 매개변수가 수집되지 않았음을 확인할 수 있습니다. 참고 1. UA vs GA4 이탈률 의미 변화 https://support.google.com/analytics/answer/11986666#bounce_rate_vs_engagement_rate&zippy=%2C%EC%9D%B4-%EB%8F%84%EC%9B%80%EB%A7%90%EC%97%90%EC%84%9C%EB%8A%94-%EB%8B%A4%EC%9D%8C-%EB%82%B4%EC%9A%A9%EC%9D%84-%EB%8B%A4%EB%A3%B9%EB%8B%88%EB%8B%A4 2. 사용자 참여 발생 시간 https://support.google.com/analytics/answer/11109416?hl=ko&sjid=8087952378343480112-AP
GA4 를 사용한다는 것은 우리 웹 사이트에 방문한 사람들의 행동 양상에 대해 궁금증이 많은 분들일 것 같은데요, "얼마나 많이 들어왔을까" --- (세션수 , 사용자 수 등) "어떻게 들어왔을까" --- (소스 /매체) "어떤 행동을 했을까" --- (이벤트) 오늘은 이 "어떤 행동을 했을까" 와 관련되어서 , 특히 GA4 에 쌓이는 시간 관련 지표인 Timestamp 관련하여 공유해볼까 합니다. [Unix timestamp] 우선 타임 스탬프에 대한 이야기를 짧게 하고 들어갈까해요. 이름 자체로 timestamp 는 '시간 도장' 인데 1970년 1월 1일 00:00:00 UTC 로부터 현재까지의 누적된 초(seconds) 값을 의미해요. 유닉스를 개발한 연구소에서 만든 지표인데 일반인이 보기엔 이렇게 길고 알아보기 힘든 값이죠 '1722783606516394' 그래서 시점 확인이 필요할 때가 있으면 보통은 이렇게 전환기에 값을 넣어서 확인합니다 전환기 예시 링크 : https://time.is/ko/Unix%20time%20converter 저희는 이 유닉스 타임스탬프 형태로 생긴 ga4 의 시간 값들을 알아볼거에요. [유저가 언제 처음 사이트에 들어왔을까 - user_first_touch_timestamp] 만약 유저가 처음으로 사이트에 들어온 시점이 궁금하다면 user 필드에 있는 'user_first_touch_timestamp' 를 사용할 수 있습니다. 출처 : [GA4] Bigquery Export 스키마 : https://support.google.com/analytics/answer/7029846?hl=ko#zippy=%2Cuser 이 부분은 이전 글에서 언급했던 부분과 같이 생각할 수 있는데요, 이전글 : http://googleanalytics360.com/board/view.php?bo_table=googleanalytics&wr_id=90&page= user_pseudo_id 라는 지표가 첫 유입시의 time_stamp 를 활용해서 만들어진 값이라는 점 이죠, 실제 쿼리에서 확인해보면 user_first_touch_timestamp 값이 user_pseudo_id에서 사용되고 있는 것을 확인할 수 있습니다. (*다만, 아래에서 테스트를 해보면서 조금 다른 경우도 있을 수 있다는 걸 공유드릴 예정입니다) [세션은 언제 시작되었을까?] 세션이 시작된 시점은 ga_session_id 라는 지표에 수집되고 있습니다. 쿼리에서 꺼내 써야한다면 (select value.int_value from unnest(event_params) where key = 'ga_session_id') 이렇게 event_params 를 unnest 해주면 됩니다. 궁금증! 그럼, 첫 번째 들어온 세션의 시점(ga_session_id) 은 위에서 언급한 '유저의 첫 방문 시점의 시점인' user_first_touch_timestamp 와 동일하지 않을까? 하는 궁금증이 생겨서 first_visit 이벤트 필터를 걸고 비교를 해보았습니다. 그랬더니 결과는 위의 user_first_touch_timestamp 예시처럼 같은 경우도 있지만, 아래 이미지처럼 미묘하게 다른 경우들도 확인 되었어요. ga_session_id : 1722783605 user_first_touch_timestamp : 1722783606516394 user_pseudo_id:1675675245.1722783605 유닉스 타임 스탬프 변환기에 user_first_touch_timestamp : 1722783606516394 , ga_session_id: 1722783605 값을 넣어보면 1722783606516394 값은 인식을 못해서 '1722783606' 까지 넣었을 때 Mon Aug 05 2024 00:00:06 UTC+0900 (한국 표준시) 라고 나왔고, 1722783605 값은 Mon Aug 05 2024 00:00:05 UTC+0900 (한국 표준시) 라고 나왔습니다. ga_session_id 값이 1초 정도 user_first_touch_timestamp 값 보다 빨리 생성된 것으로 보여요 결론적으로는 아래와 같이 정리하였습니다. "이론상으로는 가장 첫번째 세션이 시작한 시점과 유저의 첫 진입 시점은 동일해야하지만 실제 수집된 값으로는 미세하게 차이가 있을 수 있고, user_pseudo_id 에 쓰인 시간 값은 ga_session_id 의 값을 사용해 생성된다고 하는게 더 맞는 것 같다." [이벤트는 언제 발생한 것일까?] 위에서 나온 timestamp 들은 유저의 첫 방문 (user_first_touch_timestamp) , 세션의 시작 (ga_session_id) 값이였죠 마지막으로 알아볼 것은 event_time_stamp 입니다. ga4 에서 이벤트 옆에 바로 수집되는 값이에요. 해당 값은 각 이벤트마다 수집되고, unnest 도 필요 없이 바로 값을 가져올 수 있습니다. [Timestamp 값 활용하기] 그럼 위에서 배운 값들을 어떤식으로 활용할 수 있을까요? 1. 데이터 검수 > 특정 브라우저에서 이벤트들을 발생시키면서 이벤트 정상 수집 여부를 체크해야할 때 본인 (혹은 특정인의) CID (=user_pseudo_id) 를 필터로 하고, event_timestamp 로 정렬해주면 그 cid 에서 발생한 이벤트들을 시간 순서로 확인할 수 있습니다. SELECT * FROM '데이터 소스` WHERE user_pseudo_id = '체크하려는 CID' order by event_timestamp 2. 유저 세그먼트 구할 때 > 유저 세그먼트에 대한 조건을 만족한 데이터를 추출할 수 있어요 만약 "특정 페이지를 조회했으면서 구매까지 한 유저 세그먼트' 를 구하고 싶다면 아래처럼 where 절에 user_pseudo_id in (특정 페이지 조건 + purchase 이벤트 인 경우에 대한 쿼리식) 으로 활용할 수 있죠. select user_pseudo_id , (select value.int_value from unnest(event_params) where params.key='ga_session_id') as sid, ecommerce.transaction_id FROM `데이터 소스` where user_pseudo_id in ( SELECT user_pseudo_id FROM `데이터 소스` where (select value.string_value from unnest(event_params) where key = 'page_location') like '%페이지 필터%') and event_name = 'purchase' order by user_pseudo_id, sid 그 외에 다른 예시들은 이후에 또 추가해볼게요 :) 그럼 오늘은 여기서 ga4 를 빅쿼리에 연결했을 떄 활용할 수 있는 유저 / 세션 / 이벤트에 대한 timestamp 값을 알아보았습니다. 함께 소통하면 좋을 내용 있다면 언제든 댓글 남겨주세요! 감사합니당
오늘은 GA4에서 빅쿼리를 연동하는 방법에 대해 알아보자! STEP 1. 빅쿼리를 연결할 계정에서 'Bigquery 링크'클릭 STEP 2. 연결 버튼 클릭 STEP 3. 빅쿼리 프로젝트를 선택한다 *이 때 국가는 꼭! '서울' 로 지정 필수 <빅쿼리 내 구성> (노랑) 프로젝트 > (초록) 데이터셋 > (파랑) 테이블 우측의 '빅쿼리 프로젝트 선택하기' 에서 본인의 프로젝트를 선택하자 STEP 4. 설정 구성을 선택한다 <하단의 '매일' 과 '스트리밍' 의 차이> 매일 (Daily) : 하루에 1번 실시간 데이터(스트리밍 테이블) 에 *정제된 데이터가 쌓이는 것 *정제된 후의 데이터: 앱 데이터의 경우 실시간으로 데이터를 쏘지 않고 모아 놨다가 쏘기 때문에 오전 9시에 발생한 이벤트가 오후 8시에 빅쿼리데일리 테이블에 쌓이는 등 불규칙한 시간에 데이터가 업데이트 되는 경우가 있음 스트리밍 테이블 (Intraday) : 실시간으로 쌓이고 있는 테이블 , 데일리 테이블이 생성되고 나면 그 날의 스트리밍 테이블 값은 사라진다 STEP 5. 빅쿼리 연결 완료! (FIN) 연결이 완료되면 실제 빅쿼리에서 다음과 같이 테이블들이 생성되어 있다. 그럼 오늘은 이렇게 GA4 에서 빅쿼리를 연결하는 방법을 STEP BY STEP 으로 공유드렸습니다 :) 궁금하시 부분 있으시면 언제든 댓글 남겨주세요 ~
구글 애널리틱스를 다룰 때 가장 많이 보게되는 지표 중 하나는 '세션수'다. 누군가에게 단순하게 '세션수가 무엇이냐'에 대해 답한다면 '웹 사이트 혹은 앱에 발생한 유입수' 라 단순화 할 수 있지만 정의와 관련해서 좀 더 파보면 단순 '유입' 보다 신경써야하는 부분이 많다 <구글 애널리틱스 공식 도움말 참고> https://support.google.com/analytics/answer/9191807?hl=ko 우선 구글 애널리틱스의 백과사전 같은 존재인 도움말에서의 세션수 정의는 다음과 같다. "세션은 사용자가 웹사이트 또는 앱과 상호작용하는 기간입니다." 그럼 어떤 상황에서 사용자가 '상호 작용 했다' 고 인지될까? [세션으로 집계되는 상황] GA4 도움말에 의하면 세션이라 인식되는 대상은 다음과 같다. "애널리틱스에서는 사용자가 포그라운드에서 앱을 열거나 페이지 또는 화면을 보고 현재 활성화된 세션이 없을 때 세션이 시작되고, 기본적으로 세션은 사용자의 활동이 멈춘 후 30분 뒤에 종료(타임아웃)됩니다." 즉, 현재 진행되고 있는 세션이 없는 상태에서 사이트에 들어오면 세션이 시작되고, 다른 이벤트들이 발생되지 않은지 30분이 지나는 시점에 세션이 끝난다. *이 30분 조건은 GA4 관리 창에서 조정할 수 있음 예를 들어서 위처럼 유저 A 가 유튜브 광고를 클릭해서 해당 사이트에 들어갔다가 30분이 지나지 않은 시점에 다시 네이버 검색으로 사이트에 접속하게 되면 유저 A 는 1개의 세션에 유튜브 / 영상 광고 , 네이버/검색 광고로 유입되었다는 정보가 수집된다. 그럼 이어서 좀 더 DEEP 하게 세션수가 어떤 구조로 실제 GA4 에 수집되고 있고, 세션수는 어떻게 (빅쿼리에서) 집계되는 것인지 이어서 알아보자 [빅쿼리를 써야하는 두 가지 중요한 이유] 만약 빅쿼리에 대한 니즈 없이 GA4 데이터 소스만 활용한다면 GA4의 탐색 보고서 혹은 루커 스튜디오 정도만으로도 충분하다. 하지만 빅쿼리를 사용하게되면 아래의 강력한 장점이 있다. 1) 할당량과 무관하게 데이터 시각화 가능 *할당량 이슈란? GA4 의 데이터를 실시간으로 보고서에 불러올 때 프로젝트 별, 시간별, 동시요청 시에 대한 할당량에 제한이 있어 보고서 생성시의 제약 . (빅쿼리 연결 없이 GA 데이터 소스를 그대로 사용할 때 발생 ) *참고: https://developers.google.com/analytics/devguides/reporting/data/v1/quotas 2) 실시간 RAW 수집 데이터 확인 가능 GA4에 빅쿼리를 연동하게 되면 GA4 데이터를 위해 빅쿼리에 프로젝트 , 데이터셋 , 테이블이 자동으로 생성되는데 그 중 events 테이블은 약 1일에 1번 정제된 데이터가 쌓이는 곳이고 intraday 가 당일의 실시간 데이터가 쌓이므로 해당 테이블에서는 방금 내가 이벤트를 발생시켰더라도 데이터 전송 여부를 바로 확인할 수 있게 된다. 위의 빅쿼리의 장점을 활용하기 위해 빅쿼리를 연결한 상태라면 session 데이터의 수집 구조를 알아보자 :) [빅쿼리에 쌓이는 세션 관련 데이터] 결론적으로 말하면 빅쿼리에서 세션수는 user_pseudo_id 와 session_id 를 결합해서 만든다. *GA 도움말에도 아래처럼 명시되어 있음 우선 해당 가이드에 언급되어 있는 session_id , user_pseudo_id 에 대해서 알아보자. [ga_session_id (session_id] 빅쿼리에 연결된 ga 데이터에서 session_start 라는 이벤트를 where 절로 걸어서 확인해보면 SELECT * FROM `ga 데이터` where event_name = 'session_start' event_params 에 'ga_session_id' 라 해서 요리보고 저리봐도 session 관련 구분자로 보이는 값이 하나 있다. session_start 는 말 그대로 세션이 시작될 때 ga 에서 자동으로 수집해주는 이벤트인데 ga_session_id 는 이 '세션이 시작된 시점 값' 이다. 인터넷에 'Unix timestamp 변환기' 라 검색하고 ga_session_id 값을 넣으면 아래처럼 시간으로 변환 되는걸 볼 수 있다. 이 ga_session_id 값은 session_start 외에 다른 이벤트들에도 따라다니면서 이후 발생하는 이벤트들이 어느 session_id 에서 발생한건지 구분해준다. 근데 위에서 지금 session_start 라는 이벤트도 있고,ga_session_id 라는 세션을 위한 구분자도 있는데 왜 굳이 ga_session_id + user_pseudo_id 를 결합해서 세션수를 집계할까? 그 이유는 1) session_start 가 누락 수집 되는 경우가 있을 수 있음 프로젝트를 하면서 ga 의 자동 수집 이벤트가 발생하지 않는 경우들도 확인했었고, 실제로 구글 애널리틱스 도움말에서도 세션 id 와 session_start 가 연결되지 않았을 수 있다고 한다 2) ga_session_id 만으로는 고유한 식별자가 될 수 없음 위에서 언급한 것 처럼 ga_session_id 는 세션이 시작된 시점의 time_stamp다. 만약 엄청 낮은 확률일지라도, 유저가 많이 접속하는 사이트 혹은 앱에서 조금의 시간차이 없이 동시에 접속 하는 사람이 있었다면? ( 국민 6%가 몰린 동탄역 롯데캐슬 로또 청약 신청일의 청약홈을 상상해보자^^) 그 사람들은 같은 ga_session_id 를 갖게 되니 고유한 값이라 할 수없다. 그럼 user_pseudo_id 가 무엇이길래 ga_session_id와 결합해서 세션수를 카운트하는 고유값으로 으로 사용하는걸까? [user_pseudo_id란?] user_pseudo_id를 이해하려면 ga 의 쿠키를 이해해야한다. 쿠키는 '쿠키 수집 동의' 등등 여러 알람이 있으니 많은 사람들이 익숙한 용어인데 단순히 표현하면 해당 사이트에 다녀간 흔적을 남기는 표시다. GA 역시 그런 쿠키 값들을 수집하는데 "웹 페이지 우클릭 > 검사 > 개발창 > 애플리케이션" 에서 확인할수 있다. 지금 _ga 라는 쿠키에 'GA1.1.141576118.1662352900'가 수집 되어 있다 GA 빅쿼리에 있는 user_pseudo_id 가 바로 이 쿠키의 141576118.1662352900 값이다 이 값은 브라우저와 기기별로 부여되는 고유값과, 해당 쿠키값이 생성된 시점 , 즉, 첫 유입시의 time_stamp 가 결합되어 있는 값이다. 예를 들어서 운 좋게 어떤 한 사람이 컴퓨터와 핸드폰으로 각각 크롬, 빙 같이 다른 브라우저를 동시에 들어가게 되면 time_stamp 는 같더라도 기기. 브라우저 등 다른 고유 값이 있으니 user_pseudo_id 는 총 4개가 생성된다. 이처럼 user_pseudo_id 는 이처럼 기기, 브라우저 단위로 생성되는 고유값이고 ga_session_id 는 특정 세션이 시작된 시점에 대한 고유값이니 이 두개를 결합하면 기기,브라우저별로 특정 세션이 발생할 때마다 카운트할 수 있는 고유값이 된다 [빅쿼리 세션수 집계 쿼리식] 그럼 마지막으로 위에서 알게된 내용들을 토대로 실제로 쿼리를 통해 세션수를 집계할 수 있다 SELECT count(distinct (concat(user_pseudo_id,'.',(select value.int_value from unnest(event_params) where key = 'ga_session_id')))) FROM `ga 데이터 소스` *'ga_session_id' 값을 왜 아래처럼 표현하는지 궁금하다면? 아래 글을 참고해주세요! (select value.int_value from unnest(event_params) where key = 'ga_session_id') [GA4] GA4 데이터, 빅쿼리 Array/Struct/Unnest 활용하여 조회하기
오늘은 GA4의 데이터 Scope과 세그먼트 3가지 유형에 대해 이야기해보려고 합니다. GA4에서 수집한 데이터를 다음과 같이 4가지 Scope으로 나눌 수 있습니다. 1. 사용자 스코프(User Scope) : 사용자의 행동과 속성을 추적하기 위해 사용하며, 특정 사용자와 관련된 모든 이벤트를 결합하여 사용자의 행동 패턴을 이해하는데 유용한 데이터 수집 단위입니다. 2. 세션 스코프 (Session Scope) : 한 세션 내에서 발생하는 모든 이벤트를 그룹화하고, 특정 세션 동안 사용자가 웹사이트에서 어떻게 상호작용했는지 분석하는 데 유용한 데이터 수집 단위입니다. 3. 이벤트 스코프 (Event Scope) : GA4에서 가장 기본적인 데이터 수집 단위로, 이벤트는 페이지 조회, 메뉴 클릭, 버튼 클릭과 같이 웹사이트에서 발생하는 사용자의 모든 상호작용을 의미합니다. 특히 이벤트를 부연 설명할 수 있는 이벤트 파라미터와 함께 사용하여 분석합니다. 4. 상품 스코프 (Item Scope) : 전자상거래와 관련된 데이터로 제품 성과를 분석하고자 할 때 사용하고, 상품 ID, 상품명, 상품 카테고리, 가격, 색상 등의 정보가 상품 스코프 데이터에 포함됩니다. 이커머스 이벤트 수집 시 발생하는 데이터레이어 중, items 배열 안에 묶여 있는 item_id, item_name, price, quantity, item_category 등 값들이 상품 스코프에 속하는 데이터라고 보면 됩니다. 데이터 Scope을 아래와 같이 구조화해 볼 수 있습니다. 사용자 > 세션 > 이벤트 순서로 큰 단위이고, 상품 스코프의 경우 select_promotion, view_item 같이 이커머스 이벤트일 때 상품 데이터를 확인할 수 있습니다. 여기까지 잘 이해하셨다면, GA4에서 세그먼트를 조금 더 수월하게 사용할 수 있을 것입니다. 세그먼트 유형도 데이터 Scope을 기준으로 크게 사용자 세그먼트, 세션 세그먼트, 이벤트 세그먼트로 나뉘어집니다. 우선 세그먼트에 대해 짚고 넘어가겠습니다. 세그먼트(Segment)는 특정 조건을 만족하는 집합을 의미하는데요. GA4에서는 사용자, 세션, 이벤트로 구분하여 특정 조건을 만족하는 집합을 분석하기 위해 세그먼트를 활용합니다. 예를 들어, 특정 페이지를 방문한 사용자, 특정 이벤트를 발생시킨 사용자 등을 세그먼트로 정의할 수 있습니다. GA4 탐색 보고서에서 세그먼트 추가를 클릭하면, 아래와 같이 3가지 유형을 볼 수 있습니다. 1. 사용자 세그먼트(User Segment) : 특정 행동이나 특징이 있는 사용자 그룹을 정의할 때 사용합니다. 예를 들어 장바구니에 상품은 담았지만 구매를 하지 않은 유저를 보고자 할 때 사용할 수 있습니다. 2. 세션 세그먼트(Session Segment) : 특정 조건을 만족하는 세션 그룹을 정의할 때 사용합니다. 주로 특정 캠페인이나 소스/매체로 유입된 경우를 보고자 할 때 사용할 수 있습니다. 3. 이벤트 세그먼트(Event Segment) : 특정 위치에서 발생하는 이벤트를 정의할 때 사용합니다. (이벤트 세그먼트는 잘 사용하지 않는 편입니다.) 구조화된 데이터 Scope을 떠올려보면, 세그먼트 유형을 아래와 같이 정리해볼 수도 있습니다. - 사용자 세그먼트에서는 동일 사용자, 동일 세션, 동일 이벤트를 포함 - 세션 세그먼트에서는 동일 세션, 동일 이벤트를 포함 - 이벤트 세그먼트에서는 동일 이벤트를 포함 이처럼 세그먼트 유형에 따라 가져오는 데이터의 범위가 달라져 데이터의 모수가 달라지므로, 세그먼트 분석 시 유의하시길 바랍니다. 마지막으로 세그먼트 유형별로 가지는 조건 범위에 대해 알아보겠습니다. - Across all sessions : 사용자가 조회 기간 내 한 번이라도 설정한 조건을 만족했다면 포함됩니다. 특히 사용자가 여러 세션에 걸쳐 특정 페이지를 한 번 이상 방문했거나, 특정 이벤트를 한 번 이상 발생시켰는지를 확인할 때 유용합니다. - Within the same session : 하나의 세션 내에서 설정한 조건을 만족했다면 포함됩니다. 사용자가 특정 세션 내에서 특정 페이지를 방문하거나, 특정 이벤트를 발생시켰는지를 확인할 때 유용합니다. - Within the same event : 동일한 이벤트 내에서 설정한 조건을 만족했다면 포함됩니다. 특정 페이지에서 발생한 이벤트를 확인할 때 유용합니다. 시퀀스 조건으로 시간 순서대로 일련의 행동 데이터를 볼 수 있고, 이는 유저 범위 세그먼트에서만 설정 가능한 점 참고하시면 좋을 것 같습니다.
최근 GA4 빅쿼리에 신규 세션 컬럼이 생겼습니다. [session_traffic_source_last_click] 컬럼인데요 기존 존재하던 [collected_traffic_source]와의 차이를 알아보겠습니다. 기존 collected_traffic_source는 현재 접속한 세션의 source(소스)와 medium(매체)정보를 보유하고 있었습니다. 그러다보니 GA4 리포트와 같이 Last Click Source / Medium은 확인을 할 수 없어 직접 복잡한 쿼리를 이용해 데이터를 추출하고 있었는데요 기존 collected_traffic_source와 신규 session_traffic_source_last_click 이번에 새로 생긴 [session_traffic_source_last_click] 컬럼에서 Last Click Source / Medium에 대한 정보를 제공하게 됐습니다. 더 흥미로운 부분은 UA에서도 제공하지 않았던 Google Ads(구글 애즈)데이터를 함께 제공한다는 것 입니다. 따라서 GA4 빅쿼리를 이용해서 구글 애즈 데이터를 분석하거나 대시보드를 제작 혹은 애즈 API를 통한 확장을 고민할 수 있게 됐습니다. 하지만 주의할점은 최근 패치된 기능인 만큼 7월 중순부터의 데이터만 확인이 가능하다는 것 입니다. 따라서 그 전 데이터는 쿼리를 통해 직접 추출해야하는 불편함이 있습니다. 만약 7월 중순 이전 데이터가 필요하다면 플러스제로에 문의해주세요 :)
console.log(msg) 콘솔에 Verbose단계의 msg를 보여줍니다 console.info(msg) 콘솔에 Info단계의 msg를 보여줍니다 console.warn(msg) 콘솔에 Warnings단계의 msg를 보여줍니다 console.error(msg) 콘솔에 Errors단계의 msg를 보여줍니다 console.time(label) 스크립트 실행를 기점으로 시간을 측정하기위한 준비를 합니다. label을 입력하여 각각 시간을 측정해줄 지점을 지정해 줄 수 있습니다. console.timeLog(label) console.time 으로 부터 얼마나 많은 시간이 지났는지 ms 단위로 중간 결과를 출력합니다. console.timeEnd(label) console.time 으로 부터 얼마나 많은 시간이 지났는지 ms 단위로 종료 결과를 출력합니다.
구글에서 제공하는 SDK를 통해 GA4 보고서 데이터를 얻을 수 있습니다. 설치 가이드: https://github.com/googleapis/google-cloud-python/blob/main/packages/google-analytics-data/README.rst#installation 모듈 import GCP 인증 우선 dict 형태 또는 json 파일 형태의 Service Account를 읽어 구글 클라우드 플랫폼에 인증 과정을 거칩니다. 서비스 어카운트에는 Analytics Data API 사용 권한이 있어야 합니다. API 요청 폼 생성 기본적인 요청 폼 클래스 인스턴스를 생성합니다. 폼 생성 가이드: https://developers.google.com/analytics/devguides/reporting/data/v1/basics?hl=ko#generate_a_report REST API 가이드 : https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport?hl=ko#request-body 실행 및 데이터 확인 date필드의 경우 "yyyymmdd" 포맷으로 나오므로 필요할 경우 위와 같이 원하는 포맷으로 가공할 수 있습니다. 모든 메트릭은 String 형태이며 숫자로 나오는 메트릭도 Numeric String 형태로 나옵니다. 필요할 경우 int 타입으로 캐스팅 후 사용할 수 있습니다. response 인스턴스에서 row_count 애트리뷰트를 보면 현재 요청한 보고서의 총 row 개수를 얻을 수 있습니다. 만일 현재 뽑은 데이터 수가 row_count보다 적다면 offset 파라미터를 현재 limit 값으로 입력하여 다음 페이지의 데이터를 얻을 수 있습니다.