일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 부트캠프
- human interface guidelines
- 책후기
- NumberFormatter
- delegation
- SWIFTUI
- NotificationCenter
- roundingMode
- @available
- 스위프트
- Codegen
- Info.plist
- xcode
- contentInset
- 스타트업주니어로살아남기
- Mock
- mvvm
- 독후감
- Navigation
- View Life Cycle
- 야곰아카데미
- 아이폰
- viewcontroller
- SWIFT
- Structures and Classes
- Failed to register bundle identifier
- 독서후기
- Modality
- UIResponder
- IOS
- Today
- Total
호댕의 iOS 개발
[iOS] AB 테스트를 위한 툴, Hackle 본문
단순히 규모가 작은 개인 앱을 만들 때에는 크게 AB테스트가 필요하지 않았다. 일단 테스트의 모수가 되는 사용자의 수도 많지 않았고, 다양한 아이디어를 수행해볼 만큼 앱이 고도화가 되지 않았기 때문이다.
하지만 회사에서 직접 업무를 하며, 성과를 비교하고 앱의 기능을 어떤 식으로 추가할 지 판단하기 위해 AB 테스트를 사용하는 경우가 생겼다.
물론 단순히 User ID를 홀짝으로 나눠서 AB테스트를 수행할 수도 있다.
그러나 이 경우 특정 UserID를 가진 사용자만 특정 실험에 노출되거나 할 수 있어 제대로 된 판단이 불가능할 수도 있다. 그래서 이렇게 분류하는 것이 그렇게 좋지 못한 AB 테스트 분류라는 것을 알게 됐다.
(이 경우 실험에 경향성이 생긴다고 한다)
그래서 이번에 회사에선 Hackle이라는 AB 테스트 및 데이터 분석 툴을 도입하게 됐다.
이외에도 다양한 AB 테스트를 위한 툴이 있었지만 회사의 웹/앱 개발환경과 가장 적합하다고 판단하여 핵클을 선택했다.
해당 서비스의 PoC를 내가 진행하게 되면서 Hackle 서비스에 대해 정리를 해보고자 한다.
일단 핵클의 경우 아래의 언어들을 지원하고 있다.
나는 현재 iOS 개발을 하고 있으니 iOS를 기준으로 정리를 해보자!
일단 위에서 UserID의 홀짝으로 AB Test를 하는 것은 좋은 테스트 분배가 아니라고 했었다.
그래서 Hackle SDK의 경우 MurmurHash 함수를 통해 테스트 그룹을 자동으로 분배를 해준다. 이 경우 시드가 변경되지 않는 한 같은 사용자를 항상 동일한 그룹으로 분배해준다고 한다. 따라서 실험을 하다가 A그룹이었던 사람이 B그룹으로 되진 않는다는 것이다.
(MurmurHash 함수에 대한 이해는 못했다...)
https://docs-kr.hackle.io/docs/ab-bucketing
iOS에서 Hackle SDK 시작하기
물론 개발자 문서에 정리가 되어 있긴 하지만 내가 적용하고 이해한 방식으로 다시 정리해보고자 한다.
일단 여느 라이브러리처럼 의존성을 추가해줘야 한다.
이때 의존성 관리도구는 CocoaPods와 Swift Package Manager를 사용할 수 있다.
이렇게 의존성 관리도구를 사용해 의존성을 추가해줬다면, SDK를 초기화해주는 작업이 필요하다. 초기화의 경우 앱을 실행하면서 반드시 한 번만 수행되어야 하며 이때 SDK 연동에 필요한 정보들을 핵클 서버에서 가져와 SDK에 저장하게 된다.
이 작업은 비동기로 실행되며 completion handler를 통해 완료된 후 해야하는 작업을 정해줄 수도 있다.
처음에는 AppDelegate에서 초기화를 직접해주는 방법을 사용할까 했으나, 이렇게 하는 경우 AppDelegate에 타입 프로퍼티로 만들어놓고 사용할 때마다 AppDelegate를 호출해야 했다.
final class HackleManager {
private var hackleAppKey: String {
get {
guard let filePath = Bundle.main.path(forResource: "APIKey", ofType: "plist") else {
fatalError("Couldn't find file 'APIKey.plist'")
}
let plist = NSDictionary(contentsOfFile: filePath)
#if DEBUG
guard let value = plist?.object(forKey: "jssHackleAppDevKey") as? String else {
fatalError("Couldn't find key 'hackleDevKey' in 'APIKey.plist'")
}
return value
#else
guard let value = plist?.object(forKey: "jssHackleAppKey") as? String else {
fatalError("Couldn't find key 'hackleKey' in 'APIKey.plist'")
}
return value
#endif
}
}
static let shared = HackleManager()
private(set) var hackleApp: HackleApp? = nil
private init() { }
func startHackle() {
Hackle.initialize(sdkKey: hackleAppKey)
hackleApp = Hackle.app()
}
}
그래서 싱글턴 패턴을 사용해 객체의 인스턴스를 사용할 때 딱 한 번만 만들 수 있도록 하도록 했다. 싱글턴 패턴을 너무 남용하는 것은 좋지 않다고 생각하지만 Hackle.initialize를 앱을 실행하면서 딱 한 번만 호출해야 했고, AB테스트를 하는 곳에서 hackleApp에 쉽게 접근할 수 있어야 테스트가 용이하다고 판단하여 싱글턴 페턴을 사용했다.
AppKey의 경우
Hackle 서비스에서 개발환경 > SDK 연동정보에 보면 확인할 수 있다.
이렇게 initializer를 했다면 HackleApp의 인스턴스를 생성해야 한다. 이렇게 생성한 인스턴스를 통해 AB 테스트를 진행할 수 있게 된다.
이는 간단하게 Hackle의 app 함수를 호출해 인스턴스를 생성할 수 있다.
권장 초기화 전략은 로딩화면을 표시하고 SDK를 초기화한 뒤 앱을 실행하라고 하고 있다. 또한 이 경우 로딩화면에 제한시간을 두어 SDK를 초기화할 수 있는 시간을 부여하라고 제안하고 있다.
나는 AppDelegate의 아래 함수에서 실행해주더라도 SDK를 초기화하는 것은 충분하다고 판단하여, 여기서 위에서 만든 startHackle 함수를 호출하도록 해줬다.
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
만약 분배 결과의 중복 노출이벤트를 제거하고 싶은 경우 2.7.0 버전 이상에서
let config = HackleConfigBuilder()
.exposureEventDedupIntervalSeconds(60)
.build()
Hackle.initialize(sdkKey: "YOUR_APP_SDK_KEY", config: config) {
// SDK ready to use.
}
위처럼 HackleConfigBuilder를 통해 중복 노출을 제거할 시간(초)을 작성하고 초기화하라고 하고 있다.
다만 CocoaPods로 설치를 할 경우 내가 잘못한 것인지 2.7.0 버전을 찾지 못해 2.4.2 버전으로 설치를 했다.
테스트 분배하기
위에서 말했던 것처럼 테스트의 분배 로직 자체는 SDK가 알아서 해주게 된다. 사용자 식별자를 따로 보내지 않아도 자동으로 hackleApp의 deviceId에 접근해서 시용자를 파악할 수 있다.
나는 hackleApp의 variationDetail을 사용해 실험키와 userId를 사용자 식별자로 보낼 수 있도록 했다.
let decision = HackleHandler.shared.hackleApp?.variationDetail(
experimentKey: hackleExperimentKey,
userId: {사용자의 ID를 String값으로 넣어주면 됨}
)
let variation = decision?.variation
여기서 experimentKey가 있는데 이는 각각의 AB 테스트에 보면 고유한 실험키를 가지고 있다. 이 값을 experimentKey 파라미터에 넣어주면 된다.
(이 실험키는 자동으로 부여되며 따로 수정할 순 없다)
실험키의 경우 핵클 대쉬보드 AB 테스트 탭에서 확인할 수 있다.
이렇게 해서 decision의 variation 프로퍼티를 통해 어떤 그룹인지를 내부 로직을 통해 판단 후 이를 String 값으로 보내주게 된다.
나는 이렇게 나온 값을 switch를 통해 나눠서 A그룹, B그룹에 해당하는 기능을 구현할 수 있도록 했다.
switch variation {
case "A":
{A그룹에서 해야할 기능 구현}
case "B":
{B그룹에서 해야할 기능 구현}
default:
{String으로 되어 있어 default를 구현해줬다}
}
이렇게 간단하게 테스트 그룹 분배가 가능하고 쉽게 A, B를 나눠 구현해줄 수 있었다.
이벤트 보내주기
어떤 이벤트가 발생했는지 보내주는 것도 간단하게 가능하다.
일단 대쉬보드의 이벤트 관리 탭에 들어가 확인하고 싶은 이벤트를 생성해주면 된다.
그리고 hackleApp의 track 메서드를 사용해 이벤트가 발생했는지 보내주면 된다.
HackleHandler.shared.hackleApp?.track(
eventKey: "생성한 이벤트 이름",
userId: {userID 값}
)
userId는 식별자로 필요 없는 경우 넣지 않아도 된다.
사용할 수 있는 track 메서드는 이렇게 있었다.
이렇게 보내준 데이터들은 대쉬보드 이벤트 관리 탭에서 확인해볼 수 있다.
일단 회사에 필요한 툴을 처음 도입하면서 PoC를 진행해본 경험이 처음이었는데 새로운 툴에 대해 알아가고 기여했다는 점이 재미있었던 것 같다.
그리고 개발자가 아니더라도 데이터를 시각화해서 바로바로 확인할 수 있다는 점이 매력적이었고, AB테스트를 위해 적용하는 것도 간단해서 좋았다.
참고문서
- 핵클 개발자 가이드 https://docs-kr.hackle.io/docs/getting-started-developers-guide
'Software Engineering > iOS' 카테고리의 다른 글
[배포] Appstore에 프로젝트를 배포해보자. (0) | 2022.12.02 |
---|---|
[RxSwift] 버튼을 누르는 이벤트를 일정 시간 동안 한 번만 보내고 싶다면 (Throttle, Debounce) (0) | 2022.11.07 |
[iOS] Universal Links에 대해 알아보자 (0) | 2022.10.27 |
[iOS] 인스타그램 스토리 공유하기 (+ UIGraphicsImageRenderer) (0) | 2022.10.25 |
[iOS] 푸쉬 알림(Push Notification) 어떻게 할까? (OneSignal, FCM, Foreground에서 알림 받지 않기 등등) (0) | 2022.09.29 |