1. 소개

허위 행위에는 연결된 항목의 숨겨진 네트워크가 포함되는 경우가 많습니다. 예를 들어 동일한 이메일 주소, 전화번호 또는 실제 주소를 공유하는 여러 계정이 있습니다. 기존의 관계형 데이터베이스는 이러한 복잡한 다중 홉 관계를 효율적으로 쿼리하는 데 어려움을 겪을 수 있습니다.
BigQuery Graph 를 사용하면 그래프 데이터베이스를 사용하여 이러한 네트워크를 대규모로 분석할 수 있습니다. 기존 BigQuery 테이블 위에 속성 그래프를 정의하고 Graph Query Language (GQL)를 사용하여 데이터에서 패턴을 찾을 수 있습니다.
사기 감지를 위한 그래프 네트워크의 일반적인 애플리케이션은 사기 네트워크와 연결된 배송 주소가 있는 주문을 중지하거나 에 속한 결제를 중지하는 것입니다 .
이 Codelab에서는 BigQuery Graph를 사용하여 사기 감지 솔루션을 빌드합니다. Cloud Storage에서 데이터를 로드하고, 속성 그래프를 만들고, 그래프 쿼리를 사용하여 의심스러운 연결을 식별합니다.
학습할 내용
- BigQuery 데이터 세트를 만들고 데이터를 로드하는 방법을 알아봅니다.
- DDL을 사용하여 속성 그래프를 정의하는 방법을 알아봅니다.
- GQL을 사용하여 그래프를 쿼리하는 방법을 알아봅니다.
- 그래프 분석을 사용하여 사기를 감지하는 방법을 알아봅니다.
필요한 항목
- 결제가 사용 설정된 Google Cloud 프로젝트.
- BigQuery 노트북 환경 (BigQuery Studio 또는 Colab Enterprise).
비용
이 실습에서는 청구 가능한 Google Cloud 리소스를 사용합니다. 완료 후 리소스를 삭제한다고 가정할 때 예상 비용은 5달러 미만입니다.
2. 시작하기 전에
Google Cloud 프로젝트 선택 또는 만들기
- Google Cloud 콘솔의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
- Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 결제가 사용 설정되어 있는지 확인하는 방법을 알아봅니다.
환경 선택
이 실습을 실행하려면 노트북 환경이 필요합니다. BigQuery Studio 또는 Colab Enterprise 를 사용할 수 있습니다.
- Google Cloud 콘솔에서 BigQuery 페이지로 이동합니다.
- Python 노트북을 사용하여 그래프 쿼리를 실행합니다.
Cloud Shell 시작
- Google Cloud 콘솔 상단에서 Cloud Shell 활성화 를 클릭합니다.
- 인증 확인:
gcloud auth list
- 프로젝트 확인:
gcloud config get project
- 필요한 경우 설정합니다.
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID
API 사용 설정
이 명령어를 실행하여 필요한 BigQuery API를 사용 설정합니다.
gcloud services enable bigquery.googleapis.com \
bigqueryreservation.googleapis.com
BigQuery 예약 및 할당 만들기
GQL 쿼리를 실행하려면 Enterprise 또는 Enterprise Plus 버전을 사용하는 예약이 있어야 합니다. Cloud Shell에서 자동 확장을 사용하여 Enterprise 버전 예약을 만듭니다.
# 1. Create a BigQuery Enterprise reservation with 0 baseline slots and 100 max autoscaling slots
bq mk --reservation \
--project_id=${GCP_PROJECT} \
--location=US \
--edition=ENTERPRISE \
--slots=0 \
--autoscale_max_slots=100 \
--ignore_idle_slots=true \
fraud-reservation
# 2. Assign your Cloud project to the newly created reservation for query execution
bq mk --reservation_assignment \
--project_id=${GCP_PROJECT} \
--location=US \
--reservation_id=fraud-reservation \
--job_type=QUERY \
--assignee_type=PROJECT \
--assignee_id=${GCP_PROJECT}
3. 데이터 로드
이 단계에서는 BigQuery 데이터 세트를 만들고 Cloud Storage에서 샘플 데이터를 로드합니다.
샘플 데이터는 시뮬레이션된 소매 환경을 나타내는 여러 CSV 파일로 구성됩니다.
customers.csv: 고객 계정 정보emails.csv: 이메일 주소phones.csv: 전화번호addresses.csv: 실제 주소customer_emails.csv,customer_phones.csv,customer_addresses.csv: 테이블 연결orders.csv: 사기 플래그를 포함한 주문 내역
데이터세트 만들기
테이블을 보관할 fraud_demo라는 데이터 세트를 만듭니다.
- 이 Codelab에서는 SQL 명령어를 실행합니다. BigQuery Studio > SQL 편집기에서 이러한 명령어를 실행하거나 Cloud Shell에서
bq query명령어를 사용할 수 있습니다.
여러 줄의 create 문을 더 효과적으로 사용하기 위해 BigQuery SQL 편집기 를 사용한다고 가정합니다.
CREATE SCHEMA IF NOT EXISTS `fraud_demo` OPTIONS(location="US");
테이블 로드
다음 SQL 문을 실행하여 Cloud Storage에서 데이터 세트로 데이터를 로드합니다.
LOAD DATA OVERWRITE `fraud_demo.customers`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/customers.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.emails`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/emails.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.phones`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/phones.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.addresses`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/addresses.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.customer_emails`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/customer_emails.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.customer_phones`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/customer_phones.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.customer_addresses`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/customer_addresses.csv'],
skip_leading_rows = 1
);
LOAD DATA OVERWRITE `fraud_demo.orders`
FROM FILES (
format = 'CSV',
uris = ['gs://sample-data-and-media/fraud-demo-data/orders.csv'],
skip_leading_rows = 1
);
4. 속성 그래프 만들기
이제 데이터가 로드되었으므로 속성 그래프를 정의할 수 있습니다. 속성 그래프는 노드 (항목)와 에지 (관계)로 구성됩니다.
이 실습에서 노드는 다음과 같습니다.
- 고객: 계정 소유자를 나타냅니다.
- 전화: 전화번호를 나타냅니다.
- 이메일: 이메일 주소를 나타냅니다.
- 주소: 실제 주소를 나타냅니다.
에지는 다음과 같습니다.
- OwnsPhone: 고객을 전화에 연결합니다.
- OwnsEmail: 고객을 이메일에 연결합니다.
- LinkedToAddress: 고객을 주소에 연결합니다.

그래프 만들기
다음 DDL 문을 실행하여 fraud_demo 데이터 세트에 FraudDemo라는 그래프를 만듭니다.
CREATE OR REPLACE PROPERTY GRAPH fraud_demo.FraudDemo
NODE TABLES(
fraud_demo.customers
KEY(account_id)
LABEL Customer PROPERTIES(
account_id,
name),
fraud_demo.emails
KEY(email)
LABEL Email PROPERTIES(
email,
email_type),
fraud_demo.phones
KEY(phone_number)
LABEL Phone PROPERTIES(
phone_number,
phone_type),
fraud_demo.addresses
KEY(address)
LABEL Address PROPERTIES(
address,
address_type)
)
EDGE TABLES(
fraud_demo.customer_emails
KEY(account_id, email)
SOURCE KEY(account_id) REFERENCES customers(account_id)
DESTINATION KEY(email) REFERENCES emails(email)
LABEL OwnsEmail PROPERTIES(
account_id,
email,
last_updated_ts),
fraud_demo.customer_phones
KEY(account_id, phone_number)
SOURCE KEY(account_id) REFERENCES customers(account_id)
DESTINATION KEY(phone_number) REFERENCES phones(phone_number)
LABEL OwnsPhone PROPERTIES(
account_id,
phone_number,
last_updated_ts),
fraud_demo.customer_addresses
KEY(account_id, address)
SOURCE KEY(account_id) REFERENCES customers(account_id)
DESTINATION KEY(address) REFERENCES addresses(address)
LABEL LinkedToAddress PROPERTIES(
account_id,
address,
last_updated_ts)
);
5. 네트워크 분석 (2홉)
BigQuery Studio에서 새 노트북 을 엽니다.

이 Codelab의 시각화 및 추천 부분에서는 BigQuery Studio의 Google Colab 노트북을 사용합니다. 이렇게 하면 그래프 결과를 쉽게 시각화할 수 있습니다.
다음 코드를 코드 셀에 붙여넣습니다.
!pip install bigquery-magics==0.12.1
BigQuery Graph 노트북은 IPython 매직으로 구현됩니다. TO_JSON 함수와 함께 %%bigquery 매직 명령어를 추가하면 다음 섹션에 표시된 대로 결과를 시각화할 수 있습니다. 이 단계에서는 그래프 쿼리를 실행하여 계정 간의 간단한 연결을 찾습니다. 이 쿼리는 시작 노드에서 2홉 이동하여 관련 노드 (예: 고객 -> 이메일 -> 고객)를 찾기 때문에 '2홉' 쿼리입니다.
먼저 Nicole Wade 의 계정을 조사합니다. 2홉을 통해 그녀와 관련된 계정을 찾으려고 합니다.
2홉 쿼리 실행
노트북에서 다음 쿼리를 실행합니다.
%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH
p=(a:Customer)
( -[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']- (n) ){2}
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
-- Verify the final node in the hop array is a Customer
AND 'Customer' IN UNNEST(LABELS(n[OFFSET(1)]))
RETURN TO_JSON(p) AS paths

각 결과의 의미
이 쿼리는 다음과 같은 작업을 합니다.
account_id'd2f1f992-d116-41b3-955b-6c76a3352657'(Nicole Wade)로Customer노드에서 시작합니다.- 연결 노드 (
Phone,Email, 또는Address)로 이어지는OwnsEmail,OwnsPhone, 또는LinkedToAddress에지를 따릅니다. - 해당 연결 노드에서 다른
Customer노드로 이어지는 에지를 따릅니다. - 타임스탬프 (
last_updated_ts)를 기준으로 에지를 필터링하여 특정 시점의 네트워크 상태를 확인합니다.
Zachary Cordova 와 Brenda Brown 이 동일한 주소를 통해 Nicole에 연결되어 있음을 확인할 수 있습니다.
6. 네트워크 분석 (4홉)
이 단계에서는 더 복잡한 관계를 찾도록 쿼리를 확장합니다. 4홉 연결을 찾습니다. 이렇게 하면 여러 중간 항목을 통해 연결된 계정 (예: 고객 A -> 이메일 -> 고객 B -> 전화 -> 고객 C)을 찾을 수 있습니다.
또한 이 네트워크가 시간이 지남에 따라 어떻게 변화하는지 관찰합니다.
'이전' 상태
먼저 2025년 7월 30일 에 존재했던 네트워크를 살펴보겠습니다.
다음 쿼리를 실행합니다.
%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH p= ANY SHORTEST (a:Customer)
( -[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']- (n) ){3, 5}(reachable_a:Customer)
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
-- Ensure the final node in the dynamic chain is actually a Customer
AND 'Customer' IN UNNEST(LABELS(n[OFFSET(ARRAY_LENGTH(n) - 1)]))
RETURN
TO_JSON(p) AS paths, -- Array of all traversed edges
ARRAY_LENGTH(e) AS hop_count

'이후' 상태
이제 2주 후 네트워크가 어떻게 보이는지 살펴보겠습니다. 동일한 쿼리를 실행하지만 날짜 제한은 없습니다.
다음 쿼리를 실행합니다.
%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH p= ANY SHORTEST (a:Customer)
( -[e:OwnsEmail|OwnsPhone|LinkedToAddress]- (n) ){3, 5}(reachable_a:Customer)
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
-- Ensure the final node in the dynamic chain is actually a Customer
AND 'Customer' IN UNNEST(LABELS(n[OFFSET(ARRAY_LENGTH(n) - 1)]))
RETURN
TO_JSON(p) AS paths, -- Array of all traversed edges
ARRAY_LENGTH(e) AS hop_count

각 결과의 의미
날짜 필터를 삭제하면 전체 데이터 세트에 대해 쿼리합니다. 네트워크가 크게 확장되었음을 확인할 수 있습니다. 이제 Nicole Wade 는 훨씬 더 크고 연결성이 높은 그룹에 속해 있습니다. 연결된 네트워크의 이러한 빠른 확장은 시간이 지남에 따라 리소스를 공유하는 사기 조직과 같은 잠재적 허위 행위를 나타내는 강력한 지표입니다.
7. 사기 보고서 생성
이 단계에서는 그래프 분석을 기존 비즈니스 데이터 (주문)와 결합하여 포괄적인 사기 보고서를 생성합니다. 위험에 처한 계정과 잠재적 사기 주문을 식별합니다.
이 쿼리는 더 복잡합니다. GRAPH_TABLE을 사용하여 표준 SQL 내에서 그래프 쿼리를 실행하고 이전 단계에서 관찰한 '이전' 상태와 '이후' 상태 간의 네트워크 크기 변화 (diff)를 계산합니다.
사기 보고서 쿼리 실행
노트북에서 다음 쿼리를 실행합니다.
%%bigquery --graph
WITH num_orders AS (
SELECT account_id, COUNT(1) AS num_order
FROM fraud_demo.orders
WHERE order_time > '2025-07-30'
GROUP BY account_id
),
orders AS (
SELECT account_id, order_id, fraud, order_total
FROM fraud_demo.orders
WHERE order_time > '2025-07-30'
),
-- Use Quantified Path Patterns to find connections up to 4 hops away
latest_connect AS (
SELECT
account_id,
ARRAY_LENGTH(ARRAY_AGG(DISTINCT connected_id)) AS size
FROM GRAPH_TABLE(
fraud_demo.FraudDemo
MATCH (a:Customer)-[:OwnsEmail|OwnsPhone|LinkedToAddress]-{4}(connected:Customer)
RETURN a.account_id AS account_id, connected.account_id AS connected_id
)
GROUP BY account_id
),
prev_connect AS (
SELECT
account_id,
ARRAY_LENGTH(ARRAY_AGG(DISTINCT connected_id)) AS size
FROM GRAPH_TABLE(
fraud_demo.FraudDemo
-- Apply the timestamp filter to EVERY edge in the 4-hop chain
MATCH (a:Customer)
(-[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']-(n)){4}
WHERE 'Customer' IN UNNEST(LABELS(n[OFFSET(3)]))
RETURN a.account_id AS account_id, n[OFFSET(3)].account_id AS connected_id
)
GROUP BY account_id
),
edge_changes AS (
SELECT account_id, MAX(last_updated_ts) AS max_last_updated_ts
FROM fraud_demo.customer_addresses
GROUP BY account_id
)
SELECT
la.account_id,
o.order_id,
la.size AS latest_size,
COALESCE(pa.size, 0) AS previous_size,
la.size - COALESCE(pa.size, 0) AS diff,
nos.num_order,
o.fraud AS reported_as_fraud,
o.order_total,
CASE
WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NULL THEN "CUSTOMER AT RISK"
WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NOT NULL AND o.fraud THEN "CONFIRMED FRAUD ORDER"
WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NOT NULL AND NOT o.fraud THEN "POTENTIAL FRAUD ORDER"
ELSE ""
END AS notes
FROM latest_connect la
LEFT JOIN prev_connect pa ON la.account_id = pa.account_id
LEFT JOIN num_orders nos ON la.account_id = nos.account_id
LEFT JOIN orders o ON la.account_id = o.account_id
INNER JOIN edge_changes ec ON la.account_id = ec.account_id
WHERE nos.num_order > 1 OR (la.size - COALESCE(pa.size, 0)) > 10
ORDER BY diff DESC
각 결과의 의미
이 보고서에는 다음이 표시됩니다.
account_id: 분석 중인 계정의 ID입니다.order_id: 최근 주문 ID입니다.latest_size: 오늘 연결된 네트워크의 크기입니다.previous_size: 2주 전 네트워크의 크기입니다.diff: 네트워크 크기의 증가입니다.num_order: 최근 주문 수입니다.reported_as_fraud: 주문이 사기로 신고되었는지 여부입니다.order_total: 주문의 총액입니다.notes: 네트워크 증가 및 주문 내역을 기반으로 계산된 위험 상태입니다.
diff 값이 크고 주문 총액이 높은 계정이 표시되며, 이러한 계정은 추가 조사의 주요 대상입니다. '위험에 처한 고객' 및 '잠재적 사기 주문' 메모는 이러한 계정의 우선순위를 지정하는 데 도움이 됩니다.

8. 대규모 감지
이 최종 분석 단계에서는 네트워크를 더 큰 규모로 시각화합니다. 단일 계정으로 시작하는 대신 의심스러운 계정 집합 간의 연결을 쿼리합니다.
이렇게 하면 여러 독립적인 조사가 실제로 더 큰 사기 조직의 일부인지 확인할 수 있습니다.
확장된 쿼리 실행
노트북에서 다음 쿼리를 실행합니다.
%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH
p= ANY SHORTEST (a:Customer)
( -[e:OwnsEmail|OwnsPhone|LinkedToAddress]- (n) ){3, 5}
(reachable_a:Customer)
-- these IDs are from the previous results
WHERE a.account_id in ( "845f2b14-cd10-4750-9f28-fe542c4a731b"
, "3ff59684-fbf9-40d7-8c41-285ade5002e6"
, "8887c17b-e6fb-4b3b-8c62-cb721aafd028"
, "03e777e5-6fb4-445d-b48c-cf42b7620874"
, "81629832-eb1d-4a0e-86da-81a198604898"
, "845f2b14-cd10-4750-9f28-fe542c4a731b",
"89e9a8fe-ffc4-44eb-8693-a711a3534849"
)
LIMIT 400
RETURN TO_JSON(p) as paths
각 결과의 의미
이 쿼리는 지정된 의심스러운 계정이 어떻게 겹치고 리소스를 공유하는지 보여주는 복잡한 그래프를 반환합니다. 이제 대규모 사기 감지를 살펴보고, 조정된 대응이 필요할 수 있는 활동 클러스터를 식별합니다.

9. 삭제
이 Codelab에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 데이터 세트와 속성 그래프를 삭제해야 합니다.
다음 SQL 문을 실행하여 환경을 정리합니다.
DROP PROPERTY GRAPH IF EXISTS fraud_demo.FraudDemo;
DROP SCHEMA IF EXISTS fraud_demo CASCADE;
10. 축하합니다
축하합니다 BigQuery Graph를 사용하여 사기 감지 솔루션을 빌드했습니다.
지금까지 학습한 내용은 다음과 같습니다.
- Cloud Storage에서 BigQuery로 데이터를 로드합니다.
- DDL을 사용하여 속성 그래프를 정의합니다.
- GQL을 사용하여 그래프를 쿼리하여 간단하고 복잡한 관계를 찾습니다.
- 그래프 분석을 비즈니스 데이터와 결합하여 위험을 식별합니다.
- 대규모로 네트워크를 시각화합니다.