BigQuery 그래프를 사용한 사기 감지

1. 소개

마케팅 헤더 - 핀테크의 사기

사기 행위에는 연결된 법인의 숨겨진 네트워크가 포함되는 경우가 많습니다. 예를 들어 동일한 이메일 주소, 전화번호 또는 실제 주소를 공유하는 여러 계정이 있습니다. 기존 관계형 데이터베이스는 이러한 복잡한 멀티 홉 관계를 효율적으로 쿼리하는 데 어려움을 겪을 수 있습니다.

BigQuery 그래프를 사용하면 그래프 데이터베이스를 사용하여 이러한 네트워크를 대규모로 분석할 수 있습니다. 기존 BigQuery 테이블 위에 속성 그래프를 정의하고 그래프 쿼리 언어 (GQL)를 사용하여 데이터에서 패턴을 찾을 수 있습니다.

사기 감지를 위한 그래프 네트워크의 일반적인 적용 사례는 사기 네트워크와 연결된 배송 주소가 있는 주문을 중지하거나

이 Codelab에서는 BigQuery 그래프를 사용하여 사기 감지 솔루션을 빌드합니다. Cloud Storage에서 데이터를 로드하고, 속성 그래프를 만들고, 그래프 쿼리를 사용하여 의심스러운 연결을 식별합니다.

학습할 내용

  • BigQuery 데이터 세트를 만들고 데이터를 로드하는 방법
  • DDL을 사용하여 속성 그래프를 정의하는 방법
  • GQL을 사용하여 그래프를 쿼리하는 방법
  • 그래프 분석을 사용하여 사기를 감지하는 방법

필요한 항목

  • 결제가 사용 설정된 Google Cloud 프로젝트.
  • BigQuery 노트북 환경 (BigQuery Studio 또는 Colab Enterprise)

비용

이 실습에서는 비용이 청구될 수 있는 Google Cloud 리소스를 사용합니다. 완료 후 리소스를 삭제하면 예상 비용은 5달러 미만입니다.

2. 시작하기 전에

Google Cloud 프로젝트 선택 또는 만들기

  1. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
  2. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.

환경 선택

이 실습을 실행하려면 노트북 환경이 필요합니다. BigQuery Studio 또는 Colab Enterprise를 사용할 수 있습니다.

  1. Google Cloud 콘솔에서 BigQuery 페이지로 이동합니다.
  2. Python 노트북을 사용하여 그래프 쿼리를 실행합니다.

Cloud Shell 시작

  1. Google Cloud 콘솔 상단에서 Cloud Shell 활성화를 클릭합니다.
  2. 인증을 확인합니다.
gcloud auth list
  1. 프로젝트를 확인합니다.
gcloud config get project
  1. 필요한 경우 설정합니다.
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

API 사용 설정

다음 명령어를 실행하여 필요한 BigQuery API를 사용 설정합니다.

gcloud services enable bigquery.googleapis.com

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이라는 데이터 세트를 만듭니다.

  1. 이 Codelab에서는 SQL 명령어를 실행합니다. BigQuery Studio > SQL 편집기에서 이러한 명령어를 실행하거나 Cloud Shell에서 bq query 명령어를 사용할 수 있습니다. 새 SQL 쿼리여러 줄로 된 생성 문을 더 효과적으로 사용하려면 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 노트북을 사용합니다. 이렇게 하면 그래프 결과를 쉽게 시각화할 수 있습니다.

BigQuery 그래프 노트북은 IPython 매직으로 구현됩니다. TO_JSON 함수와 함께 %%bigquery 매직 명령어를 추가하면 다음 섹션에 표시된 대로 결과를 시각화할 수 있습니다. 이 단계에서는 그래프 쿼리를 실행하여 계정 간의 간단한 연결을 찾습니다. 시작 노드에서 2홉을 이동하여 관련 노드를 찾으므로 '2홉' 쿼리입니다 (예: 고객 -> 이메일 -> 고객).

니콜 웨이드의 계정부터 조사하겠습니다. 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

결과 이해하기

이 쿼리는 다음과 같은 작업을 합니다.

  1. account_id 'd2f1f992-d116-41b3-955b-6c76a3352657' (니콜 웨이드)로 Customer 노드에서 시작합니다.
  2. OwnsEmail, OwnsPhone 또는 LinkedToAddress 가장자리를 따라 연결 노드 (Phone, Email 또는 Address)로 이동합니다.
  3. 연결 노드에서 다른 Customer 노드로 에지를 따라 돌아갑니다.
  4. 타임스탬프 (last_updated_ts)를 기반으로 가장자리를 필터링하여 특정 시점의 네트워크 상태를 확인합니다.

Zachary CordovaBrenda Brown이 동일한 주소를 통해 Nicole에 연결되어 있는 것을 확인할 수 있습니다.

6. 네트워크 분석 (4홉)

이 단계에서는 더 복잡한 관계를 찾기 위해 쿼리를 확장합니다. 4홉 연결을 찾습니다. 이를 통해 여러 중간 법인 (예: 고객 A -> 이메일 -> 고객 B -> 전화 -> 고객 C)을 통해 연결된 계정을 찾을 수 있습니다.

시간이 지남에 따라 이 네트워크가 어떻게 변화하는지도 관찰합니다.

'이전' 상태

먼저 2025년 7월 30일에 존재했던 네트워크를 살펴보겠습니다.

다음 쿼리를 실행합니다.

%%bigquery --graph
%%bigquery --graph

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)]))
GRAPH fraud_demo.FraudDemo
RETURN 
  TO_JSON(p) AS paths,                   -- Array of all traversed edges
  ARRAY_LENGTH(e) AS hop_count

'After' 상태

이제 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

결과 이해하기

날짜 필터를 삭제하면 전체 데이터 세트에 대해 쿼리하게 됩니다. 네트워크가 크게 성장한 것을 확인할 수 있습니다. 니콜 웨이드는 이제 훨씬 더 큰, 긴밀하게 연결된 그룹에 속해 있습니다. 연결된 네트워크의 이러한 급격한 확장은 시간이 지남에 따라 리소스를 공유하는 사기 조직과 같은 사기 행위가 발생할 수 있음을 나타내는 강력한 지표입니다.

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 그래프를 사용하여 사기 감지 솔루션을 빌드했습니다.

지금까지 학습한 내용은 다음과 같습니다.

  • Cloud Storage에서 BigQuery로 데이터를 로드합니다.
  • DDL을 사용하여 속성 그래프를 정의합니다.
  • GQL을 사용하여 그래프를 쿼리하여 간단한 관계와 복잡한 관계를 찾습니다.
  • 그래프 분석과 비즈니스 데이터를 결합하여 위험을 식별합니다.
  • 규모에 따라 네트워크를 시각화합니다.

기타 자료