SDK ์—ฐ๋™

๐Ÿ“˜

Hackle Python SDK๋Š” Python 3 ์ด์ƒ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

WSGI ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๊ฒฝ์šฐ ๊ฐ€์ด๋“œ WSGI ์„ค์ • ์„ ๊ผญ ํ™•์ธํ•ด์ฃผ์„ธ์š”


1. ์˜์กด์„ฑ ์ถ”๊ฐ€

pip install hackle-sdk

2. SDK ์ดˆ๊ธฐํ™”

hackle_client๋Š” SDK ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ๋“ค์„ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

SDK๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” hackle_client ์ดˆ๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šคํ™”

SDK ํ‚ค๋ฅผ ์ „๋‹ฌํ•˜์—ฌ hackle_client๋ฅผ ์ธ์Šคํ„ด์Šคํ™” ํ•ฉ๋‹ˆ๋‹ค.
hackle_client๋Š” ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ ์–ป๊ธฐ ์œ„ํ•ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์œผ๋กœ ํ•ตํด ์„œ๋ฒ„์™€ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋™๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

  • SDK ํ‚ค๋Š” ํ•ตํด ์„œ๋น„์Šค์˜ ๋Œ€์‹œ๋ณด๋“œ ์•ˆ์— ์œ„์น˜ํ•œ SDK ์—ฐ๋™ ์ •๋ณด์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โ—๏ธ

hackle_client๋Š” ์ „์—ญ๋ณ€์ˆ˜์ด๋ฉฐ, ๋ฐ˜๋“œ์‹œ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

hackle_client๋Š” ์“ฐ๋ ˆ๋“œ์—์„œ I/O์—†์ด ๋ฐ”๋กœ ๊ฒฐ๊ณผ๋ฅผ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ถ”๊ฐ€์ ์ธ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์ด๋ฏธ ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

hackle_client ๋Š” ์‹ฑ๊ธ€ํ†ค(Singleton) ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค๋ฅธ ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉํ•  ๋•Œ hackle.Client()๋กœ ์‚ฌ์šฉํ•ด๋„ ์žฌ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

from hackle import hackle

// YOUR_SERVER_SDK_KEY ์ž๋ฆฌ์— SDK ํ‚ค๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.
hackle_client = hackle.Client(sdk_key=YOUR_SERVER_SDK_KEY)

3. ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ ์ „์†ก

์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ํ–‰๋™์„ ์ด๋ฒคํŠธ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์†ก๋œ ์ด๋ฒคํŠธ๋Š” A/B ํ…Œ์ŠคํŠธ ๋ถ„์„, ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋“ฑ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งค๋ผ๋Š” ํ–‰๋™์„ ํ–ˆ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

from hackle.model import HackleUser, HackleEvent


# "ae2182e0"๋ผ๋Š” ๋””๋ฐ”์ด์Šค ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐœ์ƒ์‹œํ‚จ "purchase"๋ผ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ „์†ก

user = HackleUser.builder() \
                 .device_id('ae2182e0') \
                 .build()

event = HackleEvent.builder('purchase') \
                   .property('pay_method', 'CARD') \
                   .property('discount_amount', 800) \
                   .property('is_discounted', True) \
                   .build()

hackle_client.track(event=event, user=user)

์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ ์ „์†ก ํ™•์ธํ•˜๊ธฐ

SDK ์—์„œ ์ „์†กํ•œ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ˆ˜์ง‘๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์„ธ์š”.
ํ•ตํด ๋Œ€์‹œ๋ณด๋“œ ์•ˆ์˜ [์™ผ์ชฝ ๋ฉ”๋‰ด๋ฐ”] - [์ด๋ฒคํŠธ ๊ด€๋ฆฌ] ๋ฉ”๋‰ด์—์„œ SDK๋กœ ์ „์†กํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ฐพ์•„ ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ ์ˆ˜์ง‘ํ˜„ํ™ฉ์„ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


4. A/B ํ…Œ์ŠคํŠธ, ๊ธฐ๋Šฅํ”Œ๋ž˜๊ทธ

A/B ํ…Œ์ŠคํŠธ

A/B ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ, ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์„ ๋Œ€์ƒ์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋ถ„๋ฐฐํ•˜๊ณ  ๊ฐ ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์— ํ•ด๋‹นํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ์‚ฌ์šฉ์ž ๋ถ„๋ฐฐ๋ฅผ ํ•ตํด SDK๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“˜

ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน

ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์€ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ด ๋˜๋Š” ๊ธฐ์กด์•ˆ(๋Œ€์กฐ๊ตฐ)๊ณผ ๊ฐœ์„ ์•ˆ(์‹คํ—˜๊ตฐ)์„ ์˜๋ฏธํ•˜๋ฉฐ, ๊ฐœ์„ ์•ˆ์€ 1๊ฐœ ์ด์ƒ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์„ค์ • ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” A/B ํ…Œ์ŠคํŠธ ์„ค์ • ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

variation

variation() ๋ฉ”์†Œ๋“œ์— ์‹คํ—˜ ํ‚ค์™€ ์‚ฌ์šฉ์ž ์‹๋ณ„์ž๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์‚ฌ์šฉ์ž๋ฅผ ๋ถ„๋ฐฐํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน ๋ณ„ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
์‹คํ—˜ ํ‚ค๋Š” ๊ฐ A/B ํ…Œ์ŠคํŠธ๋ณ„๋กœ ๊ฐ–๊ฒŒ ๋˜๋Š” ๊ณ ์œ  ๋ฒˆํ˜ธ์ด๋ฉฐ, ํ•ตํด ์„œ๋น„์Šค ๋‚ด์˜ ๋Œ€์‹œ๋ณด๋“œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ๋Š” ์‹คํ—˜ ํ‚ค 42๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์€ A์™€ B ๋‘ ๊ฐœ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

from hackle.model import HackleUser

# ์‹คํ—˜ ํ‚ค๊ฐ€ 42์ธ A/B ํ…Œ์ŠคํŠธ์—์„œ
# "ae2182e0"๋ผ๋Š” ๋””๋ฐ”์ด์Šค ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœํ•  ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
# ๊ฒฐ์ •ํ•˜์ง€ ๋ชปํ•˜๋Š” ์ƒํ™ฉ์ธ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน A๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

user = HackleUser.builder() \
                 .device_id('ae2182e0') \
                 .build()

variation = hackle_client.variation(experiment_key=42, user=user)

# ํ• ๋‹น๋ฐ›์€ ๊ทธ๋ฃน์— ๋Œ€ํ•œ ๋กœ์ง
if variation == 'A':
    # ๊ทธ๋ฃน A ๋กœ์ง
elif variation == 'B':
    # ๊ทธ๋ฃน B ๋กœ์ง

๊ธฐ๋Šฅํ”Œ๋ž˜๊ทธ

๐Ÿ“˜

๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋Š” SDK ๋ฒ„์ „ 2.0.0 ์ด์ƒ์ธ ๊ฒฝ์šฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๊ฒฝ์šฐ ์˜์กด์„ฑ ์ถ”๊ฐ€ ์‹œ SDK ๋ฒ„์ „์„ 2.0.0 ์ด์ƒ์œผ๋กœ ์ ์šฉํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋Š” ์ผœ์ง(on) ์ƒํƒœ์™€ ๊บผ์ง(off) ์ƒํƒœ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ์„ค์ •ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ ์šฉํ•œ ๊ธฐ๋Šฅ์— ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ๊ฒฝ์šฐ ์ผœ์ง ํ˜น์€ ๊บผ์ง ์ƒํƒœ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒํƒœ ๊ฒฐ์ •์„ ํ•ตํด SDK๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

isFeatureOn

is_feature_on() ๋ฉ”์†Œ๋“œ์— ๊ธฐ๋Šฅ ํ‚ค๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ƒํƒœ ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ๋Šฅ ํ‚ค๋Š” ๊ฐ ๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋ณ„๋กœ ๊ฐ–๊ฒŒ ๋˜๋Š” ๊ณ ์œ  ๋ฒˆํ˜ธ์ด๋ฉฐ ํ•ตํด ์„œ๋น„์Šค ๋‚ด์˜ ๋Œ€์‹œ๋ณด๋“œ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ๋Š” ๊ธฐ๋Šฅ ํ‚ค 42๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ƒํƒœ๋ฅผ ๋ฐ›์„ ์‚ฌ์šฉ์ž์˜ ์‚ฌ์šฉ์ž ์‹๋ณ„์ž๋Š” "ae03e1adf" ์ž…๋‹ˆ๋‹ค.

from hackle.model import HackleUser

# ๊ธฐ๋Šฅ ํ‚ค๊ฐ€ 42์ธ ๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ์—์„œ
# "ae2182e0"๋ผ๋Š” ๋””๋ฐ”์ด์Šค ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœํ•  ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
# ๊ฒฐ์ •ํ•˜์ง€ ๋ชปํ•˜๋Š” ์ƒํ™ฉ์ธ ๊ฒฝ์šฐ false(๊บผ์ง ์ƒํƒœ)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

user = HackleUser.builder() \
                 .device_id('ae2182e0') \
                 .build()

feature_on = hackle_client.is_feature_on(feature_key=42, user=user)

# ํ• ๋‹น๋ฐ›์€ ๊ทธ๋ฃน์— ๋Œ€ํ•œ ๋กœ์ง
if feature_on == True:
    # ON ๊ธฐ๋Šฅ
elif feature_on == False:
    # OFF ๊ธฐ๋Šฅ

๋ถ„๋ฐฐ๊ฒฐ๊ณผ ํ™•์ธํ•˜๊ธฐ

ํ•ตํด ๋Œ€์‹œ๋ณด๋“œ ์•ˆ์˜ [์™ผ์ชฝ ๋ฉ”๋‰ด๋ฐ”] - ๋Š” ๊ธฐ๋Šฅํ”Œ๋ž˜๊ทธ] ํ™”๋ฉด์—์„œ ๋…ธ์ถœ๋˜๋Š” ํ™”๋ฉด์—์„œ ๋…ธ์ถœ๋˜๋Š” A/Bํ…Œ์ŠคํŠธ ๋˜๋Š” ๊ธฐ๋Šฅํ”Œ๋ž˜๊ทธ ๋ฆฌ์ŠคํŠธ ์ค‘ ์—ฐ๋™ํ•œ ๋Œ€์ƒ์„ ์ฐพ์•„ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ ํ•œ ํ›„ ํ™”๋ฉด ์ค‘๊ฐ„์— [์‹ค์‹œ๊ฐ„ ๋…ธ์ถœ ํ˜„ํ™ฉ] ํƒญ์„ ํด๋ฆญํ•˜๋ฉด SDK๋กœ ์—ฐ๋™ ๋œ ๋ถ„๋ฐฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


5. ์ข…๋ฃŒ

hackle_client.close()

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ hackle_client.close() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด hackle_client๋ฅผ ์ข…๋ฃŒ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ˜๋‚ฉํ•˜๊ณ  ๋‚จ์•„์žˆ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—†์ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Flask๋‚˜ Django๋ฅผ ์‚ฌ์šฉ ์ค‘ ์ด๋ผ๋ฉด?

@atexit.register๋ฅผ ํ†ตํ•ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ ์‹œ ์ž๋™์œผ๋กœ hackle_client๋ฅผ ์ข…๋ฃŒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import atexit

@atexit.register
def __exit__():
    hackle_client.close()