dbt๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์ด๋ฏธ ์ž˜ํ•˜๊ณ  ์žˆ๋Š” ๊ฒ๋‹ˆ๋‹ค. ๐Ÿ™Œ

๊ทธ๋Ÿฐ๋ฐ dbt์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋” ๊นจ๋—ํ•˜๊ณ  ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ํ…Œ์ŠคํŠธ ๊ธฐ๋Šฅ๋„ ์žˆ๋‹ค๋Š” ๊ฑฐ, ์•Œ๊ณ  ๊ณ„์…จ๋‚˜์š”?

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋‹ค์Œ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค:

  • โœ… ๊ธฐ๋ณธ dbt ํ…Œ์ŠคํŠธ โ€” ๋น ๋ฅด๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋ณธ๊ธฐ
  • ๐Ÿš€ ์ค‘๊ธ‰ ํ…Œ์ŠคํŠธ โ€” ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ์ง๊ณผ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋งคํฌ๋กœ

โœ… ๊ธฐ๋ณธ dbt ํ…Œ์ŠคํŠธ (Built-in)

dbt๋Š” ๋ชจ๋ธ์˜ .yml ํŒŒ์ผ ์•ˆ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ ํ…Œ์ŠคํŠธ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์‹œ:

version: 2

models:
  - name: customers
    description: ๊ณ ๊ฐ ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”
    columns:
      - name: customer_id
        tests:
          - not_null
          - unique
      - name: email
        tests:
          - not_null

๐Ÿ”ง ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•˜๋Š” ์ผ:

  • not_null: ์ปฌ๋Ÿผ์— NULL ๊ฐ’์ด ์—†๋Š”์ง€ ํ™•์ธ
  • unique: ๊ฐ’์ด ๊ณ ์œ ํ•œ์ง€ ๊ฒ€์ฆ
  • accepted_values: ํ—ˆ์šฉ๋œ ๊ฐ’๋งŒ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ์ฒดํฌ
  • relationships: ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์ฐธ์กฐ ๋Œ€์ƒ ํ…Œ์ด๋ธ”๊ณผ ๋งค์นญ๋˜๋Š”์ง€ ํ™•์ธ

accepted_values ์˜ˆ์‹œ

      - name: status
        tests:
          - accepted_values:
              values: ['active', 'inactive', 'suspended']

์ด๋Ÿฐ ๊ธฐ๋ณธ ํ…Œ์ŠคํŠธ๋“ค์€ ์ƒ์‚ฐ ๋Œ€์‹œ๋ณด๋“œ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐ˜์˜๋˜๊ธฐ ์ „์— ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ ์ด์ƒ์„ ๋น ๋ฅด๊ฒŒ ์žก์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿš€ ์ค‘๊ธ‰ dbt ํ…Œ์ŠคํŠธ (์‚ฌ์šฉ์ž ์ •์˜ + ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ)

๊ธฐ๋ณธ ํ…Œ์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

  • ํŠน์ • ๊ธฐ๊ฐ„ ๋‚ด ์ค‘๋ณต ์‚ฌ์šฉ์ž ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
  • ์ฃผ๋ฌธ ์ด์•ก์ด ๊ฐ ํ•ญ๋ชฉ์˜ ํ•ฉ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆ
  • ์‚ฌ์šฉ์ž๋‹น ํ•˜๋‚˜์˜ ํ™œ์„ฑ ๊ตฌ๋…๋งŒ ํ—ˆ์šฉ

์ด๋Ÿด ๋•Œ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ SQL ํ…Œ์ŠคํŠธ์™€ ๋งคํฌ๋กœ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿงช ์‚ฌ์šฉ์ž ์ •์˜ SQL ํ…Œ์ŠคํŠธ ์˜ˆ์‹œ

tests/ ํด๋”์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค:

-- tests/one_active_subscription.sql

SELECT user_id
FROM {{ ref('subscriptions') }}
GROUP BY user_id
HAVING SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) > 1

๊ทธ๋ฆฌ๊ณ  .yml์—์„œ ํ˜ธ์ถœ:

      - name: user_id
        tests:
          - one_active_subscription

ํ™œ์„ฑ ๊ตฌ๋…์ด 2๊ฐœ ์ด์ƒ์ธ ์œ ์ €๊ฐ€ ์žˆ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿงฐ ๋ณด๋„ˆ์Šค: ํŒŒ๋ผ๋ฏธํ„ฐํ™”๋œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋งคํฌ๋กœ ํ…Œ์ŠคํŠธ

์—ฌ๋Ÿฌ ๋ชจ๋ธ์—์„œ ์ˆซ์ž ์ปฌ๋Ÿผ์ด ํ•ญ์ƒ ์–‘์ˆ˜์ธ์ง€ ๊ฒ€์ฆํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?

Jinja ๋งคํฌ๋กœ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑ:

-- macros/test_positive_values.sql

{% test positive_values(model, column_name) %}
SELECT *
FROM {{ model }}
WHERE {{ column_name }} < 0
{% endtest %}

๊ทธ๋ฆฌ๊ณ  .yml์—์„œ ์‚ฌ์šฉ:

      - name: price
        tests:
          - positive_values

์ด์ œ ํ•œ ๋ฒˆ ์ •์˜ํ•œ ํ…Œ์ŠคํŠธ ๋กœ์ง์„ ์—ฌ๋Ÿฌ ๋ชจ๋ธ/์ปฌ๋Ÿผ์— ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

dbt ์ปค์Šคํ…€ ํ…Œ์ŠคํŠธ์™€ ๋งคํฌ๋กœ ์ฐจ์ด

dbt ์‚ฌ์šฉ์ž ์ •์˜ SQL ํ…Œ์ŠคํŠธ๋Š” ํŠน์ • ๋ชจ๋ธ์— ํŠนํ™”๋œ ์กฐ๊ฑด์„ ํ™•์ธํ•˜๋Š” ๋‹จ์ผ .sql ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋งคํฌ๋กœ ํ…Œ์ŠคํŠธ๋Š” Jinja ๋งคํฌ๋กœ๋กœ ์ž‘์„ฑ๋˜๋ฉฐ, ์—ฌ๋Ÿฌ ๋ชจ๋ธ/์ปฌ๋Ÿผ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด ๋” ์œ ์—ฐํ•˜๊ณ  **DRY(Donโ€™t Repeat Yourself)**ํ•ฉ๋‹ˆ๋‹ค. ํŠน์ •ํ•œ ์ผ€์ด์Šค์—๋Š” SQL ํ…Œ์ŠคํŠธ, ์—ฌ๋Ÿฌ ๊ณณ์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•  ๋• ๋งคํฌ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์“ฐ๋Š” ๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

๐Ÿงต ๋งˆ๋ฌด๋ฆฌ ์ •๋ฆฌ

์ˆ˜์ค€ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ ์˜ˆ์‹œ
๊ธฐ๋ณธ ๋‚ด์žฅ YAML ํ…Œ์ŠคํŠธ not_null, unique, accepted_values
์ค‘๊ธ‰ ์‚ฌ์šฉ์ž ์ •์˜ SQL/๋งคํฌ๋กœ positive_values, one_active_subscription

์ฒ˜์Œ์—” ์ž‘๊ฒŒ ์‹œ์ž‘ํ•˜์„ธ์š”. not_null๊ณผ unique๋งŒ ์ ์šฉํ•ด๋„ ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ ์  ๋ณต์žกํ•œ ํ…Œ์ŠคํŠธ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด๋ณด์„ธ์š”.

๋ฏธ๋ž˜์˜ ๋‹น์‹ ๊ณผ ํŒ€์›๋“ค์ด ์ง„์งœ๋กœ ๊ฐ์‚ฌํ•  ์ผ์ž…๋‹ˆ๋‹ค.

โ€œ๋ง๊ฐ€์ง„ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์€ ๊ฑฐ์ง“๋งํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹จ์ง€ ๋‹น์‹ ์˜ ์ธ๋‚ด์‹ฌ์„ ์‹œํ—˜ํ•  ๋ฟ์ด๋‹ค.โ€

dbt๋กœ ์ฆ๊ฑฐ์šด ๋ฐ์ดํ„ฐ ํ…Œ์ŠคํŠธ ๋˜์„ธ์š”! ๐Ÿงช