Klaviyo와 Snowflake를 연결합니다.
Klaviyo 고급 데이터 플랫폼은 Klaviyo의 표준 마케팅 애플리케이션에 포함되어 있지 않으며, 관련 기능에 액세스하려면 구독이 필요합니다. 이 요금제를 구매하는 방법을 알아보려면 결제 가이드를 확인해 주세요.
Snowflake를 가져오기에 대비해 준비하는 방법, Klaviyo에서 예약된 프로필 동기화를 만드는 방법, 그리고 가져오기 성능 문제를 해결하는 방법을 알아보세요. 지원되는 데이터와 사용 사례에 대한 개요는 Klaviyo의 데이터 웨어하우스 가져오기 이해하기를 참고해 주세요.
이 문서의 목적상 "테이블"이라는 용어를 사용하지만 뷰, 구체화된 뷰 및 테이블은 모두 가져올 수 있는 유효한 Snowflake 객체입니다. Klaviyo가 개체에 대해 SELECT col1 FROM table_name을 실행할 수 있다면, 원하는 대로 자유롭게 사용할 수 있습니다.
이 문서에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL"이라는 핵심 단어는 RFC 2119에 설명된 대로 해석해야 해요.
Snowflake 관리자 설정
시작하기 전에 Snowflake에서 securityadmin 및 sysadmin 역할이 있는지 확인해 주세요. SHOW GRANTS TO USER <your_username>를 실행하여 역할을 확인하고, 액세스가 필요하면 Snowflake 관리자에게 문의해 주세요. 그런 다음 다음 단계를 따라 Klaviyo에서 데이터를 가져올 수 있도록 허용해 주세요.
- 로컬 터미널에서 다음 명령을 실행하여 개인 키를 생성합니다.
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt- 터미널에서 다음 명령을 실행하여 개인 키를 참조하는 공개 키를 생성합니다.
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub- 텍스트 편집기에서
rsa_key.pub을(를) 열고,-----BEGIN PUBLIC KEY-----및-----END PUBLIC KEY-----줄이나 줄바꿈 없이 공개 키를 복사해 주세요. SQL 설정 스크립트에서rsa_public_key에 있는GENERATE_PUBLIC_KEY을(를) 이 값으로 바꾸되, 주변의 작은따옴표는 그대로 유지해 주세요. Mac에서는 아래 명령을 사용하면 값이 클립보드에 복사돼요.
# Mac terminal command to copy the public key without PEM delimiters or line breaks
sed '/-----BEGIN PUBLIC KEY-----/d; /-----END PUBLIC KEY-----/d' rsa_key.pub | tr -d '\r\n' | pbcopy- Klaviyo용 서비스 사용자를 만들려면 Snowflake 환경에서 다음 SQL 스크립트를 실행해 주세요.
- 필요에 따라 스크립트 시작 부분에서 사용자, 역할, 웨어하우스, 데이터베이스, 네트워크 이름을 맞춤 설정해 주세요. 이름은 대문자로 유지하고, DO NOT CHANGE 섹션에서는 스키마 이름을 변경하지 마세요.
- 요약하면 다음과 같습니다.
- 기존 웨어하우스를 선택하거나 새 웨어하우스를 만듭니다.
- 기존 데이터베이스를 선택하거나 새 스키마를 보유할 새 데이터베이스를 만듭니다.
- 새 스키마 2개(
KLAVIYO_TMP및)를 생성하세요.KLAVIYO_IMPORT_FROM_DWH - 새 네트워크 정책을 만들고 목록 Klaviyo IP를 허용합니다
- Klaviyo의 사용자 및 역할을 만듭니다.
- 이 스크립트는 동일한 이름의 기존 객체를 유지하기 위해 IF NOT EXISTS를 사용해요. 기존 서비스 사용자를 재사용하는 경우, 구성된 공개 키가 Klaviyo에 입력할 개인 키와 일치하는지 확인해 주세요. 이 스크립트는 기존 사용자의 공개 키를 교체하지 않아요.
BEGIN;
-- create variables for user / password / role / warehouse / database.
-- Change these to whatever you prefer.
SET role_name = 'KLAVIYO_DATA_TRANSFER_ROLE'; -- all letters must be uppercase, ex. 'KLAVIYO_DATA_TRANSFER_ROLE'
SET user_name = 'KLAVIYO_DATA_TRANSFER_USER'; -- all letters must be uppercase, ex. 'KLAVIYO_DATA_TRANSFER_USER'
SET warehouse_name = 'KLAVIYO_DATA_TRANSFER_WAREHOUSE'; -- all letters must be uppercase, ex. 'KLAVIYO_DATA_TRANSFER_WAREHOUSE'
SET database_name = 'KLAVIYO_DATABASE'; -- all letters must be uppercase, ex. 'KLAVIYO_DATABASE'. If this database doesn't exist, a new one will be created.
SET network_policy = 'KLAVIYO_DATA_TRANSFER_NETWORK_POLICY'; -- all letters must be uppercase, ex. 'KLAVIYO_NETWORK_POLICY'
SET network_rule = 'KLAVIYO_DATA_TRANSFER_NETWORK_RULE'; -- all letters must be uppercase, ex. 'KLAVIYO_NETWORK_RULE'
/* replace GENERATE_PUBLIC_KEY below with generated public key */
-- DO NOT CHANGE
SET schema_name_tmp = $database_name || '.KLAVIYO_TMP'; -- DO NOT CHANGE
SET schema_name_import = $database_name || '.KLAVIYO_IMPORT_FROM_DWH'; -- DO NOT CHANGE
SET full_network_rule_tmp = $schema_name_tmp || '.' || $network_rule; -- DO NOT CHANGE
SET full_network_rule_import = $schema_name_import || '.' || $network_rule; -- DO NOT CHANGE
-- change role to sysadmin for warehouse / database steps
USE ROLE sysadmin;
-- create a warehouse for data transfer service
CREATE WAREHOUSE IF NOT EXISTS IDENTIFIER($warehouse_name)
warehouse_size = xsmall
warehouse_type = standard
auto_suspend = 60
auto_resume = true
initially_suspended = true;
-- create database for data transfer service
CREATE DATABASE IF NOT EXISTS IDENTIFIER($database_name);
-- create schemas for data transfer service
CREATE SCHEMA IF NOT EXISTS IDENTIFIER($schema_name_tmp);
CREATE SCHEMA IF NOT EXISTS IDENTIFIER($schema_name_import);
-- change role to securityadmin for user / role steps
USE ROLE securityadmin;
-- create network rule and policy for database
GRANT USAGE ON DATABASE IDENTIFIER($database_name) TO ROLE securityadmin;
GRANT USAGE, CREATE NETWORK RULE ON SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE securityadmin;
GRANT USAGE, CREATE NETWORK RULE ON SCHEMA IDENTIFIER($schema_name_import) TO ROLE securityadmin;
-- whitelist klaviyo ip ranges, for KLAVIYO_TMP schema
CREATE NETWORK RULE IF NOT EXISTS IDENTIFIER($full_network_rule_tmp)
type = IPV4
value_list = (
'184.72.183.187/32', '52.206.71.52/32', '3.227.146.32/32', '44.198.39.11/32', '35.172.58.121/32', '3.228.37.244/32', '54.88.219.8/32', '3.214.211.176/32'
)
comment = 'Klaviyo IP Ranges as of April 2025';
CREATE NETWORK POLICY IF NOT EXISTS IDENTIFIER($network_policy)
allowed_network_rule_list = ($full_network_rule_tmp);
-- whitelist klaviyo ip ranges, for KLAVIYO_IMPORT_FROM_DWH schema
CREATE NETWORK RULE IF NOT EXISTS IDENTIFIER($full_network_rule_import)
type = IPV4
value_list = (
'184.72.183.187/32', '52.206.71.52/32', '3.227.146.32/32', '44.198.39.11/32', '35.172.58.121/32', '3.228.37.244/32', '54.88.219.8/32', '3.214.211.176/32'
)
comment = 'Klaviyo IP Ranges as of April 2025';
CREATE NETWORK POLICY IF NOT EXISTS IDENTIFIER($network_policy)
allowed_network_rule_list = ($full_network_rule_import);
-- create role for data transfer service
CREATE ROLE IF NOT EXISTS IDENTIFIER($role_name);
GRANT ROLE IDENTIFIER($role_name) TO ROLE sysadmin;
-- create a user for data transfer service
CREATE USER IF NOT EXISTS IDENTIFIER($user_name)
type = SERVICE
network_policy = $network_policy
default_role = $role_name
default_warehouse = $warehouse_name
rsa_public_key = 'GENERATE_PUBLIC_KEY';
GRANT ROLE IDENTIFIER($role_name) TO USER IDENTIFIER($user_name);
ALTER USER IDENTIFIER($user_name) SET NETWORK_POLICY = $network_policy;
-- grant service role access to warehouse
GRANT USAGE
ON WAREHOUSE IDENTIFIER($warehouse_name)
TO ROLE IDENTIFIER($role_name);
-- grant service access to database
GRANT MONITOR, USAGE
ON DATABASE IDENTIFIER($database_name)
TO ROLE IDENTIFIER($role_name);
-- Grant privileges for KLAVIYO_TMP
GRANT USAGE ON SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE IDENTIFIER($role_name);
GRANT MONITOR, USAGE, CREATE TABLE, CREATE VIEW, CREATE SEQUENCE, CREATE FUNCTION, CREATE PROCEDURE
ON SCHEMA IDENTIFIER($schema_name_tmp)
TO ROLE IDENTIFIER($role_name);
GRANT ALL ON ALL TABLES IN SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE IDENTIFIER($role_name);
GRANT ALL ON FUTURE TABLES IN SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE IDENTIFIER($role_name);
GRANT ALL ON ALL VIEWS IN SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE IDENTIFIER($role_name);
GRANT ALL ON FUTURE VIEWS IN SCHEMA IDENTIFIER($schema_name_tmp) TO ROLE IDENTIFIER($role_name);
-- Grant privileges for KLAVIYO_IMPORT_FROM_DWH
GRANT USAGE ON SCHEMA IDENTIFIER($schema_name_import) TO ROLE IDENTIFIER($role_name);
GRANT SELECT ON ALL TABLES IN SCHEMA IDENTIFIER($schema_name_import) TO ROLE IDENTIFIER($role_name);
GRANT SELECT ON FUTURE TABLES IN SCHEMA IDENTIFIER($schema_name_import) TO ROLE IDENTIFIER($role_name);
GRANT SELECT ON ALL VIEWS IN SCHEMA IDENTIFIER($schema_name_import) TO ROLE IDENTIFIER($role_name);
GRANT SELECT ON FUTURE VIEWS IN SCHEMA IDENTIFIER($schema_name_import) TO ROLE IDENTIFIER($role_name);
COMMIT;Snowflake 데이터 설정
설정 스크립트는 스키마 2개를 생성해요:
- KLAVIYO_TMP는 Klaviyo에서만 사용됩니다. 이 스키마에 생성된 테이블은 수정해서는 안 됩니다. Klaviyo는 더 이상 필요하지 않은 테이블을 삭제합니다.
- KLAVIYO_IMPORT_FROM_DWH는 Klaviyo를 가져올 최종 테이블을 저장하는 곳입니다. 동기화 생성 프로세스를 진행하면 이 스키마의 모든 테이블 중에서 선택할 수 있도록 나열됩니다. 따라서 가져올 최종 테이블만 저장하여 설정 도중 혼란을 방지해야 합니다.
Klaviyo로 가져오려는 모든 테이블은 다음 기준을 충족해야 합니다.
타임스탬프 요구사항
- 각 테이블에는
inserted_at또는updated_at와 같이 행이 생성되었거나 마지막으로 업데이트된 시점을 나타내는 타임스탬프 필드가 MUST 포함되어야 해요. 동기화 생성 중에 새 행 감지에서 해당 필드를 사용할 수 있도록 Snowflake 타임스탬프 데이터 유형을 사용해 주세요.- 타임스탬프 필드는 단조롭게 증가해야 합니다(예: 항상 더 커지거나 동일하게 유지되어야 하며 결코 작아지지 않아야 함).
- 동기화 생성 후에는 행의 타임스탬프 값을 과거 시간으로 설정하지 않아야 하며 그렇지 않으면 Klaviyo가 해당 행을 픽업하지 못할 수 있습니다.
- 타임스탬프는 UTC 형식이거나 시간대 정보를 포함해야 합니다. 시간대 정보가 누락된 경우 Klaviyo에서 UTC로 가정합니다. 사용자 지정 속성의 경우 이러한 타임스탬프는 문자열 형식으로 유지되므로 원하는 시간대로 해석할 수 있습니다.
- 변경 감지 타임스탬프는 가져오기 테이블에서 행이 삽입되거나 업데이트되는 시점을 MUST 반영해야 해요. 과거 가져오기와 수동 가져오기는 가장 오래된 타임스탬프부터 시작해 1시간 단위의 윈도우로 앞으로 스캔해요. 최근 행들 사이에 2023년 레코드 같은 오래된 타임스탬프가 하나만 있어도, Klaviyo가 그 사이의 빈 윈도우를 스캔하기 때문에 이러한 가져오기가 느려질 수 있어요. 이후 예약된 실행은 저장된 진행 상태에서 계속 진행되며, 가장 오래된 행부터 다시 시작하지 않아요.
- 각 1시간 타임스탬프 창 내에서 행 밀도를 고려해 주세요. 같은 창에 수백만 개의 레코드가 있으면 동기화가 느려질 수 있어요. 창당 약 100,000행은 고정된 한도가 아니라 성능 가이드라인이며, 행 크기도 성능에 영향을 줘요.
- Klaviyo는 동기화할 테이블에 행을 추가할 때마다 타임스탬프 필드를 CURRENT_TIMESTAMP() 또는 이와 동등한 함수로 설정할 것을 권장합니다. 여러 행의 타임스탬프가 동일할 수 있습니다. 아래 예시를 참조하세요.
INSERT INTO table_name (email, inserted_at)
SELECT email, CURRENT_TIMESTAMP()
FROM source_table;표 구조
- 테이블은 추가 전용(또는 삽입 전용)으로 처리해야 합니다.
- 대신 행을 업데이트하려면 Klaviyo가 변경 사항을 파악할 수 있도록 타임스탬프 필드를 업데이트해야 합니다.
- 테이블은 타임스탬프 열에 주문해야 합니다. Snowflake는 삽입 순서에 따라 클러스터링 및 파티셔닝을 처리합니다. 이렇게 하면 Klaviyo의 가져오기 쿼리를 최적화하여 Snowflake의 계산 비용을 낮출 수 있습니다
프로필 고유성 및 일관성
- 식별자가 아닌 각 프로필 속성은 반드시 1개의 테이블에서만 가져와야 해요. Klaviyo는 동기화를 저장할 때 테이블 간 중복 프로필 속성 매핑을 거부해요. 이메일, 전화번호, 외부 ID와 같은 프로필 식별자는 여러 테이블에서 매핑할 수 있어요.
- 모든 가져오기 테이블에 동일한 프로필 식별자(이메일, 전화번호, 외부 ID 등)를 사용하여 중복 프로필이 생성될 위험을 최소화해야 합니다.
- 입력한 프로필 식별자가 Klaviyo 내 기존 프로필과 일치하지 않는 경우 Klaviyo는 새 프로필을 생성합니다.
- 예: Table1(이메일, fav_color) + Table2(전화, 생일)
- 이 표는 이메일과 전화번호가 동일한 기존 프로필로 확인되지 않으면, 같은 사람에 대해 프로필 2개를 만들 수 있어요. 두 테이블 모두에서 기존 프로필 1개를 업데이트하려면 해당 프로필에 이미 속한 식별자를 사용하거나, 두 테이블 모두에 공통 식별자를 포함해 주세요.
- 이 문제를 방지하는 한 가지 방법은 모든 프로필에 하나의 가져오기 표만 사용하는 것입니다.
순환 가져오기/내보내기 루프 방지
- 순환 가져오기-내보내기 루프를 방지하기 위해 가져오기 및 내보내기 기능을 모두 사용하는 시나리오를 주의 깊게 관리해야 합니다. Klaviyo는 현재 이 시나리오를 감지하지 못하므로 내보내기 프로세스에서 가져오기 테이블의 업스트림에 있는 테이블로 데이터를 다시 전송하지 않도록 하세요.
- 다음과 같이 표시됩니다.
- Klaviyo는 새로 추가되거나 업데이트된 프로필을 웨어하우스로 내보내요.
- 변환은 해당 프로필을 가져오기 테이블에 기록하고 변경 감지 타임스탬프를 진행시켜요.
- Klaviyo가 해당 행을 다시 가져와 프로필을 업데이트하고, 이후 다시 내보낼 수 있게 해요.
- 이 루프를 피하는 예시:
- 내보낸 데이터는 각 내보내기 시 원본 행이나 변경 감지 타임스탬프를 다시 쓰지 않고, 가져올 원본 행을 필터링하는 용도로만 사용하세요.
- 가져오기 테이블에서 행을 추가하거나 업데이트하는 변환에는 내보낸 데이터를 포함하지 마세요.
- 순환 가져오기-내보내기 루프의 결과는 무엇인가요?
- 이렇게 하면 사용자와 Klaviyo 모두에게 불필요한 계산 비용이 발생합니다.
- 다음과 같이 표시됩니다.
Klaviyo에서 예약된 프로필 가져오기 만들기
테이블을 준비한 후, 가져오려는 각 테이블에 대해 동기화를 생성해 주세요. Snowflake 계정 식별자, 웨어하우스 및 데이터베이스 이름, 서비스 사용자 이름, 그리고 rsa_key.p8의 전체 내용을 미리 준비해 주세요. Klaviyo에서 데이터 웨어하우스 동기화를 생성할 수 있는 권한도 필요해요. 여기에는 PEM 헤더와 푸터를 포함해 비공개 키를 사용해 주세요, 공개 키는 Snowflake 설정 스크립트에 넣어야 해요.
- Klaviyo에서 데이터 동기화를 여세요. 이미 연결이 있으면 동기화 만들기를 클릭해 주세요.
- 데이터 웨어하우스에서 데이터 가져오기를 선택하고 Snowflake를 선택한 다음 Snowflake에 연결을 클릭하세요.
- 계정, Warehouse, Database, Username, Private Key를 입력해 주세요. 설정 스크립트의 warehouse, database, user name을 사용해 주세요. 다음을 클릭합니다.
- 테스트를 클릭하세요. 연결이 성공하고 Klaviyo에서 가져오기 테이블을 찾으면 다음을 클릭해 주세요.
- 동기화 이름을 입력하고 테이블을 선택한 다음 데이터 객체 유형으로 프로필을 선택해 주세요. Cadence 아래에서 Schedule run을 선택하고 빈도, 시작 날짜, 시작 시간 및 시간대를 선택해 주세요. 다음을 클릭합니다.
- 변경 감지에서 새 행 감지의 타임스탬프 필드를 선택해 주세요. 테이블의 식별자 열을 이메일, 전화번호 또는 외부 ID 같은 해당 Klaviyo 프로필 식별자에 매핑하고, 가져오려는 프로필 속성을 매핑해 주세요.
- 연결을 저장하고 동기화하려면 완료를 클릭해 주세요. 성공적으로 저장한 후 Klaviyo에서 데이터 동기화로 돌아가요.
예약된 동기화는 기존 행을 가져오고, 선택한 타임스탬프 필드를 사용해 새로 추가되었거나 업데이트된 행을 가져와요. 가져오기 행이 변경될 때마다 해당 필드를 계속 업데이트하세요.
문제 해결
동기화가 중단된 것 같습니다.
가져오기가 실행 중인데 Klaviyo에 데이터가 표시되지 않는다면 타임스탬프 값과 각 타임스탬프 창의 행 수를 확인해 주세요:
- 초기 과거 데이터 가져오기 또는 수동 가져오기에서는 다른 데이터보다 훨씬 오래된 타임스탬프가 있는지 확인해 보세요. 예를 들어, 다른 행은 지난주 데이터인데 2023년 데이터 행이 1개만 있는 경우가 있어요. Klaviyo는 최근 행에 도달하기 전에 그 사이의 1시간 단위 구간을 스캔해요. 지속적인 예약 동기화의 경우, 새로 추가되거나 업데이트된 행에 최신 변경 감지 타임스탬프가 있는지 확인해 보세요. 과거 날짜로 소급된 변경 사항은 저장된 진행 지점보다 앞에 위치해 누락될 수 있어요.
- 가져오기를 활성화하기 전에 잘못된 변경 감지 타임스탬프를 수정해 주세요. 백필의 경우, 가져오기 테이블의 변경 감지 필드에는 최근 타임스탬프를 사용하되 원래 활동을 설명하는 날짜는 별도의 열에 그대로 유지해 주세요. 대규모 백필의 경우 약 100,000행 단위로 배치 처리하는 것을 목표로 하고, 각 배치는 최소 61분 간격으로 로드한 다음 로드될 때마다 각 배치에
CURRENT_TIMESTAMP()를 찍어 주세요. 이렇게 하면 전체 백필을 1개의 창에 넣는 대신 1시간 단위 창에 행이 분산돼요.
권장 Snowflake 클러스터링 키 설정
Snowflake 테이블을 timestamp 열을 기준으로 클러스터링하면 가져오기 쿼리에서 불필요한 마이크로 파티션을 건너뛸 수 있어요. 클러스터링 키를 추가하기 전에 Snowflake의 Automatic Clustering 비용을 검토해 주세요, 쿼리 비용이 낮아진다고 해서 총비용이 낮아진다는 보장은 없어요. 키를 추가하려면 이 예시에서 데이터베이스, 테이블, 열 이름을 바꾼 다음 Snowflake에서 실행해 주세요:
ALTER TABLE your_database.KLAVIYO_IMPORT_FROM_DWH.your_table CLUSTER BY (your_timestamp_column);